Imported Upstream version cpprest 2.10.18 upstream upstream/2.10.18
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 14 Apr 2021 03:52:19 +0000 (12:52 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 14 Apr 2021 03:52:19 +0000 (12:52 +0900)
457 files changed:
.clang-format [new file with mode: 0644]
.gitmodules [new file with mode: 0644]
.vscode/launch.json [new file with mode: 0644]
.vscode/settings.json [new file with mode: 0644]
Build_android/configure.sh [new file with mode: 0755]
Build_android/openssl/Makefile [new file with mode: 0644]
Build_android/openssl/openssl-1.0.2k.patch [new file with mode: 0644]
Build_android/openssl/openssl-1.0.2l.patch [new file with mode: 0644]
Build_android/openssl/openssl-1.0.2m.patch [new file with mode: 0644]
Build_android/openssl/openssl-1.0.2n.patch [new file with mode: 0644]
Build_android/openssl/openssl-1.1.0g.patch [new file with mode: 0644]
Build_android/openssl/openssl-1.1.0j.patch [new file with mode: 0644]
Build_iOS/.gitignore [new file with mode: 0644]
Build_iOS/CMakeLists.txt [new file with mode: 0644]
Build_iOS/README.md [new file with mode: 0644]
Build_iOS/configure.sh [new file with mode: 0755]
CMakeLists.txt [new file with mode: 0644]
CONTRIBUTORS.txt [new file with mode: 0644]
README.md [new file with mode: 0644]
Release/.gitignore [new file with mode: 0644]
Release/CMakeLists.txt [new file with mode: 0644]
Release/cmake/cpprest_find_boost.cmake [new file with mode: 0644]
Release/cmake/cpprest_find_brotli.cmake [new file with mode: 0644]
Release/cmake/cpprest_find_openssl.cmake [new file with mode: 0644]
Release/cmake/cpprest_find_websocketpp.cmake [new file with mode: 0644]
Release/cmake/cpprest_find_winhttppal.cmake [new file with mode: 0644]
Release/cmake/cpprest_find_zlib.cmake [new file with mode: 0644]
Release/cmake/cpprestsdk-config-version.in.cmake [new file with mode: 0644]
Release/cmake/cpprestsdk-config.in.cmake [new file with mode: 0644]
Release/include/cpprest/astreambuf.h [new file with mode: 0644]
Release/include/cpprest/asyncrt_utils.h [new file with mode: 0644]
Release/include/cpprest/base_uri.h [new file with mode: 0644]
Release/include/cpprest/containerstream.h [new file with mode: 0644]
Release/include/cpprest/details/SafeInt3.hpp [new file with mode: 0644]
Release/include/cpprest/details/basic_types.h [new file with mode: 0644]
Release/include/cpprest/details/cpprest_compat.h [new file with mode: 0644]
Release/include/cpprest/details/fileio.h [new file with mode: 0644]
Release/include/cpprest/details/http_constants.dat [new file with mode: 0644]
Release/include/cpprest/details/http_helpers.h [new file with mode: 0644]
Release/include/cpprest/details/http_server.h [new file with mode: 0644]
Release/include/cpprest/details/http_server_api.h [new file with mode: 0644]
Release/include/cpprest/details/nosal.h [new file with mode: 0644]
Release/include/cpprest/details/resource.h [new file with mode: 0644]
Release/include/cpprest/details/web_utilities.h [new file with mode: 0644]
Release/include/cpprest/filestream.h [new file with mode: 0644]
Release/include/cpprest/http_client.h [new file with mode: 0644]
Release/include/cpprest/http_compression.h [new file with mode: 0644]
Release/include/cpprest/http_headers.h [new file with mode: 0644]
Release/include/cpprest/http_listener.h [new file with mode: 0644]
Release/include/cpprest/http_msg.h [new file with mode: 0644]
Release/include/cpprest/interopstream.h [new file with mode: 0644]
Release/include/cpprest/json.h [new file with mode: 0644]
Release/include/cpprest/oauth1.h [new file with mode: 0644]
Release/include/cpprest/oauth2.h [new file with mode: 0644]
Release/include/cpprest/producerconsumerstream.h [new file with mode: 0644]
Release/include/cpprest/rawptrstream.h [new file with mode: 0644]
Release/include/cpprest/streams.h [new file with mode: 0644]
Release/include/cpprest/uri.h [new file with mode: 0644]
Release/include/cpprest/uri_builder.h [new file with mode: 0644]
Release/include/cpprest/version.h [new file with mode: 0644]
Release/include/cpprest/ws_client.h [new file with mode: 0644]
Release/include/cpprest/ws_msg.h [new file with mode: 0644]
Release/include/pplx/pplx.h [new file with mode: 0644]
Release/include/pplx/pplxcancellation_token.h [new file with mode: 0644]
Release/include/pplx/pplxconv.h [new file with mode: 0644]
Release/include/pplx/pplxinterface.h [new file with mode: 0644]
Release/include/pplx/pplxlinux.h [new file with mode: 0644]
Release/include/pplx/pplxtasks.h [new file with mode: 0644]
Release/include/pplx/pplxwin.h [new file with mode: 0644]
Release/include/pplx/threadpool.h [new file with mode: 0644]
Release/public_apis_doxyfile [new file with mode: 0644]
Release/samples/.gitignore [new file with mode: 0644]
Release/samples/BingRequest/CMakeLists.txt [new file with mode: 0644]
Release/samples/BingRequest/bingrequest.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_Client/BlackJackClient.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_Client/CMakeLists.txt [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_Server/BlackJack_Server.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_Server/CMakeLists.txt [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_Server/Dealer.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_Server/Table.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_Server/Table.h [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_Server/messagetypes.h [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_Server/stdafx.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_Server/stdafx.h [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/App.xaml [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/App.xaml.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/App.xaml.h [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Assets/Logo.png [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Assets/SmallLogo.png [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Assets/SplashScreen.png [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Assets/StoreLogo.png [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/CardShape.xaml [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/CardShape.xaml.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/CardShape.xaml.h [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Cards.PNG [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Common/StandardStyles.xaml [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Package.appxmanifest [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Package.uwp.appxmanifest [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Player.xaml [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.h [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.h [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/messagetypes.h [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/pch.cpp [new file with mode: 0644]
Release/samples/BlackJack/BlackJack_UIClient/pch.h [new file with mode: 0644]
Release/samples/BlackJack/CMakeLists.txt [new file with mode: 0644]
Release/samples/CMakeLists.txt [new file with mode: 0644]
Release/samples/CasaLens/AppCode.html [new file with mode: 0644]
Release/samples/CasaLens/CasaLens141/AppCode.html [new file with mode: 0644]
Release/samples/CasaLens/CasaLens141/css/default.css [new file with mode: 0644]
Release/samples/CasaLens/CasaLens141/image/bing-logo.jpg [new file with mode: 0644]
Release/samples/CasaLens/CasaLens141/image/logo.png [new file with mode: 0644]
Release/samples/CasaLens/CasaLens141/image/wall.jpg [new file with mode: 0644]
Release/samples/CasaLens/CasaLens141/js/default.js [new file with mode: 0644]
Release/samples/CasaLens/ReadMe.txt [new file with mode: 0644]
Release/samples/CasaLens/casalens.cpp [new file with mode: 0644]
Release/samples/CasaLens/casalens.h [new file with mode: 0644]
Release/samples/CasaLens/css/default.css [new file with mode: 0644]
Release/samples/CasaLens/datafetcher.cpp [new file with mode: 0644]
Release/samples/CasaLens/image/bing-logo.jpg [new file with mode: 0644]
Release/samples/CasaLens/image/logo.png [new file with mode: 0644]
Release/samples/CasaLens/image/wall.jpg [new file with mode: 0644]
Release/samples/CasaLens/js/default.js [new file with mode: 0644]
Release/samples/CasaLens/stdafx.cpp [new file with mode: 0644]
Release/samples/CasaLens/stdafx.h [new file with mode: 0644]
Release/samples/FacebookDemo/App.xaml [new file with mode: 0644]
Release/samples/FacebookDemo/App.xaml.cpp [new file with mode: 0644]
Release/samples/FacebookDemo/App.xaml.h [new file with mode: 0644]
Release/samples/FacebookDemo/Assets/Logo.png [new file with mode: 0644]
Release/samples/FacebookDemo/Assets/SmallLogo.png [new file with mode: 0644]
Release/samples/FacebookDemo/Assets/SplashScreen.png [new file with mode: 0644]
Release/samples/FacebookDemo/Assets/StoreLogo.png [new file with mode: 0644]
Release/samples/FacebookDemo/Common/StandardStyles.xaml [new file with mode: 0644]
Release/samples/FacebookDemo/Facebook.cpp [new file with mode: 0644]
Release/samples/FacebookDemo/Facebook.h [new file with mode: 0644]
Release/samples/FacebookDemo/MainPage.xaml [new file with mode: 0644]
Release/samples/FacebookDemo/MainPage.xaml.cpp [new file with mode: 0644]
Release/samples/FacebookDemo/MainPage.xaml.h [new file with mode: 0644]
Release/samples/FacebookDemo/Package.appxmanifest [new file with mode: 0644]
Release/samples/FacebookDemo/Package.uwp.appxmanifest [new file with mode: 0644]
Release/samples/FacebookDemo/pch.cpp [new file with mode: 0644]
Release/samples/FacebookDemo/pch.h [new file with mode: 0644]
Release/samples/OAuth2Live/App.xaml [new file with mode: 0644]
Release/samples/OAuth2Live/App.xaml.cpp [new file with mode: 0644]
Release/samples/OAuth2Live/App.xaml.h [new file with mode: 0644]
Release/samples/OAuth2Live/Assets/Logo.png [new file with mode: 0644]
Release/samples/OAuth2Live/Assets/SmallLogo.png [new file with mode: 0644]
Release/samples/OAuth2Live/Assets/SplashScreen.png [new file with mode: 0644]
Release/samples/OAuth2Live/Assets/StoreLogo.png [new file with mode: 0644]
Release/samples/OAuth2Live/Common/StandardStyles.xaml [new file with mode: 0644]
Release/samples/OAuth2Live/MainPage.xaml [new file with mode: 0644]
Release/samples/OAuth2Live/MainPage.xaml.cpp [new file with mode: 0644]
Release/samples/OAuth2Live/MainPage.xaml.h [new file with mode: 0644]
Release/samples/OAuth2Live/Package.appxmanifest [new file with mode: 0644]
Release/samples/OAuth2Live/Package.uwp.appxmanifest [new file with mode: 0644]
Release/samples/OAuth2Live/pch.cpp [new file with mode: 0644]
Release/samples/OAuth2Live/pch.h [new file with mode: 0644]
Release/samples/Oauth1Client/CMakeLists.txt [new file with mode: 0644]
Release/samples/Oauth1Client/Oauth1Client.cpp [new file with mode: 0644]
Release/samples/Oauth2Client/CMakeLists.txt [new file with mode: 0644]
Release/samples/Oauth2Client/Oauth2Client.cpp [new file with mode: 0644]
Release/samples/SearchFile/CMakeLists.txt [new file with mode: 0644]
Release/samples/SearchFile/searchfile.cpp [new file with mode: 0644]
Release/samples/WindowsLiveAuth/App.xaml [new file with mode: 0644]
Release/samples/WindowsLiveAuth/App.xaml.cpp [new file with mode: 0644]
Release/samples/WindowsLiveAuth/App.xaml.h [new file with mode: 0644]
Release/samples/WindowsLiveAuth/Assets/Logo.png [new file with mode: 0644]
Release/samples/WindowsLiveAuth/Assets/SmallLogo.png [new file with mode: 0644]
Release/samples/WindowsLiveAuth/Assets/SplashScreen.png [new file with mode: 0644]
Release/samples/WindowsLiveAuth/Assets/StoreLogo.png [new file with mode: 0644]
Release/samples/WindowsLiveAuth/Common/StandardStyles.xaml [new file with mode: 0644]
Release/samples/WindowsLiveAuth/MainPage.xaml [new file with mode: 0644]
Release/samples/WindowsLiveAuth/MainPage.xaml.cpp [new file with mode: 0644]
Release/samples/WindowsLiveAuth/MainPage.xaml.h [new file with mode: 0644]
Release/samples/WindowsLiveAuth/Package.appxmanifest [new file with mode: 0644]
Release/samples/WindowsLiveAuth/live_connect.h [new file with mode: 0644]
Release/samples/WindowsLiveAuth/pch.cpp [new file with mode: 0644]
Release/samples/WindowsLiveAuth/pch.h [new file with mode: 0644]
Release/src/.gitignore [new file with mode: 0644]
Release/src/CMakeLists.txt [new file with mode: 0644]
Release/src/http/client/http_client.cpp [new file with mode: 0644]
Release/src/http/client/http_client_asio.cpp [new file with mode: 0644]
Release/src/http/client/http_client_impl.h [new file with mode: 0644]
Release/src/http/client/http_client_msg.cpp [new file with mode: 0644]
Release/src/http/client/http_client_winhttp.cpp [new file with mode: 0644]
Release/src/http/client/http_client_winrt.cpp [new file with mode: 0644]
Release/src/http/client/x509_cert_utilities.cpp [new file with mode: 0644]
Release/src/http/common/connection_pool_helpers.h [new file with mode: 0644]
Release/src/http/common/http_compression.cpp [new file with mode: 0644]
Release/src/http/common/http_helpers.cpp [new file with mode: 0644]
Release/src/http/common/http_msg.cpp [new file with mode: 0644]
Release/src/http/common/internal_http_helpers.h [new file with mode: 0644]
Release/src/http/common/x509_cert_utilities.h [new file with mode: 0644]
Release/src/http/listener/http_listener.cpp [new file with mode: 0644]
Release/src/http/listener/http_listener_msg.cpp [new file with mode: 0644]
Release/src/http/listener/http_server_api.cpp [new file with mode: 0644]
Release/src/http/listener/http_server_asio.cpp [new file with mode: 0644]
Release/src/http/listener/http_server_httpsys.cpp [new file with mode: 0644]
Release/src/http/listener/http_server_httpsys.h [new file with mode: 0644]
Release/src/http/listener/http_server_impl.h [new file with mode: 0644]
Release/src/http/oauth/oauth1.cpp [new file with mode: 0644]
Release/src/http/oauth/oauth2.cpp [new file with mode: 0644]
Release/src/json/json.cpp [new file with mode: 0644]
Release/src/json/json_parsing.cpp [new file with mode: 0644]
Release/src/json/json_serialization.cpp [new file with mode: 0644]
Release/src/pch/stdafx.cpp [new file with mode: 0644]
Release/src/pch/stdafx.h [new file with mode: 0644]
Release/src/pplx/pplx.cpp [new file with mode: 0644]
Release/src/pplx/pplxapple.cpp [new file with mode: 0644]
Release/src/pplx/pplxlinux.cpp [new file with mode: 0644]
Release/src/pplx/pplxwin.cpp [new file with mode: 0644]
Release/src/pplx/threadpool.cpp [new file with mode: 0644]
Release/src/streams/fileio_posix.cpp [new file with mode: 0644]
Release/src/streams/fileio_win32.cpp [new file with mode: 0644]
Release/src/streams/fileio_winrt.cpp [new file with mode: 0644]
Release/src/uri/uri.cpp [new file with mode: 0644]
Release/src/uri/uri_builder.cpp [new file with mode: 0644]
Release/src/utilities/Resource.rc [new file with mode: 0644]
Release/src/utilities/asyncrt_utils.cpp [new file with mode: 0644]
Release/src/utilities/base64.cpp [new file with mode: 0644]
Release/src/utilities/web_utilities.cpp [new file with mode: 0644]
Release/src/websockets/client/ws_client.cpp [new file with mode: 0644]
Release/src/websockets/client/ws_client_impl.h [new file with mode: 0644]
Release/src/websockets/client/ws_client_winrt.cpp [new file with mode: 0644]
Release/src/websockets/client/ws_client_wspp.cpp [new file with mode: 0644]
Release/src/websockets/client/ws_msg.cpp [new file with mode: 0644]
Release/tests/.gitignore [new file with mode: 0644]
Release/tests/CMakeLists.txt [new file with mode: 0644]
Release/tests/common/CMakeLists.txt [new file with mode: 0644]
Release/tests/common/TestRunner/CMakeLists.txt [new file with mode: 0644]
Release/tests/common/TestRunner/test_module_loader.cpp [new file with mode: 0644]
Release/tests/common/TestRunner/test_module_loader.h [new file with mode: 0644]
Release/tests/common/TestRunner/test_runner.cpp [new file with mode: 0644]
Release/tests/common/TestRunner/test_runner.manifest [new file with mode: 0644]
Release/tests/common/UnitTestpp/CMakeLists.txt [new file with mode: 0644]
Release/tests/common/UnitTestpp/COPYING [new file with mode: 0644]
Release/tests/common/UnitTestpp/ThirdPartyNotices.txt [new file with mode: 0644]
Release/tests/common/UnitTestpp/config.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/AssertException.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/AssertException.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/CheckMacros.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/Checks.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/CompositeTestReporter.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/CompositeTestReporter.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/CurrentTest.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/CurrentTest.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/DeferredTestReporter.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/DeferredTestReporter.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/DeferredTestResult.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/DeferredTestResult.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/ExceptionMacros.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/ExecuteTest.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/GlobalSettings.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/GlobalSettings.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/HelperMacros.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/MemoryOutStream.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/MemoryOutStream.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/Posix/SignalTranslator.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/Posix/SignalTranslator.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/Posix/TimeHelpers.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/Posix/TimeHelpers.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/ReportAssert.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/ReportAssert.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/ReportAssertImpl.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/Test.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/Test.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestDetails.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestDetails.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestList.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestList.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestMacros.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestProperties.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestReporter.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestReporter.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestReporterStdout.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestReporterStdout.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestResults.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestResults.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestRunner.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestRunner.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TestSuite.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/TimeHelpers.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/Win32/TimeHelpers.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/Win32/TimeHelpers.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/XmlTestReporter.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/XmlTestReporter.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/stdafx.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/stdafx.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/RecordingReporter.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/ScopedCurrentTest.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestAssertHandler.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestCheckMacros.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestChecks.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestCompositeTestReporter.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestCurrentTest.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestDeferredTestReporter.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestMemoryOutStream.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestTest.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestTestList.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestTestMacros.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestTestResults.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestTestRunner.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestTestSuite.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestUnitTestPP.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/TestXmlTestReporter.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/stdafx.cpp [new file with mode: 0644]
Release/tests/common/UnitTestpp/src/tests/stdafx.h [new file with mode: 0644]
Release/tests/common/UnitTestpp/unittestpp.h [new file with mode: 0644]
Release/tests/common/utilities/CMakeLists.txt [new file with mode: 0644]
Release/tests/common/utilities/include/common_utilities_public.h [new file with mode: 0644]
Release/tests/common/utilities/include/locale_guard.h [new file with mode: 0644]
Release/tests/common/utilities/include/os_utilities.h [new file with mode: 0644]
Release/tests/common/utilities/os_utilities.cpp [new file with mode: 0644]
Release/tests/functional/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/http/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/http/client/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/http/client/authentication_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/building_request_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/client_construction.cpp [new file with mode: 0644]
Release/tests/functional/http/client/compression_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/connection_pool_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/connections_and_errors.cpp [new file with mode: 0644]
Release/tests/functional/http/client/header_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/http_client_fuzz_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/http_client_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/http_client_tests.h [new file with mode: 0644]
Release/tests/functional/http/client/http_methods_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/multiple_requests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/oauth1_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/oauth2_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/outside_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/pipeline_stage_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/progress_handler_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/proxy_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/redirect_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/request_helper_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/request_stream_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/request_uri_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/response_extract_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/response_stream_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/status_code_reason_phrase_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/client/stdafx.cpp [new file with mode: 0644]
Release/tests/functional/http/client/stdafx.h [new file with mode: 0644]
Release/tests/functional/http/client/timeout_handler.h [new file with mode: 0644]
Release/tests/functional/http/client/to_string_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/http/listener/building_response_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/connections_and_errors.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/header_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/http_listener_tests.h [new file with mode: 0644]
Release/tests/functional/http/listener/listener_construction_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/reply_helper_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/request_extract_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/request_handler_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/request_relative_uri_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/request_stream_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/requests_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/response_stream_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/status_code_reason_phrase_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/stdafx.cpp [new file with mode: 0644]
Release/tests/functional/http/listener/stdafx.h [new file with mode: 0644]
Release/tests/functional/http/listener/to_string_tests.cpp [new file with mode: 0644]
Release/tests/functional/http/utilities/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/http/utilities/http_asserts.cpp [new file with mode: 0644]
Release/tests/functional/http/utilities/include/http_asserts.h [new file with mode: 0644]
Release/tests/functional/http/utilities/include/http_test_utilities.h [new file with mode: 0644]
Release/tests/functional/http/utilities/include/http_test_utilities_public.h [new file with mode: 0644]
Release/tests/functional/http/utilities/include/test_http_client.h [new file with mode: 0644]
Release/tests/functional/http/utilities/include/test_http_server.h [new file with mode: 0644]
Release/tests/functional/http/utilities/include/test_server_utilities.h [new file with mode: 0644]
Release/tests/functional/http/utilities/stdafx.cpp [new file with mode: 0644]
Release/tests/functional/http/utilities/stdafx.h [new file with mode: 0644]
Release/tests/functional/http/utilities/test_http_client.cpp [new file with mode: 0644]
Release/tests/functional/http/utilities/test_http_server.cpp [new file with mode: 0644]
Release/tests/functional/http/utilities/test_server_utilities.cpp [new file with mode: 0644]
Release/tests/functional/json/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/json/construction_tests.cpp [new file with mode: 0644]
Release/tests/functional/json/fuzz_tests.cpp [new file with mode: 0644]
Release/tests/functional/json/iterator_tests.cpp [new file with mode: 0644]
Release/tests/functional/json/json_numbers_tests.cpp [new file with mode: 0644]
Release/tests/functional/json/negative_parsing_tests.cpp [new file with mode: 0644]
Release/tests/functional/json/parsing_tests.cpp [new file with mode: 0644]
Release/tests/functional/json/to_as_and_operators_tests.cpp [new file with mode: 0644]
Release/tests/functional/misc/atl_headers/Resource.h [new file with mode: 0644]
Release/tests/functional/misc/atl_headers/header_test.rc [new file with mode: 0644]
Release/tests/functional/misc/atl_headers/header_test1.cpp [new file with mode: 0644]
Release/tests/functional/misc/atl_headers/header_test2.cpp [new file with mode: 0644]
Release/tests/functional/pplx/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/pplx/pplx_test/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/pplx/pplx_test/pplx_op_test.cpp [new file with mode: 0644]
Release/tests/functional/pplx/pplx_test/pplx_task_options.cpp [new file with mode: 0644]
Release/tests/functional/pplx/pplx_test/pplxtask_tests.cpp [new file with mode: 0644]
Release/tests/functional/pplx/pplx_test/stdafx.cpp [new file with mode: 0644]
Release/tests/functional/pplx/pplx_test/stdafx.h [new file with mode: 0644]
Release/tests/functional/streams/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/streams/CppSparseFile.cpp [new file with mode: 0644]
Release/tests/functional/streams/CppSparseFile.h [new file with mode: 0644]
Release/tests/functional/streams/fstreambuf_tests.cpp [new file with mode: 0644]
Release/tests/functional/streams/fuzz_tests.cpp [new file with mode: 0644]
Release/tests/functional/streams/istream_tests.cpp [new file with mode: 0644]
Release/tests/functional/streams/memstream_tests.cpp [new file with mode: 0644]
Release/tests/functional/streams/ostream_tests.cpp [new file with mode: 0644]
Release/tests/functional/streams/prefix.h [new file with mode: 0644]
Release/tests/functional/streams/stdafx.cpp [new file with mode: 0644]
Release/tests/functional/streams/stdafx.h [new file with mode: 0644]
Release/tests/functional/streams/stdstream_tests.cpp [new file with mode: 0644]
Release/tests/functional/streams/streams_tests.h [new file with mode: 0644]
Release/tests/functional/streams/winrt_interop_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/uri/accessor_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/combining_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/constructor_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/conversions_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/diagnostic_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/encoding_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/operator_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/resolve_uri_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/splitting_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/stdafx.cpp [new file with mode: 0644]
Release/tests/functional/uri/stdafx.h [new file with mode: 0644]
Release/tests/functional/uri/uri_builder_tests.cpp [new file with mode: 0644]
Release/tests/functional/uri/uri_tests.h [new file with mode: 0644]
Release/tests/functional/utils/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/utils/base64.cpp [new file with mode: 0644]
Release/tests/functional/utils/datetime.cpp [new file with mode: 0644]
Release/tests/functional/utils/macro_test.cpp [new file with mode: 0644]
Release/tests/functional/utils/nonce_generator_tests.cpp [new file with mode: 0644]
Release/tests/functional/utils/stdafx.cpp [new file with mode: 0644]
Release/tests/functional/utils/stdafx.h [new file with mode: 0644]
Release/tests/functional/utils/strings.cpp [new file with mode: 0644]
Release/tests/functional/utils/utils_tests.h [new file with mode: 0644]
Release/tests/functional/utils/win32_encryption_tests.cpp [new file with mode: 0644]
Release/tests/functional/websockets/CMakeLists.txt [new file with mode: 0644]
Release/tests/functional/websockets/client/authentication_tests.cpp [new file with mode: 0644]
Release/tests/functional/websockets/client/client_construction.cpp [new file with mode: 0644]
Release/tests/functional/websockets/client/close_tests.cpp [new file with mode: 0644]
Release/tests/functional/websockets/client/error_tests.cpp [new file with mode: 0644]
Release/tests/functional/websockets/client/proxy_tests.cpp [new file with mode: 0644]
Release/tests/functional/websockets/client/receive_msg_tests.cpp [new file with mode: 0644]
Release/tests/functional/websockets/client/send_msg_tests.cpp [new file with mode: 0644]
Release/tests/functional/websockets/client/stdafx.cpp [new file with mode: 0644]
Release/tests/functional/websockets/client/stdafx.h [new file with mode: 0644]
Release/tests/functional/websockets/client/websocket_client_tests.h [new file with mode: 0644]
Release/tests/functional/websockets/utilities/stdafx.cpp [new file with mode: 0644]
Release/tests/functional/websockets/utilities/stdafx.h [new file with mode: 0644]
Release/tests/functional/websockets/utilities/test_websocket_server.cpp [new file with mode: 0644]
Release/tests/functional/websockets/utilities/test_websocket_server.h [new file with mode: 0644]
ThirdPartyNotices.txt [new file with mode: 0644]
azure-devops/build-ubuntu-apt.yml [new file with mode: 0644]
azure-devops/build-ubuntu-vcpkg.yml [new file with mode: 0644]
azure-devops/build-windows.yml [new file with mode: 0644]
azure-devops/vcpkg-windows.txt [new file with mode: 0644]
azure-pipelines.yml [new file with mode: 0644]
changelog.md [new file with mode: 0644]
license.txt [new file with mode: 0644]

diff --git a/.clang-format b/.clang-format
new file mode 100644 (file)
index 0000000..bab911a
--- /dev/null
@@ -0,0 +1,46 @@
+# https://releases.llvm.org/7.0.0/tools/clang/docs/ClangFormatStyleOptions.html
+
+---
+Language: Cpp
+
+BasedOnStyle: WebKit
+
+AlignAfterOpenBracket: Align
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: false
+BinPackParameters: false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Allman
+BreakConstructorInitializersBeforeComma: true
+ColumnLimit: 120
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+Cpp11BracedListStyle: true
+FixNamespaceComments: true
+IncludeBlocks:   Regroup
+IncludeCategories:
+  - Regex:           '^["<](stdafx|pch)\.h[">]$'
+    Priority:        -1
+  - Regex:           '^<Windows\.h>$'
+    Priority:        3
+  - Regex:           '^<(WinIoCtl|winhttp|Shellapi)\.h>$'
+    Priority:        4
+  - Regex:           '.*'
+    Priority:        2
+IndentCaseLabels: true
+IndentWidth: 4
+KeepEmptyLinesAtTheStartOfBlocks: false
+MaxEmptyLinesToKeep: 2
+NamespaceIndentation: None
+PenaltyReturnTypeOnItsOwnLine: 1000
+PointerAlignment: Left
+SpaceAfterTemplateKeyword: false
+Standard: Cpp11
+UseTab: Never
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..f9d0b58
--- /dev/null
@@ -0,0 +1,7 @@
+[submodule "vcpkg"]
+       path = vcpkg
+       url = https://github.com/Microsoft/vcpkg
+[submodule "websocketpp"]
+       path = Release/libs/websocketpp
+       url = https://github.com/zaphoyd/websocketpp
+       fetchRecurseSubmodules = false
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644 (file)
index 0000000..655ea37
--- /dev/null
@@ -0,0 +1,19 @@
+{\r
+    // Use IntelliSense to learn about possible attributes.\r
+    // Hover to view descriptions of existing attributes.\r
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\r
+    "version": "0.2.0",\r
+    "configurations": [\r
+        {\r
+            "name": "(Windows) Launch Debug Tests",\r
+            "type": "cppvsdbg",\r
+            "request": "launch",\r
+            "program": "${workspaceFolder}/build.debug/Release/Binaries/test_runner.exe",\r
+            "args": ["*testd.dll"],\r
+            "stopAtEntry": false,\r
+            "cwd": "${workspaceFolder}/build.debug/Release/Binaries",\r
+            "environment": [],\r
+            "externalConsole": true\r
+        }\r
+    ]\r
+}\r
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644 (file)
index 0000000..9bad9cb
--- /dev/null
@@ -0,0 +1,20 @@
+{
+    "files.watcherExclude": {
+        "**/.git/objects/**": true,
+        "**/.git/subtree-cache/**": true,
+        "**/node_modules/*/**": 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
new file mode 100755 (executable)
index 0000000..59b3565
--- /dev/null
@@ -0,0 +1,209 @@
+#!/bin/bash
+# Copyright (C) Microsoft. All rights reserved.
+# Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+# =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+#
+# configure.sh
+#
+# Build script for casablanca on android
+#
+# For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+#
+# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+set -e
+
+# The Android NDK r10e or later may work, but we test with r18b. To download, see the following link:
+# https://developer.android.com/ndk/downloads/index.html
+
+# -----------------
+# Parse args
+# -----------------
+
+DO_BOOST=1
+DO_OPENSSL=1
+DO_CMAKE=1
+DO_CPPRESTSDK=1
+
+BOOSTVER=1.70.0
+OPENSSLVER=1.1.0j
+CMAKEVER=3.14.0
+
+API=15
+STL=c++_shared
+
+function usage {
+    echo "Usage: $0 [--skip-boost] [--skip-openssl] [--skip-cpprestsdk] [-h] [--ndk <android-ndk>]"
+    echo ""
+    echo "    --skip-boost          Skip fetching and compiling boost"
+    echo "    --skip-openssl        Skip fetching and compiling openssl"
+    echo "    --skip-cpprestsdk     Skip compiling cpprestsdk"
+    echo "    --boost <version>     Override the Boost version to build (default is ${BOOSTVER})"
+    echo "    --openssl <version>   Override the OpenSSL version to build (default is ${OPENSSLVER})"
+    echo "    --ndk <android-ndk>   If specified, overrides the ANDROID_NDK environment variable"
+    echo "    -h,--help,-?          Display this information"
+}
+
+while [[ $# > 0 ]]
+do
+    case $1 in
+    "--skip-boost")
+        DO_BOOST=0
+        ;;
+    "--skip-openssl")
+        DO_OPENSSL=0
+        ;;
+    "--skip-cmake")
+        DO_CMAKE=0
+        ;;
+    "--skip-cpprestsdk")
+        DO_CPPRESTSDK=0
+        ;;
+    "--boost")
+        shift
+        DO_BOOST=1
+        BOOSTVER=$1
+        ;;
+    "--cmake")
+        shift
+        DO_CMAKE=1
+        CMAKEVER=$1
+        ;;
+    "--openssl")
+        shift
+        DO_OPENSSL=1
+        OPENSSLVER=$1
+        ;;
+    "--ndk")
+        shift
+        export ANDROID_NDK=$1
+        ;;
+    "-?"|"-h"|"--help")
+        usage
+        exit
+        ;;
+    *)
+        usage
+        exit 1
+        ;;
+    esac
+    shift
+done
+
+# Variables setup
+
+unset BOOST_ROOT
+
+if [ ! -e "${ANDROID_NDK}/ndk-build" ]
+then
+    echo "ANDROID_NDK does not point to a valid NDK."
+    echo "(current setting: '${ANDROID_NDK}')"
+    exit 1
+fi
+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
+fi
+
+# -----------------------
+# Identify the script dir
+# -----------------------
+
+# source:
+# http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
+
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
+  DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
+  SOURCE="$(readlink "$SOURCE")"
+  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
+done
+DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
+
+if [ "$SRC_DIR" == "$DIR" ]
+then
+    echo "You must use a separate directory for building."
+    echo "(try 'mkdir build; cd build; ../configure.sh')"
+    exit 1
+fi
+
+# -------
+# Openssl
+# -------
+
+# This steps are based on the official openssl build instructions
+# http://wiki.openssl.org/index.php/Android
+if [ "${DO_OPENSSL}" == "1" ]; then (
+    if [ ! -d "openssl" ]; then mkdir openssl; fi
+    cd openssl
+    cp -af "${DIR}/openssl/." .
+    make all ANDROID_NDK="${NDK_DIR}" ANDROID_TOOLCHAIN=clang ANDROID_ABI=armeabi-v7a OPENSSL_PREFIX=armeabi-v7a OPENSSL_VERSION=$OPENSSLVER -j $NCPU
+    make all ANDROID_NDK="${NDK_DIR}" ANDROID_TOOLCHAIN=clang ANDROID_ABI=x86 OPENSSL_PREFIX=x86 OPENSSL_VERSION=$OPENSSLVER -j $NCPU
+) fi
+
+# -----
+# Boost
+# -----
+# 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/o01eg/Boost-for-Android/; fi
+    cd Boost-for-Android
+    git checkout 7626dd6f7cab7866dce20e685d4a1b11194366a7
+    PATH="$PATH:$NDK_DIR" \
+    CXXFLAGS="-std=gnu++11" \
+    ./build-android.sh \
+        --boost=$BOOSTVER \
+        --arch=armeabi-v7a,x86 \
+        --with-libraries=atomic,random,date_time,filesystem,system,thread,chrono \
+        "${NDK_DIR}" || exit 1
+) fi
+
+# ------
+# CMake
+# ------
+# We update CMake because the version included with Ubuntu is too old to handle Boost 1.69.
+
+if [ "${DO_CMAKE}" == "1" ]; then (
+    if [ ! -d "cmake-${CMAKEVER}" ]; then wget https://github.com/Kitware/CMake/releases/download/v${CMAKEVER}/cmake-${CMAKEVER}-Linux-x86_64.sh; fi
+    chmod +x cmake-${CMAKEVER}-Linux-x86_64.sh
+    rm -rf cmake-${CMAKEVER}
+    mkdir cmake-${CMAKEVER}
+    cd cmake-${CMAKEVER}
+    ../cmake-${CMAKEVER}-Linux-x86_64.sh --skip-license
+) fi
+
+# ----------
+# casablanca
+# ----------
+
+if [ "${DO_CPPRESTSDK}" == "1" ]; then
+    # Use the builtin CMake toolchain configuration that comes with the NDK
+    function build_cpprestsdk { (
+    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
+        make -j $NCPU -C $1
+    ) }
+
+    # Build the cpprestsdk for each target configuration
+    build_cpprestsdk build.armv7.debug armeabi-v7a Debug
+    build_cpprestsdk build.armv7.release armeabi-v7a Release
+    build_cpprestsdk build.x86.debug x86 Debug
+    build_cpprestsdk build.x86.release x86 Release
+fi
diff --git a/Build_android/openssl/Makefile b/Build_android/openssl/Makefile
new file mode 100644 (file)
index 0000000..2da8fa4
--- /dev/null
@@ -0,0 +1,176 @@
+# Configuration parameters
+ANDROID_API = 18
+ANDROID_ABI = armeabi-v7a
+ANDROID_HOST = linux-x86_64
+ANDROID_TOOLCHAIN = gcc
+ANDROID_GCC_VERSION = 4.8
+OPENSSL_VERSION = 1.0.2k
+OPENSSL_PACKAGE = openssl-$(OPENSSL_VERSION)
+OPENSSL_PATCH = $(OPENSSL_PACKAGE).patch
+OPENSSL_TARBALL = $(OPENSSL_PACKAGE).tar.gz
+OPENSSL_URL = https://www.openssl.org/source/$(OPENSSL_TARBALL)
+OPENSSL_OPTIONS = -no-ssl2 -no-ssl3 -no-comp -no-hw -no-engine
+OPENSSL_PREFIX = android-$(ANDROID_API)-$(ANDROID_ABI)-$(ANDROID_TOOLCHAIN)
+OPENSSL_SOURCE = $(OPENSSL_PACKAGE)-$(ANDROID_ABI)
+
+# Setup target parameters from ABI
+ifneq ($(findstring armeabi,$(ANDROID_ABI)),)
+ANDROID_ARCH := arm
+ANDROID_TRIPLE := arm-linux-androideabi
+ANDROID_TOOLARCH := $(ANDROID_TRIPLE)
+OPENSSL_MACHINE := armv7
+OPENSSL_SYSTEM := android
+OPENSSL_TARGET := android-armeabi
+endif
+
+ifneq ($(findstring aarch64,$(ANDROID_ABI)),)
+ANDROID_ARCH := arm64
+ANDROID_TRIPLE := aarch64-linux-android
+ANDROID_TOOLARCH := $(ANDROID_TRIPLE)
+OPENSSL_MACHINE := aarch64
+OPENSSL_SYSTEM := android64
+OPENSSL_TARGET := android64-aarch64
+endif
+
+ifneq ($(findstring x86,$(ANDROID_ABI)),)
+ANDROID_ARCH := x86
+ANDROID_TRIPLE := i686-linux-android
+ANDROID_TOOLARCH := $(ANDROID_ARCH)
+OPENSSL_MACHINE := i686
+OPENSSL_SYSTEM := android
+OPENSSL_TARGET := android-x86
+endif
+
+# Validate Android NDK directory paths and use fallback directories where applicable
+define direxists =
+$(if $(wildcard $(1)),$(strip $(1)),"")
+endef
+
+define findfirstdir =
+$(call direxists,$(firstword $(filter-out "",$(foreach val,$(3),$(call direxists,$(subst $(2),$(strip $(val)),$(1)))))))
+endef
+
+ifeq ($(call direxists,$(ANDROID_NDK)),"")
+$(error invalid Android NDK root directory)
+endif
+
+ANDROID_LINK_SYSROOT := $(ANDROID_NDK)/platforms/android-$(ANDROID_API)/arch-$(ANDROID_ARCH)
+ifeq ($(call direxists,$(ANDROID_LINK_SYSROOT)),"")
+$(error invalid Android ABI or API level, could not locate Android NDK sysroot directory)
+endif
+
+ANDROID_SYSROOT := $(ANDROID_NDK)/sysroot
+ifeq ($(call direxists,$(ANDROID_SYSROOT)),"")
+ANDROID_SYSROOT := $(ANDROID_LINK_SYSROOT)
+endif
+
+ANDROID_GCC_VERSIONS := $(ANDROID_GCC_VERSION) 4.9 4.8
+ANDROID_GCC_PREBUILT_template := $(ANDROID_NDK)/toolchains/$(ANDROID_TOOLARCH)-<<VERSION>>/prebuilt
+ANDROID_GCC_PREBUILT := $(call findfirstdir,$(ANDROID_GCC_PREBUILT_template),<<VERSION>>,$(ANDROID_GCC_VERSIONS))
+ifeq ($(ANDROID_GCC_PREBUILT),"")
+$(error could not determine Android NDK GCC toolchain prebuilt directory)
+endif
+
+ANDROID_HOSTS := $(ANDROID_HOST) linux-x86_64 linux-x86 darwin-x86_64 darwin-x86
+ANDROID_GCC_TOOLCHAIN_template := $(ANDROID_GCC_PREBUILT)/<<HOST>>
+ANDROID_GCC_TOOLCHAIN := $(call findfirstdir,$(ANDROID_GCC_TOOLCHAIN_template),<<HOST>>,$(ANDROID_HOSTS))
+ifeq ($(ANDROID_GCC_TOOLCHAIN),"")
+$(error could not determine Android NDK GCC toolchain host directory)
+endif
+
+ANDROID_LLVM_VERSIONS := llvm llvm-3.6 llvm-3.5 llvm-3.4
+ANDROID_LLVM_TOOLCHAIN_template := $(ANDROID_NDK)/toolchains/<<LLVM>>/prebuilt/$(notdir $(ANDROID_GCC_TOOLCHAIN))
+ANDROID_LLVM_TOOLCHAIN := $(call findfirstdir,$(ANDROID_LLVM_TOOLCHAIN_template),<<LLVM>>,$(ANDROID_LLVM_VERSIONS))
+ifeq ($(ANDROID_LLVM_TOOLCHAIN),"")
+$(error could not determine Android NDK LLVM toolchain directory)
+endif
+
+# Configure toolchain
+OPENSSL_CROSS_COMPILE :=
+OPENSSL_CC :=
+OPENSSL_RANLIB := $(ANDROID_GCC_TOOLCHAIN)/bin/$(ANDROID_TRIPLE)-ranlib
+
+ifneq ($(findstring clang,$(ANDROID_TOOLCHAIN)),)
+OPENSSL_TARGET := $(OPENSSL_TARGET)-clang
+OPENSSL_CC := $(ANDROID_LLVM_TOOLCHAIN)/bin/clang
+endif
+
+ifneq ($(findstring gcc,$(ANDROID_TOOLCHAIN)),)
+OPENSSL_CROSS_COMPILE := $(ANDROID_TRIPLE)-
+OPENSSL_CC := $(ANDROID_GCC_TOOLCHAIN)/bin/$(ANDROID_TRIPLE)-gcc
+endif
+
+ifeq ($(OPENSSL_CC),)
+$(error invalid toolchain specified for ANDROID_TOOLCHAIN)
+endif
+
+all: info $(OPENSSL_PREFIX)/lib/libssl.a
+
+$(OPENSSL_TARBALL):
+       @echo "Downloading OpenSSL tarball"
+       wget $(OPENSSL_URL)
+
+$(OPENSSL_PREFIX)/lib/libssl.a: $(OPENSSL_TARBALL)
+       @echo "Decompressing OpenSSL package" && \
+       ( \
+               set -e; \
+               rm -rf $(OPENSSL_SOURCE); \
+               rm -rf $(OPENSSL_PACKAGE); \
+               tar xzf $(OPENSSL_TARBALL); \
+       ) && \
+       mv $(OPENSSL_PACKAGE) $(OPENSSL_SOURCE)
+       @if test -f $(OPENSSL_PATCH); then \
+               echo "Patching OpenSSL source tree"; \
+               ( cd $(OPENSSL_SOURCE) && patch -p1 < ../$(OPENSSL_PATCH) ); \
+       fi
+       @echo "Building OpenSSL" && \
+       export ANDROID_NDK="$(ANDROID_NDK)" && \
+       export ANDROID_API="$(ANDROID_API)" && \
+       export ANDROID_ARCH="$(ANDROID_ARCH)" && \
+       export ANDROID_TRIPLE="$(ANDROID_TRIPLE)" && \
+       export ANDROID_SYSROOT="$(ANDROID_SYSROOT)" && \
+       export ANDROID_LINK_SYSROOT="$(ANDROID_LINK_SYSROOT)" && \
+       export ANDROID_GCC_TOOLCHAIN="$(ANDROID_GCC_TOOLCHAIN)" && \
+       export CROSS_SYSROOT="$(ANDROID_SYSROOT)" && \
+       export SYSROOT="$(ANDROID_SYSROOT)" && \
+       export ARCH="$(ANDROID_ARCH)" && \
+       export MACHINE="$(OPENSSL_MACHINE)" && \
+       export SYSTEM="$(OPENSSL_SYSTEM)" && \
+       export CROSS_COMPILE="$(OPENSSL_CROSS_COMPILE)" && \
+       export HOSTCC="$(ANDROID_TOOLCHAIN)" && \
+       export PATH="$(ANDROID_GCC_TOOLCHAIN)/bin:$(ANDROID_LLVM_TOOLCHAIN)/bin:$(PATH)" && \
+       ( \
+               cd $(OPENSSL_SOURCE); \
+               perl Configure $(OPENSSL_TARGET) shared $(OPENSSL_OPTIONS) --prefix="`pwd`/../$(OPENSSL_PREFIX)" $(OPENSSL_CFLAGS) && \
+               make depend && \
+               make all && \
+               make install CC=$(OPENSSL_CC) RANLIB=$(OPENSSL_RANLIB); \
+       )
+
+clean:
+       @echo "Cleaning"
+       rm -rf $(OPENSSL_SOURCE)
+       rm -rf $(OPENSSL_PACKAGE)
+       rm -rf $(OPENSSL_PREFIX)
+
+info:
+       @echo "OpenSSL build options"
+       @echo "ANDROID_NDK = $(ANDROID_NDK)"
+       @echo "ANDROID_API = $(ANDROID_API)"
+       @echo "ANDROID_ABI = $(ANDROID_ABI)"
+       @echo "ANDROID_HOST = $(ANDROID_HOST)"
+       @echo "ANDROID_TOOLCHAIN = $(ANDROID_TOOLCHAIN)"
+       @echo "ANDROID_TRIPLE = $(ANDROID_TRIPLE)"
+       @echo "ANDROID_SYSROOT = $(ANDROID_SYSROOT)"
+       @echo "ANDROID_LINK_SYSROOT = $(ANDROID_LINK_SYSROOT)"
+       @echo "ANDROID_GCC_TOOLCHAIN = $(ANDROID_GCC_TOOLCHAIN)"
+       @echo "ANDROID_LLVM_TOOLCHAIN = $(ANDROID_LLVM_TOOLCHAIN)"
+       @echo "OPENSSL_VERSION = $(OPENSSL_VERSION)"
+       @echo "OPENSSL_URL = $(OPENSSL_URL)"
+       @echo "OPENSSL_OPTIONS = $(OPENSSL_OPTIONS)"
+       @echo "OPENSSL_PREFIX = $(OPENSSL_PREFIX)"
+       @echo "OPENSSL_CFLAGS = $(OPENSSL_CFLAGS)"
+       @echo "OPENSSL_CC = $(OPENSSL_CC)"
+       @echo "OPENSSL_RANLIB = $(OPENSSL_RANLIB)"
+
+.PHONY: all clean info
diff --git a/Build_android/openssl/openssl-1.0.2k.patch b/Build_android/openssl/openssl-1.0.2k.patch
new file mode 100644 (file)
index 0000000..992a27e
--- /dev/null
@@ -0,0 +1,108 @@
+This patch applies several changes that enable OpenSSL 1.0.2k to be built
+for Android using either Clang or GCC toolchains.
+
+An alias for the android-armv7 target, named android-armeabi, is added for
+compatability with the OpenSSL 1.1.0 configuration target names. Support for
+the AArch64 archicture is also added, as well as targets using the Clang
+compiler.
+
+Clang does not recognize some of the ARM assembly nmenonics that are used in
+OpenSSL. In particular, the 'adrl' pseudo instruction is not supported. To
+further complicate matters, Clang doesn't support immediate fixup values so
+the alternative adr/sub sequence used for the Thumb2 code path cannot be
+used either. Instead it is replaced with a sequence of instructions that
+computes the offset at runtime. It utilizes register r4 for computing the
+intermediate result, which is first saved to and later restored from the
+stack. The upstream bug in LLVM can be found here:
+https://llvm.org/bugs/show_bug.cgi?id=24350
+
+diff -Naur org/Configure mod/Configure
+--- org/Configure      2017-01-26 05:22:03.000000000 -0800
++++ mod/Configure      2018-01-17 14:25:44.712943600 -0800
+@@ -471,10 +471,17 @@
+ "linux-alpha+bwx-ccc","ccc:-fast -readonly_strings -DL_ENDIAN::-D_REENTRANT:::SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_PTR DES_RISC1 DES_UNROLL:${alpha_asm}",
+ # Android: linux-* but without pointers to headers and libs.
+-"android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-x86","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armv7","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armeabi","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android64-aarch64","gcc:-march=armv8-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-mips","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-x86-clang","clang:-target i686-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armv7-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armeabi-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android64-aarch64-clang","clang:-target aarch64-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-mips-clang","clang:-target mipsel-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+ #### *BSD [do see comment about ${BSDthreads} above!]
+ "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+diff -Naur org/crypto/bn/asm/armv4-gf2m.pl mod/crypto/bn/asm/armv4-gf2m.pl
+--- org/crypto/bn/asm/armv4-gf2m.pl    2017-01-26 05:22:03.000000000 -0800
++++ mod/crypto/bn/asm/armv4-gf2m.pl    2018-01-17 11:38:57.297482700 -0800
+@@ -213,8 +213,8 @@
+ .align        5
+ .LNEON:
+       ldr             r12, [sp]               @ 5th argument
+-      vmov.32         $a, r2, r1
+-      vmov.32         $b, r12, r3
++      vmov            $a, r2, r1
++      vmov            $b, r12, r3
+       vmov.i64        $k48, #0x0000ffffffffffff
+       vmov.i64        $k32, #0x00000000ffffffff
+       vmov.i64        $k16, #0x000000000000ffff
+diff -Naur org/crypto/sha/asm/sha256-armv4.pl mod/crypto/sha/asm/sha256-armv4.pl
+--- org/crypto/sha/asm/sha256-armv4.pl 2017-01-26 05:22:03.000000000 -0800
++++ mod/crypto/sha/asm/sha256-armv4.pl 2018-01-17 11:38:57.306342000 -0800
+@@ -576,6 +576,7 @@
+ my @MSG=map("q$_",(8..11));
+ my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15));
+ my $Ktbl="r3";
++my $Temp="r4";
+ $code.=<<___;
+ #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+@@ -591,7 +592,13 @@
+ sha256_block_data_order_armv8:
+ .LARMv8:
+       vld1.32 {$ABCD,$EFGH},[$ctx]
+-# ifdef __thumb2__
++# if defined(__clang__)
++      stmdb   sp!,{r4,lr}
++      adr     $Ktbl,.LARMv8
++      ldr     $Temp,=K256
++      sub     $Temp,$Ktbl,$Temp
++      sub     $Ktbl,$Ktbl,$Temp
++# elif defined(__thumb2__)
+       adr     $Ktbl,.LARMv8
+       sub     $Ktbl,$Ktbl,#.LARMv8-K256
+ # else
+@@ -655,7 +662,12 @@
+       vst1.32         {$ABCD,$EFGH},[$ctx]
++# ifdef __clang__
++      ldmia           sp!,{r4,pc}
++# else
+       ret             @ bx lr
++# endif
++
+ .size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8
+ #endif
+ ___
+diff -Naur org/Makefile.org mod/Makefile.org
+--- org/Makefile.org   2017-01-26 05:22:03.000000000 -0800
++++ mod/Makefile.org   2018-01-17 14:26:20.623418300 -0800
+@@ -532,7 +532,7 @@
+       @$(MAKE) SDIRS='$(SDIRS)' clean
+       @$(MAKE) TAR='$(TAR)' TARFLAGS='$(TARFLAGS)' $(DISTTARVARS) tar
+-install: all install_docs install_sw
++install: install_docs install_sw
+ install_sw:
+       @$(PERL) $(TOP)/util/mkdir-p.pl $(INSTALL_PREFIX)$(INSTALLTOP)/bin \
diff --git a/Build_android/openssl/openssl-1.0.2l.patch b/Build_android/openssl/openssl-1.0.2l.patch
new file mode 100644 (file)
index 0000000..03acbee
--- /dev/null
@@ -0,0 +1,108 @@
+This patch applies several changes that enable OpenSSL 1.0.2l to be built
+for Android using either Clang or GCC toolchains.
+
+An alias for the android-armv7 target, named android-armeabi, is added for
+compatability with the OpenSSL 1.1.0 configuration target names. Support for
+the AArch64 archicture is also added, as well as targets using the Clang
+compiler.
+
+Clang does not recognize some of the ARM assembly nmenonics that are used in
+OpenSSL. In particular, the 'adrl' pseudo instruction is not supported. To
+further complicate matters, Clang doesn't support immediate fixup values so
+the alternative adr/sub sequence used for the Thumb2 code path cannot be
+used either. Instead it is replaced with a sequence of instructions that
+computes the offset at runtime. It utilizes register r4 for computing the
+intermediate result, which is first saved to and later restored from the
+stack. The upstream bug in LLVM can be found here:
+https://llvm.org/bugs/show_bug.cgi?id=24350
+
+diff -Naur org/Configure mod/Configure
+--- org/Configure      2017-05-25 05:54:38.000000000 -0700
++++ mod/Configure      2018-01-17 20:36:12.497485400 -0800
+@@ -471,10 +471,17 @@
+ "linux-alpha+bwx-ccc","ccc:-fast -readonly_strings -DL_ENDIAN::-D_REENTRANT:::SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_PTR DES_RISC1 DES_UNROLL:${alpha_asm}",
+ # Android: linux-* but without pointers to headers and libs.
+-"android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-x86","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armv7","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armeabi","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android64-aarch64","gcc:-march=armv8-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-mips","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-x86-clang","clang:-target i686-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armv7-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armeabi-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android64-aarch64-clang","clang:-target aarch64-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-mips-clang","clang:-target mipsel-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+ #### *BSD [do see comment about ${BSDthreads} above!]
+ "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+diff -Naur org/crypto/bn/asm/armv4-gf2m.pl mod/crypto/bn/asm/armv4-gf2m.pl
+--- org/crypto/bn/asm/armv4-gf2m.pl    2017-05-25 05:54:34.000000000 -0700
++++ mod/crypto/bn/asm/armv4-gf2m.pl    2018-01-17 20:36:12.508421200 -0800
+@@ -213,8 +213,8 @@
+ .align        5
+ .LNEON:
+       ldr             r12, [sp]               @ 5th argument
+-      vmov.32         $a, r2, r1
+-      vmov.32         $b, r12, r3
++      vmov            $a, r2, r1
++      vmov            $b, r12, r3
+       vmov.i64        $k48, #0x0000ffffffffffff
+       vmov.i64        $k32, #0x00000000ffffffff
+       vmov.i64        $k16, #0x000000000000ffff
+diff -Naur org/crypto/sha/asm/sha256-armv4.pl mod/crypto/sha/asm/sha256-armv4.pl
+--- org/crypto/sha/asm/sha256-armv4.pl 2017-05-25 05:54:34.000000000 -0700
++++ mod/crypto/sha/asm/sha256-armv4.pl 2018-01-17 20:36:12.518242800 -0800
+@@ -576,6 +576,7 @@
+ my @MSG=map("q$_",(8..11));
+ my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15));
+ my $Ktbl="r3";
++my $Temp="r4";
+ $code.=<<___;
+ #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+@@ -591,7 +592,13 @@
+ sha256_block_data_order_armv8:
+ .LARMv8:
+       vld1.32 {$ABCD,$EFGH},[$ctx]
+-# ifdef __thumb2__
++# if defined(__clang__)
++      stmdb   sp!,{r4,lr}
++      adr     $Ktbl,.LARMv8
++      ldr     $Temp,=K256
++      sub     $Temp,$Ktbl,$Temp
++      sub     $Ktbl,$Ktbl,$Temp
++# elif defined(__thumb2__)
+       adr     $Ktbl,.LARMv8
+       sub     $Ktbl,$Ktbl,#.LARMv8-K256
+ # else
+@@ -655,7 +662,12 @@
+       vst1.32         {$ABCD,$EFGH},[$ctx]
++# ifdef __clang__
++      ldmia           sp!,{r4,pc}
++# else
+       ret             @ bx lr
++# endif
++
+ .size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8
+ #endif
+ ___
+diff -Naur org/Makefile.org mod/Makefile.org
+--- org/Makefile.org   2017-05-25 05:54:38.000000000 -0700
++++ mod/Makefile.org   2018-01-17 20:36:12.532553700 -0800
+@@ -540,7 +540,7 @@
+       @$(MAKE) SDIRS='$(SDIRS)' clean
+       @$(MAKE) TAR='$(TAR)' TARFLAGS='$(TARFLAGS)' $(DISTTARVARS) tar
+-install: all install_docs install_sw
++install: install_docs install_sw
+ install_sw:
+       @$(PERL) $(TOP)/util/mkdir-p.pl $(INSTALL_PREFIX)$(INSTALLTOP)/bin \
diff --git a/Build_android/openssl/openssl-1.0.2m.patch b/Build_android/openssl/openssl-1.0.2m.patch
new file mode 100644 (file)
index 0000000..8314b1e
--- /dev/null
@@ -0,0 +1,108 @@
+This patch applies several changes that enable OpenSSL 1.0.2m to be built
+for Android using either Clang or GCC toolchains.
+
+An alias for the android-armv7 target, named android-armeabi, is added for
+compatability with the OpenSSL 1.1.0 configuration target names. Support for
+the AArch64 archicture is also added, as well as targets using the Clang
+compiler.
+
+Clang does not recognize some of the ARM assembly nmenonics that are used in
+OpenSSL. In particular, the 'adrl' pseudo instruction is not supported. To
+further complicate matters, Clang doesn't support immediate fixup values so
+the alternative adr/sub sequence used for the Thumb2 code path cannot be
+used either. Instead it is replaced with a sequence of instructions that
+computes the offset at runtime. It utilizes register r4 for computing the
+intermediate result, which is first saved to and later restored from the
+stack. The upstream bug in LLVM can be found here:
+https://llvm.org/bugs/show_bug.cgi?id=24350
+
+diff -Naur org/Configure mod/Configure
+--- org/Configure      2017-11-02 07:32:57.000000000 -0700
++++ mod/Configure      2018-01-17 20:39:03.152448900 -0800
+@@ -471,10 +471,17 @@
+ "linux-alpha+bwx-ccc","ccc:-fast -readonly_strings -DL_ENDIAN::-D_REENTRANT:::SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_PTR DES_RISC1 DES_UNROLL:${alpha_asm}",
+ # Android: linux-* but without pointers to headers and libs.
+-"android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-x86","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armv7","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armeabi","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android64-aarch64","gcc:-march=armv8-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-mips","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-x86-clang","clang:-target i686-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armv7-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armeabi-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android64-aarch64-clang","clang:-target aarch64-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-mips-clang","clang:-target mipsel-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+ #### *BSD [do see comment about ${BSDthreads} above!]
+ "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+diff -Naur org/crypto/bn/asm/armv4-gf2m.pl mod/crypto/bn/asm/armv4-gf2m.pl
+--- org/crypto/bn/asm/armv4-gf2m.pl    2017-11-02 07:32:57.000000000 -0700
++++ mod/crypto/bn/asm/armv4-gf2m.pl    2018-01-17 20:39:03.163187500 -0800
+@@ -213,8 +213,8 @@
+ .align        5
+ .LNEON:
+       ldr             r12, [sp]               @ 5th argument
+-      vmov.32         $a, r2, r1
+-      vmov.32         $b, r12, r3
++      vmov            $a, r2, r1
++      vmov            $b, r12, r3
+       vmov.i64        $k48, #0x0000ffffffffffff
+       vmov.i64        $k32, #0x00000000ffffffff
+       vmov.i64        $k16, #0x000000000000ffff
+diff -Naur org/crypto/sha/asm/sha256-armv4.pl mod/crypto/sha/asm/sha256-armv4.pl
+--- org/crypto/sha/asm/sha256-armv4.pl 2017-11-02 07:32:58.000000000 -0700
++++ mod/crypto/sha/asm/sha256-armv4.pl 2018-01-17 20:39:03.173547800 -0800
+@@ -576,6 +576,7 @@
+ my @MSG=map("q$_",(8..11));
+ my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15));
+ my $Ktbl="r3";
++my $Temp="r4";
+ $code.=<<___;
+ #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+@@ -591,7 +592,13 @@
+ sha256_block_data_order_armv8:
+ .LARMv8:
+       vld1.32 {$ABCD,$EFGH},[$ctx]
+-# ifdef __thumb2__
++# if defined(__clang__)
++      stmdb   sp!,{r4,lr}
++      adr     $Ktbl,.LARMv8
++      ldr     $Temp,=K256
++      sub     $Temp,$Ktbl,$Temp
++      sub     $Ktbl,$Ktbl,$Temp
++# elif defined(__thumb2__)
+       adr     $Ktbl,.LARMv8
+       sub     $Ktbl,$Ktbl,#.LARMv8-K256
+ # else
+@@ -655,7 +662,12 @@
+       vst1.32         {$ABCD,$EFGH},[$ctx]
++# ifdef __clang__
++      ldmia           sp!,{r4,pc}
++# else
+       ret             @ bx lr
++# endif
++
+ .size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8
+ #endif
+ ___
+diff -Naur org/Makefile.org mod/Makefile.org
+--- org/Makefile.org   2017-11-02 07:32:57.000000000 -0700
++++ mod/Makefile.org   2018-01-17 20:39:03.187911200 -0800
+@@ -540,7 +540,7 @@
+       @$(MAKE) SDIRS='$(SDIRS)' clean
+       @$(MAKE) TAR='$(TAR)' TARFLAGS='$(TARFLAGS)' $(DISTTARVARS) tar
+-install: all install_docs install_sw
++install: install_docs install_sw
+ install_sw:
+       @$(PERL) $(TOP)/util/mkdir-p.pl $(INSTALL_PREFIX)$(INSTALLTOP)/bin \
diff --git a/Build_android/openssl/openssl-1.0.2n.patch b/Build_android/openssl/openssl-1.0.2n.patch
new file mode 100644 (file)
index 0000000..7e96205
--- /dev/null
@@ -0,0 +1,108 @@
+This patch applies several changes that enable OpenSSL 1.0.2n to be built
+for Android using either Clang or GCC toolchains.
+
+An alias for the android-armv7 target, named android-armeabi, is added for
+compatability with the OpenSSL 1.1.0 configuration target names. Support for
+the AArch64 archicture is also added, as well as targets using the Clang
+compiler.
+
+Clang does not recognize some of the ARM assembly nmenonics that are used in
+OpenSSL. In particular, the 'adrl' pseudo instruction is not supported. To
+further complicate matters, Clang doesn't support immediate fixup values so
+the alternative adr/sub sequence used for the Thumb2 code path cannot be
+used either. Instead it is replaced with a sequence of instructions that
+computes the offset at runtime. It utilizes register r4 for computing the
+intermediate result, which is first saved to and later restored from the
+stack. The upstream bug in LLVM can be found here:
+https://llvm.org/bugs/show_bug.cgi?id=24350
+
+diff -Naur org/Configure mod/Configure
+--- org/Configure      2017-12-07 05:16:38.000000000 -0800
++++ mod/Configure      2018-01-17 20:41:03.880613500 -0800
+@@ -471,10 +471,17 @@
+ "linux-alpha+bwx-ccc","ccc:-fast -readonly_strings -DL_ENDIAN::-D_REENTRANT:::SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_PTR DES_RISC1 DES_UNROLL:${alpha_asm}",
+ # Android: linux-* but without pointers to headers and libs.
+-"android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+-"android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-x86","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armv7","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armeabi","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android64-aarch64","gcc:-march=armv8-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-mips","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-x86-clang","clang:-target i686-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armv7-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-armeabi-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android64-aarch64-clang","clang:-target aarch64-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
++"android-mips-clang","clang:-target mipsel-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+ #### *BSD [do see comment about ${BSDthreads} above!]
+ "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
+diff -Naur org/crypto/bn/asm/armv4-gf2m.pl mod/crypto/bn/asm/armv4-gf2m.pl
+--- org/crypto/bn/asm/armv4-gf2m.pl    2017-12-07 05:16:38.000000000 -0800
++++ mod/crypto/bn/asm/armv4-gf2m.pl    2018-01-17 20:41:03.891956700 -0800
+@@ -213,8 +213,8 @@
+ .align        5
+ .LNEON:
+       ldr             r12, [sp]               @ 5th argument
+-      vmov.32         $a, r2, r1
+-      vmov.32         $b, r12, r3
++      vmov            $a, r2, r1
++      vmov            $b, r12, r3
+       vmov.i64        $k48, #0x0000ffffffffffff
+       vmov.i64        $k32, #0x00000000ffffffff
+       vmov.i64        $k16, #0x000000000000ffff
+diff -Naur org/crypto/sha/asm/sha256-armv4.pl mod/crypto/sha/asm/sha256-armv4.pl
+--- org/crypto/sha/asm/sha256-armv4.pl 2017-12-07 05:16:38.000000000 -0800
++++ mod/crypto/sha/asm/sha256-armv4.pl 2018-01-17 20:41:03.901983600 -0800
+@@ -576,6 +576,7 @@
+ my @MSG=map("q$_",(8..11));
+ my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15));
+ my $Ktbl="r3";
++my $Temp="r4";
+ $code.=<<___;
+ #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+@@ -591,7 +592,13 @@
+ sha256_block_data_order_armv8:
+ .LARMv8:
+       vld1.32 {$ABCD,$EFGH},[$ctx]
+-# ifdef __thumb2__
++# if defined(__clang__)
++      stmdb   sp!,{r4,lr}
++      adr     $Ktbl,.LARMv8
++      ldr     $Temp,=K256
++      sub     $Temp,$Ktbl,$Temp
++      sub     $Ktbl,$Ktbl,$Temp
++# elif defined(__thumb2__)
+       adr     $Ktbl,.LARMv8
+       sub     $Ktbl,$Ktbl,#.LARMv8-K256
+ # else
+@@ -655,7 +662,12 @@
+       vst1.32         {$ABCD,$EFGH},[$ctx]
++# ifdef __clang__
++      ldmia           sp!,{r4,pc}
++# else
+       ret             @ bx lr
++# endif
++
+ .size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8
+ #endif
+ ___
+diff -Naur org/Makefile.org mod/Makefile.org
+--- org/Makefile.org   2017-12-07 05:16:38.000000000 -0800
++++ mod/Makefile.org   2018-01-17 20:41:03.916748800 -0800
+@@ -540,7 +540,7 @@
+       @$(MAKE) SDIRS='$(SDIRS)' clean
+       @$(MAKE) TAR='$(TAR)' TARFLAGS='$(TARFLAGS)' $(DISTTARVARS) tar
+-install: all install_docs install_sw
++install: install_docs install_sw
+ install_sw:
+       @$(PERL) $(TOP)/util/mkdir-p.pl $(INSTALL_PREFIX)$(INSTALLTOP)/bin \
diff --git a/Build_android/openssl/openssl-1.1.0g.patch b/Build_android/openssl/openssl-1.1.0g.patch
new file mode 100644 (file)
index 0000000..77a9277
--- /dev/null
@@ -0,0 +1,76 @@
+This patch applies several changes that enable OpenSSL 1.1.0g to be built
+for Android using either Clang or GCC toolchains.
+
+diff -Naur org/Configurations/10-main.conf mod/Configurations/10-main.conf
+--- org/Configurations/10-main.conf    2017-11-02 07:29:01.000000000 -0700
++++ mod/Configurations/10-main.conf    2018-01-18 10:59:41.675138500 -0800
+@@ -910,15 +910,27 @@
+         # systems are perfectly capable of executing binaries targeting
+         # Froyo. Keep in mind that in the nutshell Android builds are
+         # about JNI, i.e. shared libraries, not applications.
+-        cflags           => add(picker(default => "-mandroid -fPIC --sysroot=\$(CROSS_SYSROOT) -Wa,--noexecstack")),
++        cflags           => add(picker(default => "-mandroid -fPIC --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wa,--noexecstack")),
+         bin_cflags       => "-pie",
+     },
++    "android-clang" => {
++        inherit_from     => [ "linux-generic32" ],
++        cc               => "clang",
++        cflags           => add(picker(default => "-fPIC --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Qunused-arguments -Wa,--noexecstack")),
++},
+     "android-x86" => {
+         inherit_from     => [ "android", asm("x86_asm") ],
+         cflags           => add(picker(release => "-fomit-frame-pointer")),
+         bn_ops           => "BN_LLONG",
+         perlasm_scheme   => "android",
+     },
++    "android-x86-clang" => {
++        inherit_from     => [ "android-clang", asm("x86_asm") ],
++        cflags           => add(picker(default => "-target i686-none-linux-android",
++                                       release => "-fomit-frame-pointer")),
++        bn_ops           => "BN_LLONG",
++        perlasm_scheme   => "android",
++    },
+     ################################################################
+     # Contemporary Android applications can provide multiple JNI
+     # providers in .apk, targeting multiple architectures. Among
+@@ -943,20 +955,38 @@
+     "android-armeabi" => {
+         inherit_from     => [ "android", asm("armv4_asm") ],
+     },
++    "android-armeabi-clang" => {
++        inherit_from     => [ "android-clang", asm("armv4_asm") ],
++        cflags           => add("-target armv7-none-linux-androideabi"),
++    },
+     "android-mips" => {
+         inherit_from     => [ "android", asm("mips32_asm") ],
+         perlasm_scheme   => "o32",
+     },
+-
++    "android-mips-clang" => {
++        inherit_from     => [ "android-clang", asm("mips32_asm") ],
++        cflags           => add("-target mipsel-none-linux-android"),
++        perlasm_scheme   => "o32",
++    },
+     "android64" => {
+         inherit_from     => [ "linux-generic64" ],
+-        cflags           => add(picker(default => "-mandroid -fPIC --sysroot=\$(CROSS_SYSROOT) -Wa,--noexecstack")),
++        cflags           => add(picker(default => "-mandroid -fPIC --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wa,--noexecstack")),
+         bin_cflags       => "-pie",
+     },
++    "android64-clang" => {
++        inherit_from     => [ "linux-generic64" ],
++        cc               => "clang",
++        cflags           => add(picker(default => "-fPIC --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Qunused-arguments -Wa,--noexecstack")),
++    },
+     "android64-aarch64" => {
+         inherit_from     => [ "android64", asm("aarch64_asm") ],
+         perlasm_scheme   => "linux64",
+     },
++    "android64-aarch64-clang" => {
++        inherit_from     => [ "android64-clang", asm("aarch64_asm") ],
++        cflags           => add("-target aarch64-none-linux-android"),
++        perlasm_scheme   => "linux64",
++    },
+ #### *BSD
+     "BSD-generic32" => {
diff --git a/Build_android/openssl/openssl-1.1.0j.patch b/Build_android/openssl/openssl-1.1.0j.patch
new file mode 100644 (file)
index 0000000..77a9277
--- /dev/null
@@ -0,0 +1,76 @@
+This patch applies several changes that enable OpenSSL 1.1.0g to be built
+for Android using either Clang or GCC toolchains.
+
+diff -Naur org/Configurations/10-main.conf mod/Configurations/10-main.conf
+--- org/Configurations/10-main.conf    2017-11-02 07:29:01.000000000 -0700
++++ mod/Configurations/10-main.conf    2018-01-18 10:59:41.675138500 -0800
+@@ -910,15 +910,27 @@
+         # systems are perfectly capable of executing binaries targeting
+         # Froyo. Keep in mind that in the nutshell Android builds are
+         # about JNI, i.e. shared libraries, not applications.
+-        cflags           => add(picker(default => "-mandroid -fPIC --sysroot=\$(CROSS_SYSROOT) -Wa,--noexecstack")),
++        cflags           => add(picker(default => "-mandroid -fPIC --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wa,--noexecstack")),
+         bin_cflags       => "-pie",
+     },
++    "android-clang" => {
++        inherit_from     => [ "linux-generic32" ],
++        cc               => "clang",
++        cflags           => add(picker(default => "-fPIC --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Qunused-arguments -Wa,--noexecstack")),
++},
+     "android-x86" => {
+         inherit_from     => [ "android", asm("x86_asm") ],
+         cflags           => add(picker(release => "-fomit-frame-pointer")),
+         bn_ops           => "BN_LLONG",
+         perlasm_scheme   => "android",
+     },
++    "android-x86-clang" => {
++        inherit_from     => [ "android-clang", asm("x86_asm") ],
++        cflags           => add(picker(default => "-target i686-none-linux-android",
++                                       release => "-fomit-frame-pointer")),
++        bn_ops           => "BN_LLONG",
++        perlasm_scheme   => "android",
++    },
+     ################################################################
+     # Contemporary Android applications can provide multiple JNI
+     # providers in .apk, targeting multiple architectures. Among
+@@ -943,20 +955,38 @@
+     "android-armeabi" => {
+         inherit_from     => [ "android", asm("armv4_asm") ],
+     },
++    "android-armeabi-clang" => {
++        inherit_from     => [ "android-clang", asm("armv4_asm") ],
++        cflags           => add("-target armv7-none-linux-androideabi"),
++    },
+     "android-mips" => {
+         inherit_from     => [ "android", asm("mips32_asm") ],
+         perlasm_scheme   => "o32",
+     },
+-
++    "android-mips-clang" => {
++        inherit_from     => [ "android-clang", asm("mips32_asm") ],
++        cflags           => add("-target mipsel-none-linux-android"),
++        perlasm_scheme   => "o32",
++    },
+     "android64" => {
+         inherit_from     => [ "linux-generic64" ],
+-        cflags           => add(picker(default => "-mandroid -fPIC --sysroot=\$(CROSS_SYSROOT) -Wa,--noexecstack")),
++        cflags           => add(picker(default => "-mandroid -fPIC --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wa,--noexecstack")),
+         bin_cflags       => "-pie",
+     },
++    "android64-clang" => {
++        inherit_from     => [ "linux-generic64" ],
++        cc               => "clang",
++        cflags           => add(picker(default => "-fPIC --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Qunused-arguments -Wa,--noexecstack")),
++    },
+     "android64-aarch64" => {
+         inherit_from     => [ "android64", asm("aarch64_asm") ],
+         perlasm_scheme   => "linux64",
+     },
++    "android64-aarch64-clang" => {
++        inherit_from     => [ "android64-clang", asm("aarch64_asm") ],
++        cflags           => add("-target aarch64-none-linux-android"),
++        perlasm_scheme   => "linux64",
++    },
+ #### *BSD
+     "BSD-generic32" => {
diff --git a/Build_iOS/.gitignore b/Build_iOS/.gitignore
new file mode 100644 (file)
index 0000000..93f4744
--- /dev/null
@@ -0,0 +1,7 @@
+# iOS folders that dependencies get stored in
+boostoniphone/
+boost
+boost.framework/
+ios-cmake/
+openssl/
+OpenSSL-for-iPhone/
\ No newline at end of file
diff --git a/Build_iOS/CMakeLists.txt b/Build_iOS/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f5c5bae
--- /dev/null
@@ -0,0 +1,133 @@
+project(casablanca-ios NONE)
+cmake_minimum_required(VERSION 3.9)
+
+enable_testing()
+
+set(LIB_CPPREST libcpprest.a)
+set(LIB_CPPREST_LIB_DIR "${CMAKE_CURRENT_BINARY_DIR}/lib")
+
+if (CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET)
+  set (ENV{CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET} ${CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET})
+endif()
+
+set(TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ios-cmake/ios.toolchain.cmake")
+
+set(SIM_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.i386" CACHE INTERNAL "")
+set(SIM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Release" CACHE INTERNAL "")
+
+set(SIM64_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.x86_64" CACHE INTERNAL "")
+set(SIM64_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Release" CACHE INTERNAL "")
+
+set(ARM_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.arm" CACHE INTERNAL "")
+set(ARM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Release" CACHE INTERNAL "")
+
+if (DISABLE_BITCODE)
+  set (ENABLE_BITCODE_ARG -DENABLE_BITCODE=OFF)
+endif()
+
+if (INCLUDE_32BIT)
+  set (IOS_PLATFORM_VALUE OS)
+else()
+  set (IOS_PLATFORM_VALUE OS64)
+endif()
+
+if (DEPLOYMENT_TARGET)
+  set (DEPLOYMENT_TARGET -DIOS_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET})
+endif()
+
+add_test(NAME ios_runner
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../Release/tests/common/testrunner/ios
+    COMMAND xcodebuild test -project ios_runner.xcodeproj -configuration=${CMAKE_BUILD_TYPE} -scheme ios_runner -destination "platform=iOS Simulator,name=iPhone 6" LIBRARY_SEARCH_PATH=${SIM64_BINARY_DIR}
+  )
+
+if (INCLUDE_32BIT)
+set (SIM_BINARY_LIB ${SIM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST})
+file(MAKE_DIRECTORY ${SIM_BINARY_DIR})
+execute_process(WORKING_DIRECTORY ${SIM_BINARY_DIR}
+  COMMAND ${CMAKE_COMMAND}
+    -GXcode
+    -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}
+    -DIOS_PLATFORM=SIMULATOR
+    -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+    "${DEPLOYMENT_TARGET}"
+    "${SIM_SOURCE_DIR}"
+)
+else()
+set (SIM_BINARY_LIB "")
+endif()
+
+file(MAKE_DIRECTORY ${SIM64_BINARY_DIR})
+execute_process(WORKING_DIRECTORY ${SIM64_BINARY_DIR}
+  COMMAND ${CMAKE_COMMAND}
+    -GXcode
+    -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}
+    -DIOS_PLATFORM=SIMULATOR64
+    -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+    "${DEPLOYMENT_TARGET}"
+    "${SIM64_SOURCE_DIR}"
+)
+
+file(MAKE_DIRECTORY ${ARM_BINARY_DIR})
+execute_process(WORKING_DIRECTORY ${ARM_BINARY_DIR}
+  COMMAND ${CMAKE_COMMAND}
+    -GXcode
+    -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}
+    -DIOS_PLATFORM=${IOS_PLATFORM_VALUE}
+    -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+    "${DEPLOYMENT_TARGET}"
+    "${ENABLE_BITCODE_ARG}"    
+    "${ARM_SOURCE_DIR}"
+    )
+
+if (INCLUDE_32BIT)
+set (SIM_TARGET sim)
+## Simulator i386 version
+add_custom_target(sim
+  COMMAND ${CMAKE_COMMAND}
+    --build ${SIM_BINARY_DIR}
+    --config ${CMAKE_BUILD_TYPE}
+    COMMENT "Building for i386 (simulator)"
+VERBATIM
+)
+else()
+set (SIM_TARGET "")
+endif()
+
+## Simulator x86_64 version
+add_custom_target(sim64
+  COMMAND ${CMAKE_COMMAND}
+    --build ${SIM64_BINARY_DIR}
+    --config ${CMAKE_BUILD_TYPE}
+    COMMENT "Building for x86_64 (simulator)"
+VERBATIM
+)
+
+## ARM version
+add_custom_target(arm
+  COMMAND ${CMAKE_COMMAND}
+    --build ${ARM_BINARY_DIR}
+    --config ${CMAKE_BUILD_TYPE}
+  COMMENT "Building for arm"
+  VERBATIM
+)
+
+add_custom_command(
+  OUTPUT ${LIB_CPPREST_LIB_DIR}/${LIB_CPPREST}
+  COMMAND mkdir -p "${LIB_CPPREST_LIB_DIR}"
+  COMMAND lipo -create
+    -output "${LIB_CPPREST_LIB_DIR}/${LIB_CPPREST}"
+    ${SIM_BINARY_LIB}
+    ${SIM64_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST}
+    ${ARM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST}
+  COMMAND cp -R "${CMAKE_CURRENT_SOURCE_DIR}/../Release/include" "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPENDS
+    ${SIM_TARGET}
+    sim64
+    arm
+    ${SIM_BINARY_LIB}
+    "${SIM64_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST}"
+    "${ARM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST}"
+  VERBATIM
+)
+
+add_custom_target(cpprest ALL DEPENDS "${LIB_CPPREST_LIB_DIR}/${LIB_CPPREST}")
diff --git a/Build_iOS/README.md b/Build_iOS/README.md
new file mode 100644 (file)
index 0000000..a5c58cc
--- /dev/null
@@ -0,0 +1 @@
+Please consult the documentation [here](https://github.com/Microsoft/cpprestsdk/wiki/How-to-build-for-iOS) for iOS compilation.
diff --git a/Build_iOS/configure.sh b/Build_iOS/configure.sh
new file mode 100755 (executable)
index 0000000..7045be5
--- /dev/null
@@ -0,0 +1,165 @@
+#!/usr/bin/env bash
+set -e
+
+usage() {
+    echo "Usage: configure.sh [-build_type type] [-deployment_target version] [-config_only] [-include_32bit] [-no_bitcode]"
+    echo "       -build_type defines the CMAKE_BUILD_TYPE used. Defaults to Release."
+    echo "       -deployment_target defines minimum iOS Deployment Target. The default is dependent on ios.toolchain.cmake and currently defaults to 8.0"
+    echo "       -config_only only configures cmake (no make invoked)."
+    echo "       -include_32bit includes the 32-bit arm architectures."
+    echo "       -no_bitcode disables bitcode"
+    echo "       -clean deletes build directory prior to configuring"
+}
+
+ABS_PATH="`dirname \"$0\"`"                 # relative
+ABS_PATH="`( cd \"${ABS_PATH}\" && pwd )`"  # absolutized and normalized
+# Make sure that the path to this file exists and can be retrieved!
+if [ -z "${ABS_PATH}" ]; then
+  echo "Could not fetch the ABS_PATH."
+  exit 1
+fi
+
+CONFIG_ONLY=0
+INCLUDE_32BIT=""
+DISABLE_BITCODE=""
+DEPLOYMENT_TARGET=""
+CLEAN=0
+
+# Command line argument parsing
+while (( "$#" )); do
+    case "$1" in
+        -build_type)
+            if [ "$#" -lt 2 ] || [[ "$2" == -* ]] ; then
+                usage
+                echo "Error: argument $1 expecting a value to follow."
+                exit 1
+            fi
+
+            CPPRESTSDK_BUILD_TYPE=$2
+            shift 2
+            ;;
+        -deployment_target)
+            if [ "$#" -lt 2 ] || [[ "$2" == -* ]] ; then
+                usage
+                echo "Error: argument $1 expecting a value to follow."
+                exit 1
+            fi
+
+            DEPLOYMENT_TARGET="-DDEPLOYMENT_TARGET=$2"
+            shift 2
+            ;;
+        -config_only)
+            CONFIG_ONLY=1
+            shift 1
+            ;;
+        -include_32bit)
+            INCLUDE_32BIT="-DINCLUDE_32BIT=ON"
+            shift 1
+            ;;
+        -no_bitcode)
+            DISABLE_BITCODE="-DDISABLE_BITCODE=ON"
+            shift 1
+            ;;
+        -clean)
+            CLEAN=1
+            shift 1
+            ;;
+        *)
+            usage
+            echo "Error: unsupported argument $1"
+            exit 1
+            ;;
+    esac
+done
+
+## Configuration
+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}
+
+############################ No need to edit anything below this line
+
+## Set some needed variables
+IOS_SDK_VERSION=`xcrun --sdk iphoneos --show-sdk-version`
+
+## Buildsteps below
+
+## Fetch submodules just in case
+git submodule update --init
+
+## Build Boost
+
+if [ ! -e $ABS_PATH/boost.framework ] && [ ! -d $ABS_PATH/boost ]; then
+    if [ ! -d "${ABS_PATH}/Apple-Boost-BuildScript" ]; then
+        git clone https://github.com/faithfracture/Apple-Boost-BuildScript ${ABS_PATH}/Apple-Boost-BuildScript
+    fi
+    pushd ${ABS_PATH}/Apple-Boost-BuildScript
+    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}
+    mv ${ABS_PATH}/boost.framework/Versions/A/Headers ${ABS_PATH}/boost.headers
+    mkdir -p ${ABS_PATH}/boost.framework/Versions/A/Headers
+    mv ${ABS_PATH}/boost.headers ${ABS_PATH}/boost.framework/Versions/A/Headers/boost
+fi
+
+## Build OpenSSL
+
+if [ ! -e ${ABS_PATH}/openssl/lib/libcrypto.a ]; then
+    if [ ! -d "${ABS_PATH}/OpenSSL-for-iPhone" ]; 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 6c665e2a15ba7e834875eecaf4eb93c11605dd9a
+    ./build-libssl.sh --version=${OPENSSL_VERSION}
+    popd
+    mkdir -p ${ABS_PATH}/openssl/lib
+    if [ -e ${ABS_PATH}/OpenSSL-for-iPhone/bin/iPhoneOS${IOS_SDK_VERSION}-arm64.sdk/include ]
+    then
+        cp -r ${ABS_PATH}/OpenSSL-for-iPhone/bin/iPhoneOS${IOS_SDK_VERSION}-arm64.sdk/include ${ABS_PATH}/openssl
+    else
+        echo 'Could not find OpenSSL for iPhone'
+        exit 1
+    fi
+    cp ${ABS_PATH}/OpenSSL-for-iPhone/include/LICENSE ${ABS_PATH}/openssl
+    lipo -create -output ${ABS_PATH}/openssl/lib/libssl.a ${ABS_PATH}/OpenSSL-for-iPhone/bin/iPhone*/lib/libssl.a
+    lipo -create -output ${ABS_PATH}/openssl/lib/libcrypto.a ${ABS_PATH}/OpenSSL-for-iPhone/bin/iPhone*/lib/libcrypto.a
+fi
+
+## Fetch CMake toolchain
+
+if [ ! -e ${ABS_PATH}/ios-cmake/ios.toolchain.cmake ]; then
+    if [ ! -d "${ABS_PATH}/ios-cmake" ]; then
+        git clone https://github.com/leetal/ios-cmake ${ABS_PATH}/ios-cmake
+    fi
+    pushd ${ABS_PATH}/ios-cmake
+    git checkout 2.1.2
+    popd
+fi
+
+## Build CPPRestSDK
+if [ -d "${ABS_PATH}/build.${CPPRESTSDK_BUILD_TYPE}.ios" ]; then
+    if [ "$CLEAN" -eq 1 ]; then
+        echo "Removing directory ${ABS_PATH}/build.${CPPRESTSDK_BUILD_TYPE}.ios prior to configuring."
+        rm -rf "${ABS_PATH}/build.${CPPRESTSDK_BUILD_TYPE}.ios"
+    else
+        printf "WARNING: Running configure on an already existing configuration.\nAny changes to the existing configuration will not be picked up.\nEither remove the directory and re-run configure or run configure with the -clean flag.\n\n"
+    fi
+fi
+
+mkdir -p ${ABS_PATH}/build.${CPPRESTSDK_BUILD_TYPE}.ios
+pushd ${ABS_PATH}/build.${CPPRESTSDK_BUILD_TYPE}.ios
+cmake -DCMAKE_BUILD_TYPE=${CPPRESTSDK_BUILD_TYPE} .. ${INCLUDE_32BIT} ${DISABLE_BITCODE} ${DEPLOYMENT_TARGET}
+if [ "$CONFIG_ONLY" -eq 0 ]; then
+    make
+    printf "\n\n===================================================================================\n"
+    echo ">>>> The final library is available in 'build.${CPPRESTSDK_BUILD_TYPE}.ios/lib/libcpprest.a'"
+    printf "===================================================================================\n\n"
+else
+    printf "\n\n===================================================================================\n"
+    echo ">>>> Configuration complete. Run 'make' in 'build.${CPPRESTSDK_BUILD_TYPE}.ios' to build."
+    printf "===================================================================================\n\n"
+fi
+popd
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4e0377e
--- /dev/null
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.9)\r
+project(cpprestsdk-root NONE)\r
+enable_testing()\r
+add_subdirectory(Release)\r
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
new file mode 100644 (file)
index 0000000..36348fa
--- /dev/null
@@ -0,0 +1,59 @@
+Contributors should submit an update to this file with a commit in order to receive recognition. Thank you for your contributions.
+
+
+List of Contributors
+====================
+
+Microsoft Corporation
+Brian Wengert (bwengert79)
+Leslie Brody (Les1966)
+Michael M (M1xa)
+Matt Peterson (MattPeterson1)
+Dmitry Kolomiets (kolomiets)
+rdeterre
+DeCarabas
+luisfeliu
+intercommiura
+halex2005
+simonlep
+jracle
+gandziej
+adish
+LeonidCSIT
+kreuzerkrieg
+evanc
+Jesse Towner (jwtowner)
+
+Abinsula s.r.l.
+Gianfranco Costamagna (LocutusOfBorg)
+
+AutoDesk Inc.
+Cyrille Fauvel (cyrillef)
+
+Illumina Inc.
+Gery Vessere (gery@vessere.com)
+
+Cisco Systems
+Gergely Lukacsy (glukacsy)
+Chris Deering (deeringc)
+
+Ocedo GmbH
+Henning Pfeiffer (megaposer)
+
+thomasschaub
+
+Trimble
+Tim Boundy (gigaplex)
+
+Rami Abughazaleh (icnocop)
+
+TastenTrick
+Christian Deneke (chris0x44)
+
+leetal
+
+Benjamin Lee (mobileben)
+RenĆ© Meusel (reneme)
+
+Sony Corporation
+Gareth Sylvester-Bradley (garethsb-sony)
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..5d9cd4c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,75 @@
+## 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.  
+
+## Getting Started
+
+[![Vcpkg package](https://repology.org/badge/version-for-repo/vcpkg/cpprestsdk.svg)](https://repology.org/metapackage/cpprestsdk)</br>
+[![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/cpprestsdk.svg)](https://repology.org/metapackage/cpprestsdk)</br>
+[![Ubuntu 18.04 package](https://repology.org/badge/version-for-repo/ubuntu_18_04/cpprestsdk.svg)](https://repology.org/metapackage/cpprestsdk)</br>
+[![Fedora Rawhide package](https://repology.org/badge/version-for-repo/fedora_rawhide/cpprestsdk.svg)](https://repology.org/metapackage/cpprestsdk)</br>
+[![openSUSE Tumbleweed package](https://repology.org/badge/version-for-repo/opensuse_tumbleweed/cpprestsdk.svg)](https://repology.org/metapackage/cpprestsdk)</br>
+[![Debian Testing package](https://repology.org/badge/version-for-repo/debian_testing/cpprestsdk.svg)](https://repology.org/metapackage/cpprestsdk)</br>
+
+[![Build Status](https://dev.azure.com/vclibs/cpprestsdk/_apis/build/status/Microsoft.cpprestsdk.Ubuntu)](https://dev.azure.com/vclibs/cpprestsdk/_build/latest?definitionId=1)
+
+With [vcpkg](https://github.com/Microsoft/vcpkg) on Windows
+```
+PS> vcpkg install cpprestsdk cpprestsdk:x64-windows
+```
+With [apt-get](https://launchpad.net/ubuntu/+source/casablanca/2.8.0-2build2) on Debian/Ubuntu
+```
+$ sudo apt-get install libcpprest-dev
+```
+With [dnf](https://apps.fedoraproject.org/packages/cpprest) on Fedora
+```
+$ sudo dnf install cpprest-devel
+```
+With [brew](https://github.com/Homebrew/homebrew-core/blob/master/Formula/cpprestsdk.rb) on OSX
+```
+$ brew install cpprestsdk
+```
+With [NuGet](https://www.nuget.org/packages/cpprestsdk.android/) on Windows for Android
+```
+PM> Install-Package cpprestsdk.android
+```
+For other platforms, install options, how to build from source, and more, take a look at our [Documentation](https://github.com/Microsoft/cpprestsdk/wiki).
+
+Once you have the library, look at our [tutorial](https://github.com/Microsoft/cpprestsdk/wiki/Getting-Started-Tutorial) to use the http_client. It walks through how to setup a project to use the C++ Rest SDK and make a basic Http request.
+
+To use from CMake:
+```cmake
+cmake_minimum_required(VERSION 3.9)
+project(main)
+
+find_package(cpprestsdk REQUIRED)
+
+add_executable(main main.cpp)
+target_link_libraries(main PRIVATE cpprestsdk::cpprest)
+```
+
+## What's in the SDK:
+
+*   Features - HTTP client/server, JSON, URI, asynchronous streams, WebSockets client, oAuth
+*   PPL Tasks - A powerful model for composing asynchronous operations based on C++ 11 features
+*   Platforms - Windows desktop, Windows Store (UWP), Linux, OS X, Unix, iOS, and Android
+*   Support for [Visual Studio 2015 and 2017](https://visualstudio.microsoft.com/) with debugger visualizers
+
+## Contribute Back!
+
+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.  
+
+## Having Trouble?
+
+We'd love to get your review score, whether good or bad, but even more than that, we want to fix your problem. If you submit your issue as a Review, we won't be able to respond to your problem and ask any follow-up questions that may be necessary. The most efficient way to do that is to open an issue in our [issue tracker](https://github.com/Microsoft/cpprestsdk/issues).  
+
+### Quick Links
+
+*   [FAQ](https://github.com/Microsoft/cpprestsdk/wiki/FAQ)
+*   [Documentation](https://github.com/Microsoft/cpprestsdk/wiki)
+*   [Issue Tracker](https://github.com/Microsoft/cpprestsdk/issues)
+
+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/.gitignore b/Release/.gitignore
new file mode 100644 (file)
index 0000000..4b6047c
--- /dev/null
@@ -0,0 +1,6 @@
+.ninja_*
+*.ninja
+CMakeFiles/
+CMakeCache.txt
+CTestTestfile.cmake
+cmake_install.cmake
diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b8f3809
--- /dev/null
@@ -0,0 +1,270 @@
+set(CMAKE_LEGACY_CYGWIN_WIN32 0)
+cmake_minimum_required(VERSION 3.9)
+if(POLICY CMP0042)
+  cmake_policy(SET CMP0042 NEW) # use MACOSX_RPATH
+endif()
+if(UNIX)
+  project(cpprestsdk C CXX)
+else()
+  project(cpprestsdk CXX)
+endif()
+
+set(CPPREST_VERSION_MAJOR 2)
+set(CPPREST_VERSION_MINOR 10)
+set(CPPREST_VERSION_REVISION 18)
+
+enable_testing()
+
+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 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.")
+
+if(IOS OR ANDROID)
+  set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries")
+else()
+  set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries")
+endif()
+
+if(IOS OR ANDROID OR WINDOWS_STORE OR WINDOWS_PHONE)
+  set(BUILD_TESTS OFF CACHE BOOL "Build tests.")
+  set(BUILD_SAMPLES OFF CACHE BOOL "Build sample applications.")
+else()
+  set(BUILD_TESTS ON CACHE BOOL "Build tests.")
+  set(BUILD_SAMPLES ON CACHE BOOL "Build sample applications.")
+endif()
+
+if(WIN32)
+  set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Default filename postfix for libraries under configuration DEBUG")
+else()
+  set(CMAKE_DEBUG_POSTFIX "" CACHE STRING "Default filename postfix for libraries under configuration DEBUG")
+endif()
+
+if(WIN32)
+  set(TOOLSET)
+  if(CMAKE_VS_PLATFORM_TOOLSET)
+    string(REGEX REPLACE "^v" "" TOOLSET "${CMAKE_VS_PLATFORM_TOOLSET}")
+  endif()
+  set(CPPREST_ABI_TAG "${TOOLSET}_${CPPREST_VERSION_MAJOR}_${CPPREST_VERSION_MINOR}" CACHE STRING "Postfix tag for the cpprest abi")
+else()
+  set(CPPREST_ABI_TAG "" CACHE STRING "Postfix tag for the cpprest abi")
+endif()
+
+if(ANDROID)
+  set(Boost_USE_STATIC_LIBS ON CACHE BOOL "Link against boost statically.")
+else()
+  set(Boost_USE_STATIC_LIBS OFF CACHE BOOL "Link against boost statically.")
+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)
+include(CheckIncludeFiles)
+include(GNUInstallDirs)
+
+find_package(Threads REQUIRED)
+if(THREADS_HAVE_PTHREAD_ARG)
+  add_compile_options(-pthread)
+endif()
+if(CMAKE_THREAD_LIBS_INIT)
+  link_libraries(${CMAKE_THREAD_LIBS_INIT})
+endif()
+
+# Internal component selection logic. This allows us to avoid duplicating platform logic in multiple places.
+if(CPPREST_EXCLUDE_WEBSOCKETS)
+  set(CPPREST_WEBSOCKETS_IMPL none CACHE STRING "Internal use.")
+endif()
+
+if(NOT WIN32)
+  CHECK_INCLUDE_FILES(xlocale.h HAVE_XLOCALE_H)
+endif()
+
+if(APPLE) # Note: also iOS
+  set(CPPREST_PPLX_IMPL apple CACHE STRING "Internal use.")
+  set(CPPREST_WEBSOCKETS_IMPL wspp CACHE STRING "Internal use.")
+  set(CPPREST_FILEIO_IMPL posix CACHE STRING "Internal use.")
+  set(CPPREST_HTTP_CLIENT_IMPL asio CACHE STRING "Internal use.")
+  set(CPPREST_HTTP_LISTENER_IMPL asio CACHE STRING "Internal use.")
+elseif(UNIX AND NOT APPLE) # Note: also android
+  set(CPPREST_PPLX_IMPL linux CACHE STRING "Internal use.")
+  set(CPPREST_WEBSOCKETS_IMPL wspp CACHE STRING "Internal use.")
+  set(CPPREST_FILEIO_IMPL posix CACHE STRING "Internal use.")
+  set(CPPREST_HTTP_CLIENT_IMPL asio CACHE STRING "Internal use.")
+  set(CPPREST_HTTP_LISTENER_IMPL asio CACHE STRING "Internal use.")
+elseif(WINDOWS_PHONE OR WINDOWS_STORE)
+  set(CPPREST_PPLX_IMPL winrt CACHE STRING "Internal use.")
+  set(CPPREST_WEBSOCKETS_IMPL winrt CACHE STRING "Internal use.")
+  set(CPPREST_FILEIO_IMPL winrt CACHE STRING "Internal use.")
+  set(CPPREST_HTTP_CLIENT_IMPL winrt CACHE STRING "Internal use.")
+  set(CPPREST_HTTP_LISTENER_IMPL none CACHE STRING "Internal use.")
+elseif(WIN32)
+  set(CPPREST_PPLX_IMPL win CACHE STRING "Internal use.")
+  set(CPPREST_WEBSOCKETS_IMPL wspp CACHE STRING "Internal use.")
+  set(CPPREST_FILEIO_IMPL win32 CACHE STRING "Internal use.")
+  set(CPPREST_HTTP_CLIENT_IMPL winhttp CACHE STRING "Internal use.")
+  set(CPPREST_HTTP_LISTENER_IMPL httpsys CACHE STRING "Internal use.")
+else()
+  message(FATAL_ERROR "Unknown platform. Cannot determine appropriate feature implementations.")
+endif()
+
+set(WARNINGS)
+set(ANDROID_LIBS)
+
+# Platform (not compiler) specific settings
+if(ANDROID)
+  # These are used in the shared library case
+  set(ANDROID_LIBS atomic dl)
+elseif(UNIX) # This includes OSX
+elseif(WIN32)
+  add_definitions(-DUNICODE -D_UNICODE -DWIN32 -D_SCL_SECURE_NO_WARNINGS)
+  if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
+    add_definitions(-D_WIN32_WINNT=0x0A00)
+  else()
+    add_definitions(-D_WIN32_WINNT=0x0600)
+  endif()
+
+  if(NOT BUILD_SHARED_LIBS)
+    # This causes cmake to not link the test libraries separately, but instead hold onto their object files.
+    set(TEST_LIBRARY_TARGET_TYPE OBJECT)
+  endif()
+
+  add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS)
+else()
+  message(FATAL_ERROR "-- Unsupported Build Platform.")
+endif()
+
+# Compiler (not platform) specific settings
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR IOS)
+  message("-- Setting clang options")
+
+  if(ANDROID)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pedantic -Wno-attributes -Wno-pointer-arith")
+  elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
+    set(WARNINGS -Wall -Wextra -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls)
+    set(LINUX_SUPPRESSIONS -Wno-overloaded-virtual -Wno-sign-conversion -Wno-deprecated -Wno-unknown-pragmas -Wno-reorder -Wno-char-subscripts -Wno-switch -Wno-unused-parameter -Wno-unused-variable -Wno-deprecated -Wno-unused-value -Wno-unknown-warning-option -Wno-return-type-c-linkage -Wno-unused-function -Wno-sign-compare -Wno-shorten-64-to-32 -Wno-unused-local-typedefs)
+    set(WARNINGS ${WARNINGS} ${LINUX_SUPPRESSIONS})
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type-c-linkage -Wno-unneeded-internal-declaration")
+  else()
+    set(WARNINGS -Wall -Wextra -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls)
+    set(OSX_SUPPRESSIONS -Wno-overloaded-virtual -Wno-sign-conversion -Wno-deprecated -Wno-unknown-pragmas -Wno-reorder -Wno-char-subscripts -Wno-switch -Wno-unused-parameter -Wno-unused-variable -Wno-deprecated -Wno-unused-value -Wno-unknown-warning-option -Wno-return-type-c-linkage -Wno-unused-function -Wno-sign-compare -Wno-shorten-64-to-32 -Wno-unused-local-typedefs)
+    set(WARNINGS ${WARNINGS} ${OSX_SUPPRESSIONS})
+
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -Wno-return-type-c-linkage -Wno-unneeded-internal-declaration")
+    set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
+    set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
+  endif()
+
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing")
+
+elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+  message("-- Setting gcc options")
+
+  set(WARNINGS -Wall -Wextra -Wunused-parameter -Wcast-align -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls -Wunreachable-code)
+  set(LD_FLAGS "${LD_FLAGS} -Wl,-z,defs")
+
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing")
+  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -D_GLIBCXX_USE_SCHED_YIELD -D_GLIBCXX_USE_NANOSLEEP")
+  endif()
+
+elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
+  message("-- Setting msvc options")
+  set(WARNINGS)
+  set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4264")
+  add_compile_options(/bigobj)
+  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MP")
+  set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /MP")
+  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.")
+  message("CMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID}")
+endif()
+
+# Reconfigure final output directory
+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)
+set(Casablanca_LIBRARIES cpprest)
+get_directory_property(PARENT_DIR PARENT_DIRECTORY)
+if(NOT PARENT_DIR STREQUAL "")
+  set(Casablanca_LIBRARIES ${Casablanca_LIBRARIES} PARENT_SCOPE)
+endif()
+
+# Finally, the tests all use the same style declaration to build themselves, so we use a function
+function(add_casablanca_test NAME SOURCES_VAR)
+  add_library(${NAME} ${TEST_LIBRARY_TARGET_TYPE} ${${SOURCES_VAR}})
+  message("-- Added test library ${NAME}")
+  if(TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT")
+    foreach(_dep cpprest common_utilities unittestpp)
+      target_include_directories(${NAME} PRIVATE $<TARGET_PROPERTY:${_dep},INTERFACE_INCLUDE_DIRECTORIES>)
+      target_compile_definitions(${NAME} PRIVATE $<TARGET_PROPERTY:${_dep},INTERFACE_COMPILE_DEFINITIONS>)
+    endforeach()
+  else()
+    target_link_libraries(${NAME} PRIVATE
+      cpprest
+      common_utilities
+      unittestpp
+      ${ANDROID_LIBS}
+    )
+    if (BUILD_SHARED_LIBS)
+      add_test(NAME ${NAME}
+        WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
+        COMMAND test_runner $<TARGET_FILE_NAME:${NAME}>
+      )
+    endif()
+  endif()
+endfunction()
+
+add_subdirectory(src)
+
+if(BUILD_TESTS)
+  add_subdirectory(tests)
+endif()
+
+if(BUILD_SAMPLES)
+  add_subdirectory(samples)
+endif()
diff --git a/Release/cmake/cpprest_find_boost.cmake b/Release/cmake/cpprest_find_boost.cmake
new file mode 100644 (file)
index 0000000..3c857ba
--- /dev/null
@@ -0,0 +1,108 @@
+macro(cpprestsdk_find_boost_android_package)
+  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER)
+  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER)
+  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+  if(CMAKE_HOST_WIN32)
+    set(WIN32 1)
+    set(UNIX)
+  elseif(CMAKE_HOST_APPLE)
+    set(APPLE 1)
+    set(UNIX)
+  endif()
+  find_package(${ARGN})
+  set(APPLE)
+  set(WIN32)
+  set(UNIX 1)
+  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
+endmacro()
+
+function(cpprest_find_boost)
+  if(TARGET cpprestsdk_boost_internal)
+    return()
+  endif()
+
+  if(IOS)
+    if (EXISTS "${PROJECT_SOURCE_DIR}/../Build_iOS/boost")
+      set(IOS_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../Build_iOS")
+      set(Boost_LIBRARIES "${IOS_SOURCE_DIR}/boost/lib" CACHE INTERNAL "")
+      set(Boost_INCLUDE_DIR "${IOS_SOURCE_DIR}/boost/include" CACHE INTERNAL "")
+    else()
+      set(IOS_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../Build_iOS")
+      set(Boost_LIBRARIES "${IOS_SOURCE_DIR}/boost.framework/boost" CACHE INTERNAL "")
+      set(Boost_INCLUDE_DIR "${IOS_SOURCE_DIR}/boost.framework/Headers" CACHE INTERNAL "")
+    endif()
+  elseif(ANDROID)
+    set(Boost_COMPILER "-clang")
+    if(ANDROID_ABI STREQUAL "armeabi-v7a")
+      set(BOOST_ROOT "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/out/armeabi-v7a" CACHE INTERNAL "")
+      set(BOOST_LIBRARYDIR "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/out/armeabi-v7a/lib" CACHE INTERNAL "")
+      set(Boost_ARCHITECTURE "-a32" CACHE INTERNAL "")
+    else()
+      set(BOOST_ROOT "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/out/x86" CACHE INTERNAL "")
+      set(BOOST_LIBRARYDIR "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/out/x86/lib" CACHE INTERNAL "")
+      set(Boost_ARCHITECTURE "-x32" CACHE INTERNAL "")
+    endif()
+    cpprestsdk_find_boost_android_package(Boost ${BOOST_VERSION} EXACT REQUIRED COMPONENTS random system thread filesystem chrono atomic)
+  elseif(UNIX)
+    find_package(Boost REQUIRED COMPONENTS random system thread filesystem chrono atomic date_time regex)
+  else()
+    find_package(Boost REQUIRED COMPONENTS system date_time regex)
+  endif()
+
+  add_library(cpprestsdk_boost_internal INTERFACE)
+  # FindBoost continually breaks imported targets whenever boost updates.
+  if(1)
+    target_include_directories(cpprestsdk_boost_internal INTERFACE "$<BUILD_INTERFACE:${Boost_INCLUDE_DIR}>")
+    set(_prev)
+    set(_libs)
+    foreach(_lib ${Boost_LIBRARIES})
+      if(_lib STREQUAL "optimized" OR _lib STREQUAL "debug")
+      else()
+        if(_prev STREQUAL "optimized")
+          list(APPEND _libs "$<$<NOT:$<CONFIG:Debug>>:${_lib}>")
+        elseif(_prev STREQUAL "debug")
+          list(APPEND _libs "$<$<CONFIG:Debug>:${_lib}>")
+        else()
+          list(APPEND _libs "${_lib}")
+        endif()
+      endif()
+      set(_prev "${_lib}")
+    endforeach()
+    if (NOT IOS OR NOT EXISTS "${PROJECT_SOURCE_DIR}/../Build_iOS/boost")
+      target_link_libraries(cpprestsdk_boost_internal INTERFACE "$<BUILD_INTERFACE:${_libs}>")
+    endif()
+  else()
+    if(ANDROID)
+      target_link_libraries(cpprestsdk_boost_internal INTERFACE
+        Boost::boost
+        Boost::random
+        Boost::system
+        Boost::thread
+        Boost::filesystem
+        Boost::chrono
+        Boost::atomic
+      )
+    elseif(UNIX)
+      target_link_libraries(cpprestsdk_boost_internal INTERFACE
+        Boost::boost
+        Boost::random
+        Boost::system
+        Boost::thread
+        Boost::filesystem
+        Boost::chrono
+        Boost::atomic
+        Boost::date_time
+        Boost::regex
+      )
+    else()
+      target_link_libraries(cpprestsdk_boost_internal INTERFACE
+        Boost::boost
+        Boost::system
+        Boost::date_time
+        Boost::regex
+      )
+    endif()
+  endif()
+endfunction()
diff --git a/Release/cmake/cpprest_find_brotli.cmake b/Release/cmake/cpprest_find_brotli.cmake
new file mode 100644 (file)
index 0000000..5485d69
--- /dev/null
@@ -0,0 +1,19 @@
+function(cpprest_find_brotli)
+  if(TARGET cpprestsdk_brotli_internal)
+    return()
+  endif()
+
+
+  find_package(PkgConfig)
+  pkg_check_modules(BROTLIENC libbrotlienc)
+  pkg_check_modules(BROTLIDEC libbrotlidec)
+  if(BROTLIDEC_FOUND AND BROTLIENC_FOUND)
+         target_link_libraries(cpprest PRIVATE ${BROTLIDEC_LDFLAGS} ${BROTLIENC_LDFLAGS})
+  else(BROTLIDEC_FOUND AND BROTLIENC_FOUND)
+    find_package(unofficial-brotli REQUIRED)
+    add_library(cpprestsdk_brotli_internal INTERFACE)
+    target_link_libraries(cpprestsdk_brotli_internal INTERFACE unofficial::brotli::brotlienc unofficial::brotli::brotlidec unofficial::brotli::brotlicommon)
+    target_link_libraries(cpprest PRIVATE cpprestsdk_brotli_internal)
+  endif(BROTLIDEC_FOUND AND BROTLIENC_FOUND)
+
+endfunction()
diff --git a/Release/cmake/cpprest_find_openssl.cmake b/Release/cmake/cpprest_find_openssl.cmake
new file mode 100644 (file)
index 0000000..9333663
--- /dev/null
@@ -0,0 +1,80 @@
+function(cpprest_find_openssl)
+  if(TARGET cpprestsdk_openssl_internal)
+    return()
+  endif()
+
+  if(IOS)
+    set(IOS_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../Build_iOS")
+
+    set(OPENSSL_INCLUDE_DIR "${IOS_SOURCE_DIR}/openssl/include" CACHE INTERNAL "")
+    set(OPENSSL_LIBRARIES
+      "${IOS_SOURCE_DIR}/openssl/lib/libcrypto.a"
+      "${IOS_SOURCE_DIR}/openssl/lib/libssl.a"
+      CACHE INTERNAL ""
+      )
+    set(_SSL_LEAK_SUPPRESS_AVAILABLE ON CACHE INTERNAL "")
+  elseif(ANDROID)
+    if(ARM)
+      set(OPENSSL_INCLUDE_DIR "${CMAKE_BINARY_DIR}/../openssl/armeabi-v7a/include" CACHE INTERNAL "")
+      set(OPENSSL_LIBRARIES
+        "${CMAKE_BINARY_DIR}/../openssl/armeabi-v7a/lib/libssl.a"
+        "${CMAKE_BINARY_DIR}/../openssl/armeabi-v7a/lib/libcrypto.a"
+        CACHE INTERNAL ""
+        )
+    else()
+      set(OPENSSL_INCLUDE_DIR "${CMAKE_BINARY_DIR}/../openssl/x86/include" CACHE INTERNAL "")
+      set(OPENSSL_LIBRARIES
+        "${CMAKE_BINARY_DIR}/../openssl/x86/lib/libssl.a"
+        "${CMAKE_BINARY_DIR}/../openssl/x86/lib/libcrypto.a"
+        CACHE INTERNAL ""
+        )
+    endif()
+    set(_SSL_LEAK_SUPPRESS_AVAILABLE ON CACHE INTERNAL "")
+  else()
+    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*/*)
+        # 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()
+    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}")
+    set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}")
+    CHECK_CXX_SOURCE_COMPILES("
+      #include <openssl/ssl.h>
+      int main()
+      {
+      ::SSL_COMP_free_compression_methods();
+      }
+    " _SSL_LEAK_SUPPRESS_AVAILABLE)
+  endif()
+
+  add_library(cpprestsdk_openssl_internal INTERFACE)
+  if(TARGET OpenSSL::SSL)
+    target_link_libraries(cpprestsdk_openssl_internal INTERFACE OpenSSL::SSL)
+  else()
+    target_link_libraries(cpprestsdk_openssl_internal INTERFACE "$<BUILD_INTERFACE:${OPENSSL_LIBRARIES}>")
+    target_include_directories(cpprestsdk_openssl_internal INTERFACE "$<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>")
+  endif()
+
+  if (NOT _SSL_LEAK_SUPPRESS_AVAILABLE)
+    # 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()
diff --git a/Release/cmake/cpprest_find_websocketpp.cmake b/Release/cmake/cpprest_find_websocketpp.cmake
new file mode 100644 (file)
index 0000000..94ea81a
--- /dev/null
@@ -0,0 +1,27 @@
+function(cpprest_find_websocketpp)
+  if(TARGET cpprestsdk_websocketpp_internal)
+    return()
+  endif()
+
+  find_package(WEBSOCKETPP CONFIG QUIET)
+  if(WEBSOCKETPP_FOUND)
+    message("-- Found websocketpp version " ${WEBSOCKETPP_VERSION} " on system")
+    set(WEBSOCKETPP_INCLUDE_DIR ${WEBSOCKETPP_INCLUDE_DIR} CACHE INTERNAL "")
+  elseif(EXISTS ${PROJECT_SOURCE_DIR}/libs/websocketpp/CMakeLists.txt)
+    message("-- websocketpp not found, using the embedded version")
+    set(WEBSOCKETPP_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/libs/websocketpp CACHE INTERNAL "")
+  else()
+    message(FATAL_ERROR "-- websocketpp not found and embedded version not present; try `git submodule update --init` and run CMake again")
+  endif()
+
+  cpprest_find_boost()
+  cpprest_find_openssl()
+
+  add_library(cpprestsdk_websocketpp_internal INTERFACE)
+  target_include_directories(cpprestsdk_websocketpp_internal INTERFACE "$<BUILD_INTERFACE:${WEBSOCKETPP_INCLUDE_DIR}>")
+  target_link_libraries(cpprestsdk_websocketpp_internal
+    INTERFACE
+      cpprestsdk_boost_internal
+      cpprestsdk_openssl_internal
+  )
+endfunction()
diff --git a/Release/cmake/cpprest_find_winhttppal.cmake b/Release/cmake/cpprest_find_winhttppal.cmake
new file mode 100644 (file)
index 0000000..9a6840f
--- /dev/null
@@ -0,0 +1,17 @@
+function(cpprest_find_winhttppal)
+  if(TARGET cpprestsdk_winhttppal_internal)
+    return()
+  endif()
+
+  if(NOT WINHTTPPAL_LIBRARY OR NOT WINHTTPPAL_INCLUDE_DIRS)
+    find_package(winhttppal REQUIRED)
+  endif()
+
+  add_library(cpprestsdk_winhttppal_internal INTERFACE)
+  if(TARGET winhttppal::winhttppal)
+    target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE winhttppal::winhttppal)
+  else()
+    target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE "$<BUILD_INTERFACE:${WINHTTPPAL_LIBRARY}>")
+    target_include_directories(cpprestsdk_winhttppal_internal INTERFACE "$<BUILD_INTERFACE:${WINHTTPPAL_INCLUDE_DIRS}>")
+  endif()
+endfunction()
diff --git a/Release/cmake/cpprest_find_zlib.cmake b/Release/cmake/cpprest_find_zlib.cmake
new file mode 100644 (file)
index 0000000..99dde12
--- /dev/null
@@ -0,0 +1,25 @@
+function(cpprest_find_zlib)
+  if(TARGET cpprestsdk_zlib_internal)
+    return()
+  endif()
+
+  if(APPLE AND NOT IOS)
+    # Prefer the homebrew version of zlib
+    find_library(ZLIB_LIBRARY NAMES libz.a PATHS /usr/local/Cellar/zlib/1.2.8/lib NO_DEFAULT_PATH)
+    find_path(ZLIB_INCLUDE_DIRS NAMES zlib.h PATHS /usr/local/Cellar/zlib/1.2.8/include NO_DEFAULT_PATH)
+
+    if(NOT ZLIB_LIBRARY OR NOT ZLIB_INCLUDE_DIRS)
+      find_package(ZLIB REQUIRED)
+    endif()
+  else()
+    find_package(ZLIB REQUIRED)
+  endif()
+
+  add_library(cpprestsdk_zlib_internal INTERFACE)
+  if(TARGET ZLIB::ZLIB)
+    target_link_libraries(cpprestsdk_zlib_internal INTERFACE ZLIB::ZLIB)
+  else()
+    target_link_libraries(cpprestsdk_zlib_internal INTERFACE "$<BUILD_INTERFACE:${ZLIB_LIBRARY}>")
+    target_include_directories(cpprestsdk_zlib_internal INTERFACE "$<BUILD_INTERFACE:${ZLIB_INCLUDE_DIRS}>")
+  endif()
+endfunction()
diff --git a/Release/cmake/cpprestsdk-config-version.in.cmake b/Release/cmake/cpprestsdk-config-version.in.cmake
new file mode 100644 (file)
index 0000000..017879c
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..72476b0
--- /dev/null
@@ -0,0 +1,25 @@
+include(CMakeFindDependencyMacro)
+if(@CPPREST_USES_ZLIB@)
+  find_dependency(ZLIB)
+endif()
+
+if(@CPPREST_USES_BROTLI@)
+  find_dependency(unofficial-brotli)
+endif()
+
+if(@CPPREST_USES_OPENSSL@)
+  find_dependency(OpenSSL)
+endif()
+
+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()
+    find_dependency(Boost COMPONENTS system date_time regex)
+  endif()
+endif()
+include("${CMAKE_CURRENT_LIST_DIR}/cpprestsdk-targets.cmake")
diff --git a/Release/include/cpprest/astreambuf.h b/Release/include/cpprest/astreambuf.h
new file mode 100644 (file)
index 0000000..1dcb285
--- /dev/null
@@ -0,0 +1,1180 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Asynchronous I/O: stream buffer. This is an extension to the PPL concurrency features and therefore
+ * lives in the Concurrency namespace.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/details/basic_types.h"
+#include "pplx/pplxtasks.h"
+#include <atomic>
+#include <cstring>
+#include <ios>
+#include <math.h>
+#include <memory>
+
+#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
+namespace Concurrency // since namespace pplx = Concurrency
+#else
+namespace pplx
+#endif
+{
+namespace details
+{
+template<class F, class T = bool>
+pplx::task<T> _do_while(F func)
+{
+    pplx::task<T> first = func();
+    return first.then([=](bool guard) -> pplx::task<T> {
+        if (guard)
+            return pplx::details::_do_while<F, T>(func);
+        else
+            return first;
+    });
+}
+} // namespace details
+}
+
+namespace Concurrency
+{
+/// Library for asynchronous streams.
+namespace streams
+{
+/// <summary>
+/// Extending the standard char_traits type with one that adds values and types
+/// that are unique to "C++ REST SDK" streams.
+/// </summary>
+/// <typeparam name="_CharType">
+/// The data type of the basic element of the stream.
+/// </typeparam>
+template<typename _CharType>
+struct char_traits : std::char_traits<_CharType>
+{
+    /// <summary>
+    /// Some synchronous functions will return this value if the operation
+    /// requires an asynchronous call in a given situation.
+    /// </summary>
+    /// <returns>An <c>int_type</c> value which implies that an asynchronous call is required.</returns>
+    static typename std::char_traits<_CharType>::int_type requires_async()
+    {
+        return std::char_traits<_CharType>::eof() - 1;
+    }
+};
+#if !defined(_WIN32)
+template<>
+struct char_traits<unsigned char> : private std::char_traits<char>
+{
+public:
+    typedef unsigned char char_type;
+
+    using std::char_traits<char>::eof;
+    using std::char_traits<char>::int_type;
+    using std::char_traits<char>::off_type;
+    using std::char_traits<char>::pos_type;
+
+    static size_t length(const unsigned char* str)
+    {
+        return std::char_traits<char>::length(reinterpret_cast<const char*>(str));
+    }
+
+    static void assign(unsigned char& left, const unsigned char& right) { left = right; }
+    static unsigned char* assign(unsigned char* left, size_t n, unsigned char value)
+    {
+        return reinterpret_cast<unsigned char*>(
+            std::char_traits<char>::assign(reinterpret_cast<char*>(left), n, static_cast<char>(value)));
+    }
+
+    static unsigned char* copy(unsigned char* left, const unsigned char* right, size_t n)
+    {
+        return reinterpret_cast<unsigned char*>(
+            std::char_traits<char>::copy(reinterpret_cast<char*>(left), reinterpret_cast<const char*>(right), n));
+    }
+
+    static unsigned char* move(unsigned char* left, const unsigned char* right, size_t n)
+    {
+        return reinterpret_cast<unsigned char*>(
+            std::char_traits<char>::move(reinterpret_cast<char*>(left), reinterpret_cast<const char*>(right), n));
+    }
+
+    static int_type requires_async() { return eof() - 1; }
+};
+#endif
+
+namespace details
+{
+/// <summary>
+/// Stream buffer base class.
+/// </summary>
+template<typename _CharType>
+class basic_streambuf
+{
+public:
+    typedef _CharType char_type;
+    typedef ::concurrency::streams::char_traits<_CharType> traits;
+
+    typedef typename traits::int_type int_type;
+    typedef typename traits::pos_type pos_type;
+    typedef typename traits::off_type off_type;
+
+    /// <summary>
+    /// Virtual constructor for stream buffers.
+    /// </summary>
+    virtual ~basic_streambuf() {}
+
+    /// <summary>
+    /// <c>can_read</c> is used to determine whether a stream buffer will support read operations (get).
+    /// </summary>
+    virtual bool can_read() const = 0;
+
+    /// <summary>
+    /// <c>can_write</c> is used to determine whether a stream buffer will support write operations (put).
+    /// </summary>
+    virtual bool can_write() const = 0;
+
+    /// <summary>
+    /// <c>can_seek<c/> is used to determine whether a stream buffer supports seeking.
+    /// </summary>
+    virtual bool can_seek() const = 0;
+
+    /// <summary>
+    /// <c>has_size<c/> is used to determine whether a stream buffer supports size().
+    /// </summary>
+    virtual bool has_size() const = 0;
+
+    /// <summary>
+    /// <c>is_eof</c> is used to determine whether a read head has reached the end of the buffer.
+    /// </summary>
+    virtual bool is_eof() const = 0;
+
+    /// <summary>
+    /// Gets the stream buffer size, if one has been set.
+    /// </summary>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <returns>The size of the internal buffer (for the given direction).</returns>
+    /// <remarks>An implementation that does not support buffering will always return 0.</remarks>
+    virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const = 0;
+
+    /// <summary>
+    /// Sets the stream buffer implementation to buffer or not buffer.
+    /// </summary>
+    /// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
+    /// will not have any effect on what is returned by subsequent calls to <see cref="::buffer_size method"
+    /// />.</remarks>
+    virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in) = 0;
+
+    /// <summary>
+    /// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
+    /// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
+    /// incurring the overhead of using tasks.
+    /// </summary>
+    virtual size_t in_avail() const = 0;
+
+    /// <summary>
+    /// Checks if the stream buffer is open.
+    /// </summary>
+    /// <remarks>No separation is made between open for reading and open for writing.</remarks>
+    virtual bool is_open() const = 0;
+
+    /// <summary>
+    /// Closes the stream buffer, preventing further read or write operations.
+    /// </summary>
+    /// <param name="mode">The I/O mode (in or out) to close for.</param>
+    virtual pplx::task<void> close(std::ios_base::openmode mode = (std::ios_base::in | std::ios_base::out)) = 0;
+
+    /// <summary>
+    /// Closes the stream buffer with an exception.
+    /// </summary>
+    /// <param name="mode">The I/O mode (in or out) to close for.</param>
+    /// <param name="eptr">Pointer to the exception.</param>
+    virtual pplx::task<void> close(std::ios_base::openmode mode, std::exception_ptr eptr) = 0;
+
+    /// <summary>
+    /// Writes a single character to the stream.
+    /// </summary>
+    /// <param name="ch">The character to write</param>
+    /// <returns>A <c>task</c> that holds the value of the character. This value is EOF if the write operation
+    /// fails.</returns>
+    virtual pplx::task<int_type> putc(_CharType ch) = 0;
+
+    /// <summary>
+    /// Writes a number of characters to the stream.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be written.</param>
+    /// <param name="count">The number of characters to write.</param>
+    /// <returns>A <c>task</c> that holds the number of characters actually written, either 'count' or 0.</returns>
+    virtual pplx::task<size_t> putn(const _CharType* ptr, size_t count) = 0;
+
+    /// <summary>
+    /// Writes a number of characters to the stream. Note: callers must make sure the data to be written is valid until
+    /// the returned task completes.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be written.</param>
+    /// <param name="count">The number of characters to write.</param>
+    /// <returns>A <c>task</c> that holds the number of characters actually written, either 'count' or 0.</returns>
+    virtual pplx::task<size_t> putn_nocopy(const _CharType* ptr, size_t count) = 0;
+
+    /// <summary>
+    /// Reads a single character from the stream and advances the read position.
+    /// </summary>
+    /// <returns>A <c>task</c> that holds the value of the character. This value is EOF if the read fails.</returns>
+    virtual pplx::task<int_type> bumpc() = 0;
+
+    /// <summary>
+    /// Reads a single character from the stream and advances the read position.
+    /// </summary>
+    /// <returns>The value of the character. <c>-1</c> if the read fails. <c>-2</c> if an asynchronous read is
+    /// required</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
+    virtual int_type sbumpc() = 0;
+
+    /// <summary>
+    /// Reads a single character from the stream without advancing the read position.
+    /// </summary>
+    /// <returns>A <c>task</c> that holds the value of the byte. This value is EOF if the read fails.</returns>
+    virtual pplx::task<int_type> getc() = 0;
+
+    /// <summary>
+    /// Reads a single character from the stream without advancing the read position.
+    /// </summary>
+    /// <returns>The value of the character. EOF if the read fails. <see cref="::requires_async method" /> if an
+    /// asynchronous read is required</returns> <remarks>This is a synchronous operation, but is guaranteed to never
+    /// block.</remarks>
+    virtual int_type sgetc() = 0;
+
+    /// <summary>
+    /// Advances the read position, then returns the next character without advancing again.
+    /// </summary>
+    /// <returns>A <c>task</c> that holds the value of the character. This value is EOF if the read fails.</returns>
+    virtual pplx::task<int_type> nextc() = 0;
+
+    /// <summary>
+    /// Retreats the read position, then returns the current character without advancing.
+    /// </summary>
+    /// <returns>A <c>task</c> that holds the value of the character. This value is EOF if the read fails,
+    /// <c>requires_async</c> if an asynchronous read is required</returns>
+    virtual pplx::task<int_type> ungetc() = 0;
+
+    /// <summary>
+    /// Reads up to a given number of characters from the stream.
+    /// </summary>
+    /// <param name="ptr">The address of the target memory area.</param>
+    /// <param name="count">The maximum number of characters to read.</param>
+    /// <returns>A <c>task</c> that holds the number of characters read. This value is O if the end of the stream is
+    /// reached.</returns>
+    virtual pplx::task<size_t> getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0;
+
+    /// <summary>
+    /// Copies up to a given number of characters from the stream, synchronously.
+    /// </summary>
+    /// <param name="ptr">The address of the target memory area.</param>
+    /// <param name="count">The maximum number of characters to read.</param>
+    /// <returns>The number of characters copied. O if the end of the stream is reached or an asynchronous read is
+    /// required.</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
+    virtual size_t scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0;
+
+    /// <summary>
+    /// Gets the current read or write position in the stream.
+    /// </summary>
+    /// <param name="direction">The I/O direction to seek (see remarks)</param>
+    /// <returns>The current position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the direction parameter defines whether to move the read or the write
+    ///          cursor.</remarks>
+    virtual pos_type getpos(std::ios_base::openmode direction) const = 0;
+
+    /// <summary>
+    /// Gets the size of the stream, if known. Calls to <c>has_size</c> will determine whether
+    /// the result of <c>size</c> can be relied on.
+    /// </summary>
+    virtual utility::size64_t size() const = 0;
+
+    /// <summary>
+    /// Seeks to the given position.
+    /// </summary>
+    /// <param name="pos">The offset from the beginning of the stream.</param>
+    /// <param name="direction">The I/O direction to seek (see remarks).</param>
+    /// <returns>The position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors. For such streams, the direction parameter
+    /// defines whether to move the read or the write cursor.</remarks>
+    virtual pos_type seekpos(pos_type pos, std::ios_base::openmode direction) = 0;
+
+    /// <summary>
+    /// Seeks to a position given by a relative offset.
+    /// </summary>
+    /// <param name="offset">The relative position to seek to</param>
+    /// <param name="way">The starting point (beginning, end, current) for the seek.</param>
+    /// <param name="mode">The I/O direction to seek (see remarks)</param>
+    /// <returns>The position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
+    virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) = 0;
+
+    /// <summary>
+    /// For output streams, flush any internally buffered data to the underlying medium.
+    /// </summary>
+    /// <returns>A <c>task</c> that returns <c>true</c> if the sync succeeds, <c>false</c> if not.</returns>
+    virtual pplx::task<void> sync() = 0;
+
+    //
+    // Efficient read and write.
+    //
+    // The following routines are intended to be used for more efficient, copy-free, reading and
+    // writing of data from/to the stream. Rather than having the caller provide a buffer into which
+    // data is written or from which it is read, the stream buffer provides a pointer directly to the
+    // internal data blocks that it is using. Since not all stream buffers use internal data structures
+    // to copy data, the functions may not be supported by all. An application that wishes to use this
+    // functionality should therefore first try them and check for failure to support. If there is
+    // such failure, the application should fall back on the copying interfaces (putn / getn)
+    //
+
+    /// <summary>
+    /// Allocates a contiguous memory block and returns it.
+    /// </summary>
+    /// <param name="count">The number of characters to allocate.</param>
+    /// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
+    /// alloc/commit.</returns>
+    virtual _CharType* alloc(_In_ size_t count) = 0;
+
+    /// <summary>
+    /// Submits a block already allocated by the stream buffer.
+    /// </summary>
+    /// <param name="count">The number of characters to be committed.</param>
+    virtual void commit(_In_ size_t count) = 0;
+
+    /// <summary>
+    /// Gets a pointer to the next already allocated contiguous block of data.
+    /// </summary>
+    /// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
+    /// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
+    /// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
+    /// <remarks>
+    /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
+    /// there is no block to return immediately or that the stream buffer does not support the operation.
+    /// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
+    /// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
+    /// a subsequent read will not succeed.
+    /// </remarks>
+    virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) = 0;
+
+    /// <summary>
+    /// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
+    /// de-allocate the memory, if it so desires. Move the read position ahead by the count.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be released.</param>
+    /// <param name="count">The number of characters that were read.</param>
+    virtual void release(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0;
+
+    /// <summary>
+    /// Retrieves the stream buffer exception_ptr if it has been set.
+    /// </summary>
+    /// <returns>Pointer to the exception, if it has been set; otherwise, <c>nullptr</c> will be returned</returns>
+    virtual std::exception_ptr exception() const = 0;
+};
+
+template<typename _CharType>
+class streambuf_state_manager : public basic_streambuf<_CharType>,
+                                public std::enable_shared_from_this<streambuf_state_manager<_CharType>>
+{
+public:
+    typedef typename details::basic_streambuf<_CharType>::traits traits;
+    typedef typename details::basic_streambuf<_CharType>::int_type int_type;
+    typedef typename details::basic_streambuf<_CharType>::pos_type pos_type;
+    typedef typename details::basic_streambuf<_CharType>::off_type off_type;
+
+    /// <summary>
+    /// <c>can_read</c> is used to determine whether a stream buffer will support read operations (get).
+    /// </summary>
+    virtual bool can_read() const { return m_stream_can_read; }
+
+    /// <summary>
+    /// <c>can_write</c> is used to determine whether a stream buffer will support write operations (put).
+    /// </summary>
+    virtual bool can_write() const { return m_stream_can_write; }
+
+    /// <summary>
+    /// Checks if the stream buffer is open.
+    /// </summary>
+    /// <remarks>No separation is made between open for reading and open for writing.</remarks>
+    virtual bool is_open() const { return can_read() || can_write(); }
+
+    /// <summary>
+    /// Closes the stream buffer, preventing further read or write operations.
+    /// </summary>
+    /// <param name="mode">The I/O mode (in or out) to close for.</param>
+    virtual pplx::task<void> close(std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
+    {
+        pplx::task<void> closeOp = pplx::task_from_result();
+
+        if (mode & std::ios_base::in && can_read())
+        {
+            closeOp = _close_read();
+        }
+
+        // After the flush_internal task completed, "this" object may have been destroyed,
+        // accessing the members is invalid, use shared_from_this to avoid access violation exception.
+        auto this_ptr = std::static_pointer_cast<streambuf_state_manager>(this->shared_from_this());
+
+        if (mode & std::ios_base::out && can_write())
+        {
+            if (closeOp.is_done())
+                closeOp = closeOp && _close_write().then([this_ptr] {}); // passing down exceptions from closeOp
+            else
+                closeOp = closeOp.then([this_ptr] { return this_ptr->_close_write().then([this_ptr] {}); });
+        }
+
+        return closeOp;
+    }
+
+    /// <summary>
+    /// Closes the stream buffer with an exception.
+    /// </summary>
+    /// <param name="mode">The I/O mode (in or out) to close for.</param>
+    /// <param name="eptr">Pointer to the exception.</param>
+    virtual pplx::task<void> close(std::ios_base::openmode mode, std::exception_ptr eptr)
+    {
+        if (m_currentException == nullptr) m_currentException = eptr;
+        return close(mode);
+    }
+
+    /// <summary>
+    /// <c>is_eof</c> is used to determine whether a read head has reached the end of the buffer.
+    /// </summary>
+    virtual bool is_eof() const { return m_stream_read_eof; }
+
+    /// <summary>
+    /// Writes a single character to the stream.
+    /// </summary>
+    /// <param name="ch">The character to write</param>
+    /// <returns>The value of the character. EOF if the write operation fails</returns>
+    virtual pplx::task<int_type> putc(_CharType ch)
+    {
+        if (!can_write()) return create_exception_checked_value_task<int_type>(traits::eof());
+
+        return create_exception_checked_task<int_type>(_putc(ch), [](int_type) {
+            return false; // no EOF for write
+        });
+    }
+
+    /// <summary>
+    /// Writes a number of characters to the stream.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be written.</param>
+    /// <param name="count">The number of characters to write.</param>
+    /// <returns>The number of characters actually written, either 'count' or 0.</returns>
+    CASABLANCA_DEPRECATED("This API in some cases performs a copy. It is deprecated and will be removed in a future "
+                          "release. Use putn_nocopy instead.")
+    virtual pplx::task<size_t> putn(const _CharType* ptr, size_t count)
+    {
+        if (!can_write()) return create_exception_checked_value_task<size_t>(0);
+        if (count == 0) return pplx::task_from_result<size_t>(0);
+
+        return create_exception_checked_task<size_t>(_putn(ptr, count, true), [](size_t) {
+            return false; // no EOF for write
+        });
+    }
+
+    /// <summary>
+    /// Writes a number of characters to the stream. Note: callers must make sure the data to be written is valid until
+    /// the returned task completes.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be written.</param>
+    /// <param name="count">The number of characters to write.</param>
+    /// <returns>A <c>task</c> that holds the number of characters actually written, either 'count' or 0.</returns>
+    virtual pplx::task<size_t> putn_nocopy(const _CharType* ptr, size_t count)
+    {
+        if (!can_write()) return create_exception_checked_value_task<size_t>(0);
+        if (count == 0) return pplx::task_from_result<size_t>(0);
+
+        return create_exception_checked_task<size_t>(_putn(ptr, count), [](size_t) {
+            return false; // no EOF for write
+        });
+    }
+
+    /// <summary>
+    /// Reads a single character from the stream and advances the read position.
+    /// </summary>
+    /// <returns>The value of the character. EOF if the read fails.</returns>
+    virtual pplx::task<int_type> bumpc()
+    {
+        if (!can_read())
+            return create_exception_checked_value_task<int_type>(streambuf_state_manager<_CharType>::traits::eof());
+
+        return create_exception_checked_task<int_type>(
+            _bumpc(), [](int_type val) { return val == streambuf_state_manager<_CharType>::traits::eof(); });
+    }
+
+    /// <summary>
+    /// Reads a single character from the stream and advances the read position.
+    /// </summary>
+    /// <returns>The value of the character. <c>-1</c> if the read fails. <c>-2</c> if an asynchronous read is
+    /// required</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
+    virtual int_type sbumpc()
+    {
+        if (!(m_currentException == nullptr)) std::rethrow_exception(m_currentException);
+        if (!can_read()) return traits::eof();
+        return check_sync_read_eof(_sbumpc());
+    }
+
+    /// <summary>
+    /// Reads a single character from the stream without advancing the read position.
+    /// </summary>
+    /// <returns>The value of the byte. EOF if the read fails.</returns>
+    virtual pplx::task<int_type> getc()
+    {
+        if (!can_read()) return create_exception_checked_value_task<int_type>(traits::eof());
+
+        return create_exception_checked_task<int_type>(
+            _getc(), [](int_type val) { return val == streambuf_state_manager<_CharType>::traits::eof(); });
+    }
+
+    /// <summary>
+    /// Reads a single character from the stream without advancing the read position.
+    /// </summary>
+    /// <returns>The value of the character. EOF if the read fails. <see cref="::requires_async method" /> if an
+    /// asynchronous read is required</returns> <remarks>This is a synchronous operation, but is guaranteed to never
+    /// block.</remarks>
+    virtual int_type sgetc()
+    {
+        if (!(m_currentException == nullptr)) std::rethrow_exception(m_currentException);
+        if (!can_read()) return traits::eof();
+        return check_sync_read_eof(_sgetc());
+    }
+
+    /// <summary>
+    /// Advances the read position, then returns the next character without advancing again.
+    /// </summary>
+    /// <returns>The value of the character. EOF if the read fails.</returns>
+    virtual pplx::task<int_type> nextc()
+    {
+        if (!can_read()) return create_exception_checked_value_task<int_type>(traits::eof());
+
+        return create_exception_checked_task<int_type>(
+            _nextc(), [](int_type val) { return val == streambuf_state_manager<_CharType>::traits::eof(); });
+    }
+
+    /// <summary>
+    /// Retreats the read position, then returns the current character without advancing.
+    /// </summary>
+    /// <returns>The value of the character. EOF if the read fails. <see cref="::requires_async method" /> if an
+    /// asynchronous read is required</returns>
+    virtual pplx::task<int_type> ungetc()
+    {
+        if (!can_read()) return create_exception_checked_value_task<int_type>(traits::eof());
+
+        return create_exception_checked_task<int_type>(_ungetc(), [](int_type) { return false; });
+    }
+
+    /// <summary>
+    /// Reads up to a given number of characters from the stream.
+    /// </summary>
+    /// <param name="ptr">The address of the target memory area.</param>
+    /// <param name="count">The maximum number of characters to read.</param>
+    /// <returns>The number of characters read. O if the end of the stream is reached.</returns>
+    virtual pplx::task<size_t> getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        if (!can_read()) return create_exception_checked_value_task<size_t>(0);
+        if (count == 0) return pplx::task_from_result<size_t>(0);
+
+        return create_exception_checked_task<size_t>(_getn(ptr, count), [](size_t val) { return val == 0; });
+    }
+
+    /// <summary>
+    /// Copies up to a given number of characters from the stream, synchronously.
+    /// </summary>
+    /// <param name="ptr">The address of the target memory area.</param>
+    /// <param name="count">The maximum number of characters to read.</param>
+    /// <returns>The number of characters copied. O if the end of the stream is reached or an asynchronous read is
+    /// required.</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
+    virtual size_t scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        if (!(m_currentException == nullptr)) std::rethrow_exception(m_currentException);
+        if (!can_read()) return 0;
+
+        return _scopy(ptr, count);
+    }
+
+    /// <summary>
+    /// For output streams, flush any internally buffered data to the underlying medium.
+    /// </summary>
+    /// <returns><c>true</c> if the flush succeeds, <c>false</c> if not</returns>
+    virtual pplx::task<void> sync()
+    {
+        if (!can_write())
+        {
+            if (m_currentException == nullptr)
+                return pplx::task_from_result();
+            else
+                return pplx::task_from_exception<void>(m_currentException);
+        }
+        return create_exception_checked_task<bool>(_sync(), [](bool) { return false; }).then([](bool) {});
+    }
+
+    /// <summary>
+    /// Retrieves the stream buffer exception_ptr if it has been set.
+    /// </summary>
+    /// <returns>Pointer to the exception, if it has been set; otherwise, <c>nullptr</c> will be returned.</returns>
+    virtual std::exception_ptr exception() const { return m_currentException; }
+
+    /// <summary>
+    /// Allocates a contiguous memory block and returns it.
+    /// </summary>
+    /// <param name="count">The number of characters to allocate.</param>
+    /// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
+    /// alloc/commit.</returns> <remarks>This is intended as an advanced API to be used only when it is important to
+    /// avoid extra copies.</remarks>
+    _CharType* alloc(size_t count)
+    {
+        if (m_alloced)
+            throw std::logic_error(
+                "The buffer is already allocated, this maybe caused by overlap of stream read or write");
+
+        _CharType* alloc_result = _alloc(count);
+
+        if (alloc_result) m_alloced = true;
+
+        return alloc_result;
+    }
+
+    /// <summary>
+    /// Submits a block already allocated by the stream buffer.
+    /// </summary>
+    /// <param name="count">The number of characters to be committed.</param>
+    /// <remarks>This is intended as an advanced API to be used only when it is important to avoid extra
+    /// copies.</remarks>
+    void commit(size_t count)
+    {
+        if (!m_alloced) throw std::logic_error("The buffer needs to allocate first");
+
+        _commit(count);
+        m_alloced = false;
+    }
+
+public:
+    virtual bool can_seek() const = 0;
+    virtual bool has_size() const = 0;
+    virtual utility::size64_t size() const { return 0; }
+    virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const = 0;
+    virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in) = 0;
+    virtual size_t in_avail() const = 0;
+    virtual pos_type getpos(std::ios_base::openmode direction) const = 0;
+    virtual pos_type seekpos(pos_type pos, std::ios_base::openmode direction) = 0;
+    virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) = 0;
+    virtual bool acquire(_Out_writes_(count) _CharType*& ptr, _In_ size_t& count) = 0;
+    virtual void release(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0;
+
+protected:
+    virtual pplx::task<int_type> _putc(_CharType ch) = 0;
+
+    // This API is only needed for file streams and until we remove the deprecated stream buffer putn overload.
+    virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count, bool)
+    {
+        // Default to no copy, only the file streams API overloads and performs a copy.
+        return _putn(ptr, count);
+    }
+    virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count) = 0;
+
+    virtual pplx::task<int_type> _bumpc() = 0;
+    virtual int_type _sbumpc() = 0;
+    virtual pplx::task<int_type> _getc() = 0;
+    virtual int_type _sgetc() = 0;
+    virtual pplx::task<int_type> _nextc() = 0;
+    virtual pplx::task<int_type> _ungetc() = 0;
+    virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0;
+    virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0;
+    virtual pplx::task<bool> _sync() = 0;
+    virtual _CharType* _alloc(size_t count) = 0;
+    virtual void _commit(size_t count) = 0;
+
+    /// <summary>
+    /// The real read head close operation, implementation should override it if there is any resource to be released.
+    /// </summary>
+    virtual pplx::task<void> _close_read()
+    {
+        m_stream_can_read = false;
+        return pplx::task_from_result();
+    }
+
+    /// <summary>
+    /// The real write head close operation, implementation should override it if there is any resource to be released.
+    /// </summary>
+    virtual pplx::task<void> _close_write()
+    {
+        m_stream_can_write = false;
+        return pplx::task_from_result();
+    }
+
+protected:
+    streambuf_state_manager(std::ios_base::openmode mode)
+    {
+        m_stream_can_read = (mode & std::ios_base::in) != 0;
+        m_stream_can_write = (mode & std::ios_base::out) != 0;
+        m_stream_read_eof = false;
+        m_alloced = false;
+    }
+
+    std::exception_ptr m_currentException;
+    // The in/out mode for the buffer
+    std::atomic<bool> m_stream_can_read;
+    std::atomic<bool> m_stream_can_write;
+    std::atomic<bool> m_stream_read_eof;
+    std::atomic<bool> m_alloced;
+
+private:
+    template<typename _CharType1>
+    pplx::task<_CharType1> create_exception_checked_value_task(const _CharType1& val) const
+    {
+        if (this->exception() == nullptr)
+            return pplx::task_from_result<_CharType1>(static_cast<_CharType1>(val));
+        else
+            return pplx::task_from_exception<_CharType1>(this->exception());
+    }
+
+    // Set exception and eof states for async read
+    template<typename _CharType1>
+    pplx::task<_CharType1> create_exception_checked_task(pplx::task<_CharType1> result,
+                                                         std::function<bool(_CharType1)> eof_test,
+                                                         std::ios_base::openmode mode = std::ios_base::in |
+                                                                                        std::ios_base::out)
+    {
+        auto thisPointer = this->shared_from_this();
+
+        auto func1 = [=](pplx::task<_CharType1> t1) -> pplx::task<_CharType1> {
+            try
+            {
+                thisPointer->m_stream_read_eof = eof_test(t1.get());
+            }
+            catch (...)
+            {
+                thisPointer->close(mode, std::current_exception()).get();
+                return pplx::task_from_exception<_CharType1>(thisPointer->exception(), pplx::task_options());
+            }
+            if (thisPointer->m_stream_read_eof && !(thisPointer->exception() == nullptr))
+                return pplx::task_from_exception<_CharType1>(thisPointer->exception(), pplx::task_options());
+            return t1;
+        };
+
+        if (result.is_done())
+        {
+            // If the data is already available, we should avoid scheduling a continuation, so we do it inline.
+            return func1(result);
+        }
+        else
+        {
+            return result.then(func1);
+        }
+    }
+
+    // Set eof states for sync read
+    int_type check_sync_read_eof(int_type ch)
+    {
+        m_stream_read_eof = ch == traits::eof();
+        return ch;
+    }
+};
+
+} // namespace details
+
+// Forward declarations
+template<typename _CharType>
+class basic_istream;
+template<typename _CharType>
+class basic_ostream;
+
+/// <summary>
+/// Reference-counted stream buffer.
+/// </summary>
+/// <typeparam name="_CharType">
+/// The data type of the basic element of the <c>streambuf.</c>
+/// </typeparam>
+/// <typeparam name="_CharType2">
+/// The data type of the basic element of the <c>streambuf.</c>
+/// </typeparam>
+template<typename _CharType>
+class streambuf : public details::basic_streambuf<_CharType>
+{
+public:
+    typedef typename details::basic_streambuf<_CharType>::traits traits;
+    typedef typename details::basic_streambuf<_CharType>::int_type int_type;
+    typedef typename details::basic_streambuf<_CharType>::pos_type pos_type;
+    typedef typename details::basic_streambuf<_CharType>::off_type off_type;
+    typedef typename details::basic_streambuf<_CharType>::char_type char_type;
+
+    template<typename _CharType2>
+    friend class streambuf;
+
+    /// <summary>
+    /// Constructor.
+    /// </summary>
+    /// <param name="ptr">A pointer to the concrete stream buffer implementation.</param>
+    streambuf(_In_ const std::shared_ptr<details::basic_streambuf<_CharType>>& ptr) : m_buffer(ptr) {}
+
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    streambuf() {}
+
+    /// <summary>
+    /// Converter Constructor.
+    /// </summary>
+    /// <typeparam name="AlterCharType">
+    /// The data type of the basic element of the source <c>streambuf</c>.
+    /// </typeparam>
+    /// <param name="other">The source buffer to be converted.</param>
+    template<typename AlterCharType>
+    streambuf(const streambuf<AlterCharType>& other)
+        : m_buffer(std::static_pointer_cast<details::basic_streambuf<_CharType>>(
+              std::static_pointer_cast<void>(other.m_buffer)))
+    {
+        static_assert(std::is_same<pos_type, typename details::basic_streambuf<AlterCharType>::pos_type>::value &&
+                          std::is_same<off_type, typename details::basic_streambuf<AlterCharType>::off_type>::value &&
+                          std::is_integral<_CharType>::value && std::is_integral<AlterCharType>::value &&
+                          std::is_integral<int_type>::value &&
+                          std::is_integral<typename details::basic_streambuf<AlterCharType>::int_type>::value &&
+                          sizeof(_CharType) == sizeof(AlterCharType) &&
+                          sizeof(int_type) == sizeof(typename details::basic_streambuf<AlterCharType>::int_type),
+                      "incompatible stream character types");
+    }
+
+    /// <summary>
+    /// Constructs an input stream head for this stream buffer.
+    /// </summary>
+    /// <returns><c>basic_istream</c>.</returns>
+    concurrency::streams::basic_istream<_CharType> create_istream() const
+    {
+        if (!can_read()) throw std::runtime_error("stream buffer not set up for input of data");
+        return concurrency::streams::basic_istream<_CharType>(*this);
+    }
+
+    /// <summary>
+    /// Constructs an output stream for this stream buffer.
+    /// </summary>
+    /// <returns>basic_ostream</returns>
+    concurrency::streams::basic_ostream<_CharType> create_ostream() const
+    {
+        if (!can_write()) throw std::runtime_error("stream buffer not set up for output of data");
+        return concurrency::streams::basic_ostream<_CharType>(*this);
+    }
+
+    /// <summary>
+    /// Checks if the stream buffer has been initialized or not.
+    /// </summary>
+    operator bool() const { return (bool)m_buffer; }
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    virtual ~streambuf() {}
+
+    const std::shared_ptr<details::basic_streambuf<_CharType>>& get_base() const
+    {
+        if (!m_buffer)
+        {
+            throw std::invalid_argument("Invalid streambuf object");
+        }
+
+        return m_buffer;
+    }
+
+    /// <summary>
+    /// <c>can_read</c> is used to determine whether a stream buffer will support read operations (get).
+    /// </summary>
+    virtual bool can_read() const { return get_base()->can_read(); }
+
+    /// <summary>
+    /// <c>can_write</c> is used to determine whether a stream buffer will support write operations (put).
+    /// </summary>
+    virtual bool can_write() const { return get_base()->can_write(); }
+
+    /// <summary>
+    /// <c>can_seek</c> is used to determine whether a stream buffer supports seeking.
+    /// </summary>
+    /// <returns>True if seeking is supported, false otherwise.</returns>
+    virtual bool can_seek() const { return get_base()->can_seek(); }
+
+    /// <summary>
+    /// <c>has_size</c> is used to determine whether a stream buffer supports size().
+    /// </summary>
+    /// <returns>True if the <c>size</c> API is supported, false otherwise.</returns>
+    virtual bool has_size() const { return get_base()->has_size(); }
+
+    /// <summary>
+    /// Gets the total number of characters in the stream buffer, if known. Calls to <c>has_size</c> will determine
+    /// whether the result of <c>size</c> can be relied on.
+    /// </summary>
+    /// <returns>The total number of characters in the stream buffer.</returns>
+    virtual utility::size64_t size() const { return get_base()->size(); }
+
+    /// <summary>
+    /// Gets the stream buffer size, if one has been set.
+    /// </summary>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <returns>The size of the internal buffer (for the given direction).</returns>
+    /// <remarks>An implementation that does not support buffering will always return 0.</remarks>
+    virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const
+    {
+        return get_base()->buffer_size(direction);
+    }
+
+    /// <summary>
+    /// Sets the stream buffer implementation to buffer or not buffer.
+    /// </summary>
+    /// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
+    /// will not have any effect on what is returned by subsequent calls to <see cref="::buffer_size method"
+    /// />.</remarks>
+    virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in)
+    {
+        get_base()->set_buffer_size(size, direction);
+    }
+
+    /// <summary>
+    /// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
+    /// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
+    /// incurring the overhead of using tasks.
+    /// </summary>
+    /// <returns>Number of characters that are ready to read.</returns>
+    virtual size_t in_avail() const { return get_base()->in_avail(); }
+
+    /// <summary>
+    /// Checks if the stream buffer is open.
+    /// </summary>
+    /// <remarks>No separation is made between open for reading and open for writing.</remarks>
+    /// <returns>True if the stream buffer is open for reading or writing, false otherwise.</returns>
+    virtual bool is_open() const { return get_base()->is_open(); }
+
+    /// <summary>
+    /// <c>is_eof</c> is used to determine whether a read head has reached the end of the buffer.
+    /// </summary>
+    /// <returns>True if at the end of the buffer, false otherwise.</returns>
+    virtual bool is_eof() const { return get_base()->is_eof(); }
+
+    /// <summary>
+    /// Closes the stream buffer, preventing further read or write operations.
+    /// </summary>
+    /// <param name="mode">The I/O mode (in or out) to close for.</param>
+    virtual pplx::task<void> close(std::ios_base::openmode mode = (std::ios_base::in | std::ios_base::out))
+    {
+        // We preserve the check here to workaround a Dev10 compiler crash
+        auto buffer = get_base();
+        return buffer ? buffer->close(mode) : pplx::task_from_result();
+    }
+
+    /// <summary>
+    /// Closes the stream buffer with an exception.
+    /// </summary>
+    /// <param name="mode">The I/O mode (in or out) to close for.</param>
+    /// <param name="eptr">Pointer to the exception.</param>
+    virtual pplx::task<void> close(std::ios_base::openmode mode, std::exception_ptr eptr)
+    {
+        // We preserve the check here to workaround a Dev10 compiler crash
+        auto buffer = get_base();
+        return buffer ? buffer->close(mode, eptr) : pplx::task_from_result();
+    }
+
+    /// <summary>
+    /// Writes a single character to the stream.
+    /// </summary>
+    /// <param name="ch">The character to write</param>
+    /// <returns>The value of the character. EOF if the write operation fails</returns>
+    virtual pplx::task<int_type> putc(_CharType ch) { return get_base()->putc(ch); }
+
+    /// <summary>
+    /// Allocates a contiguous memory block and returns it.
+    /// </summary>
+    /// <param name="count">The number of characters to allocate.</param>
+    /// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
+    /// alloc/commit.</returns>
+    virtual _CharType* alloc(size_t count) { return get_base()->alloc(count); }
+
+    /// <summary>
+    /// Submits a block already allocated by the stream buffer.
+    /// </summary>
+    /// <param name="count">The number of characters to be committed.</param>
+    virtual void commit(size_t count) { get_base()->commit(count); }
+
+    /// <summary>
+    /// Gets a pointer to the next already allocated contiguous block of data.
+    /// </summary>
+    /// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
+    /// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
+    /// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
+    /// <remarks>
+    /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
+    /// there is no block to return immediately or that the stream buffer does not support the operation.
+    /// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
+    /// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
+    /// a subsequent read will not succeed.
+    /// </remarks>
+    virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
+    {
+        ptr = nullptr;
+        count = 0;
+        return get_base()->acquire(ptr, count);
+    }
+
+    /// <summary>
+    /// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
+    /// de-allocate the memory, if it so desires. Move the read position ahead by the count.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be released.</param>
+    /// <param name="count">The number of characters that were read.</param>
+    virtual void release(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { get_base()->release(ptr, count); }
+
+    /// <summary>
+    /// Writes a number of characters to the stream.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be written.</param>
+    /// <param name="count">The number of characters to write.</param>
+    /// <returns>The number of characters actually written, either 'count' or 0.</returns>
+    CASABLANCA_DEPRECATED("This API in some cases performs a copy. It is deprecated and will be removed in a future "
+                          "release. Use putn_nocopy instead.")
+    virtual pplx::task<size_t> putn(const _CharType* ptr, size_t count) { return get_base()->putn(ptr, count); }
+
+    /// <summary>
+    /// Writes a number of characters to the stream. Note: callers must make sure the data to be written is valid until
+    /// the returned task completes.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be written.</param>
+    /// <param name="count">The number of characters to write.</param>
+    /// <returns>The number of characters actually written, either 'count' or 0.</returns>
+    virtual pplx::task<size_t> putn_nocopy(const _CharType* ptr, size_t count)
+    {
+        return get_base()->putn_nocopy(ptr, count);
+    }
+
+    /// <summary>
+    /// Reads a single character from the stream and advances the read position.
+    /// </summary>
+    /// <returns>The value of the character. EOF if the read fails.</returns>
+    virtual pplx::task<int_type> bumpc() { return get_base()->bumpc(); }
+
+    /// <summary>
+    /// Reads a single character from the stream and advances the read position.
+    /// </summary>
+    /// <returns>The value of the character. <c>-1</c> if the read fails. <c>-2</c> if an asynchronous read is
+    /// required</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
+    virtual typename details::basic_streambuf<_CharType>::int_type sbumpc() { return get_base()->sbumpc(); }
+
+    /// <summary>
+    /// Reads a single character from the stream without advancing the read position.
+    /// </summary>
+    /// <returns>The value of the byte. EOF if the read fails.</returns>
+    virtual pplx::task<int_type> getc() { return get_base()->getc(); }
+
+    /// <summary>
+    /// Reads a single character from the stream without advancing the read position.
+    /// </summary>
+    /// <returns>The value of the character. EOF if the read fails. <see cref="::requires_async method" /> if an
+    /// asynchronous read is required</returns> <remarks>This is a synchronous operation, but is guaranteed to never
+    /// block.</remarks>
+    virtual typename details::basic_streambuf<_CharType>::int_type sgetc() { return get_base()->sgetc(); }
+
+    /// <summary>
+    /// Advances the read position, then returns the next character without advancing again.
+    /// </summary>
+    /// <returns>The value of the character. EOF if the read fails.</returns>
+    pplx::task<int_type> nextc() { return get_base()->nextc(); }
+
+    /// <summary>
+    /// Retreats the read position, then returns the current character without advancing.
+    /// </summary>
+    /// <returns>The value of the character. EOF if the read fails. <see cref="::requires_async method" /> if an
+    /// asynchronous read is required</returns>
+    pplx::task<int_type> ungetc() { return get_base()->ungetc(); }
+
+    /// <summary>
+    /// Reads up to a given number of characters from the stream.
+    /// </summary>
+    /// <param name="ptr">The address of the target memory area.</param>
+    /// <param name="count">The maximum number of characters to read.</param>
+    /// <returns>The number of characters read. O if the end of the stream is reached.</returns>
+    virtual pplx::task<size_t> getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        return get_base()->getn(ptr, count);
+    }
+
+    /// <summary>
+    /// Copies up to a given number of characters from the stream, synchronously.
+    /// </summary>
+    /// <param name="ptr">The address of the target memory area.</param>
+    /// <param name="count">The maximum number of characters to read.</param>
+    /// <returns>The number of characters copied. O if the end of the stream is reached or an asynchronous read is
+    /// required.</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
+    virtual size_t scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        return get_base()->scopy(ptr, count);
+    }
+
+    /// <summary>
+    /// Gets the current read or write position in the stream.
+    /// </summary>
+    /// <param name="direction">The I/O direction to seek (see remarks)</param>
+    /// <returns>The current position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the direction parameter defines whether to move the read or the write
+    ///          cursor.</remarks>
+    virtual typename details::basic_streambuf<_CharType>::pos_type getpos(std::ios_base::openmode direction) const
+    {
+        return get_base()->getpos(direction);
+    }
+
+    /// <summary>
+    /// Seeks to the given position.
+    /// </summary>
+    /// <param name="pos">The offset from the beginning of the stream.</param>
+    /// <param name="direction">The I/O direction to seek (see remarks).</param>
+    /// <returns>The position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors. For such streams, the direction parameter
+    /// defines whether to move the read or the write cursor.</remarks>
+    virtual typename details::basic_streambuf<_CharType>::pos_type seekpos(
+        typename details::basic_streambuf<_CharType>::pos_type pos, std::ios_base::openmode direction)
+    {
+        return get_base()->seekpos(pos, direction);
+    }
+
+    /// <summary>
+    /// Seeks to a position given by a relative offset.
+    /// </summary>
+    /// <param name="offset">The relative position to seek to</param>
+    /// <param name="way">The starting point (beginning, end, current) for the seek.</param>
+    /// <param name="mode">The I/O direction to seek (see remarks)</param>
+    /// <returns>The position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
+    virtual typename details::basic_streambuf<_CharType>::pos_type seekoff(
+        typename details::basic_streambuf<_CharType>::off_type offset,
+        std::ios_base::seekdir way,
+        std::ios_base::openmode mode)
+    {
+        return get_base()->seekoff(offset, way, mode);
+    }
+
+    /// <summary>
+    /// For output streams, flush any internally buffered data to the underlying medium.
+    /// </summary>
+    /// <returns><c>true</c> if the flush succeeds, <c>false</c> if not</returns>
+    virtual pplx::task<void> sync() { return get_base()->sync(); }
+
+    /// <summary>
+    /// Retrieves the stream buffer exception_ptr if it has been set.
+    /// </summary>
+    /// <returns>Pointer to the exception, if it has been set; otherwise, <c>nullptr</c> will be returned</returns>
+    virtual std::exception_ptr exception() const { return get_base()->exception(); }
+
+private:
+    std::shared_ptr<details::basic_streambuf<_CharType>> m_buffer;
+};
+
+} // namespace streams
+} // namespace Concurrency
diff --git a/Release/include/cpprest/asyncrt_utils.h b/Release/include/cpprest/asyncrt_utils.h
new file mode 100644 (file)
index 0000000..3e4bfdd
--- /dev/null
@@ -0,0 +1,736 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Various common utilities.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/details/basic_types.h"
+#include "pplx/pplxtasks.h"
+#include <chrono>
+#include <cstdint>
+#include <limits.h>
+#include <locale.h>
+#include <random>
+#include <string>
+#include <system_error>
+#include <vector>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#if !defined(ANDROID) && !defined(__ANDROID__) && defined(HAVE_XLOCALE_H) // CodePlex 269
+/* Systems using glibc: xlocale.h has been removed from glibc 2.26
+   The above include of locale.h is sufficient
+   Further details: https://sourceware.org/git/?p=glibc.git;a=commit;h=f0be25b6336db7492e47d2e8e72eb8af53b5506d */
+#include <xlocale.h>
+#endif
+#endif
+
+/// Various utilities for string conversions and date and time manipulation.
+namespace utility
+{
+// Left over from VS2010 support, remains to avoid breaking.
+typedef std::chrono::seconds seconds;
+
+/// Functions for converting to/from std::chrono::seconds to xml string.
+namespace timespan
+{
+/// <summary>
+/// Converts a timespan/interval in seconds to xml duration string as specified by
+/// http://www.w3.org/TR/xmlschema-2/#duration
+/// </summary>
+_ASYNCRTIMP utility::string_t __cdecl seconds_to_xml_duration(utility::seconds numSecs);
+
+/// <summary>
+/// Converts an xml duration to timespan/interval in seconds
+/// http://www.w3.org/TR/xmlschema-2/#duration
+/// </summary>
+_ASYNCRTIMP utility::seconds __cdecl xml_duration_to_seconds(const utility::string_t& timespanString);
+} // namespace timespan
+
+/// Functions for Unicode string conversions.
+namespace conversions
+{
+/// <summary>
+/// Converts a UTF-16 string to a UTF-8 string.
+/// </summary>
+/// <param name="w">A two byte character UTF-16 string.</param>
+/// <returns>A single byte character UTF-8 string.</returns>
+_ASYNCRTIMP std::string __cdecl utf16_to_utf8(const utf16string& w);
+
+/// <summary>
+/// Converts a UTF-8 string to a UTF-16
+/// </summary>
+/// <param name="s">A single byte character UTF-8 string.</param>
+/// <returns>A two byte character UTF-16 string.</returns>
+_ASYNCRTIMP utf16string __cdecl utf8_to_utf16(const std::string& s);
+
+/// <summary>
+/// Converts a ASCII (us-ascii) string to a UTF-16 string.
+/// </summary>
+/// <param name="s">A single byte character us-ascii string.</param>
+/// <returns>A two byte character UTF-16 string.</returns>
+_ASYNCRTIMP utf16string __cdecl usascii_to_utf16(const std::string& s);
+
+/// <summary>
+/// Converts a Latin1 (iso-8859-1) string to a UTF-16 string.
+/// </summary>
+/// <param name="s">A single byte character UTF-8 string.</param>
+/// <returns>A two byte character UTF-16 string.</returns>
+_ASYNCRTIMP utf16string __cdecl latin1_to_utf16(const std::string& s);
+
+/// <summary>
+/// Converts a Latin1 (iso-8859-1) string to a UTF-8 string.
+/// </summary>
+/// <param name="s">A single byte character UTF-8 string.</param>
+/// <returns>A single byte character UTF-8 string.</returns>
+_ASYNCRTIMP utf8string __cdecl latin1_to_utf8(const std::string& s);
+
+/// <summary>
+/// Converts to a platform dependent Unicode string type.
+/// </summary>
+/// <param name="s">A single byte character UTF-8 string.</param>
+/// <returns>A platform dependent string type.</returns>
+#ifdef _UTF16_STRINGS
+_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string&& s);
+#else
+inline utility::string_t&& to_string_t(std::string&& s) { return std::move(s); }
+#endif
+
+/// <summary>
+/// Converts to a platform dependent Unicode string type.
+/// </summary>
+/// <param name="s">A two byte character UTF-16 string.</param>
+/// <returns>A platform dependent string type.</returns>
+#ifdef _UTF16_STRINGS
+inline utility::string_t&& to_string_t(utf16string&& s) { return std::move(s); }
+#else
+_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string&& s);
+#endif
+/// <summary>
+/// Converts to a platform dependent Unicode string type.
+/// </summary>
+/// <param name="s">A single byte character UTF-8 string.</param>
+/// <returns>A platform dependent string type.</returns>
+#ifdef _UTF16_STRINGS
+_ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string& s);
+#else
+inline const utility::string_t& to_string_t(const std::string& s) { return s; }
+#endif
+
+/// <summary>
+/// Converts to a platform dependent Unicode string type.
+/// </summary>
+/// <param name="s">A two byte character UTF-16 string.</param>
+/// <returns>A platform dependent string type.</returns>
+#ifdef _UTF16_STRINGS
+inline const utility::string_t& to_string_t(const utf16string& s) { return s; }
+#else
+_ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string& s);
+#endif
+
+/// <summary>
+/// Converts to a UTF-16 from string.
+/// </summary>
+/// <param name="value">A single byte character UTF-8 string.</param>
+/// <returns>A two byte character UTF-16 string.</returns>
+_ASYNCRTIMP utf16string __cdecl to_utf16string(const std::string& value);
+
+/// <summary>
+/// Converts to a UTF-16 from string.
+/// </summary>
+/// <param name="value">A two byte character UTF-16 string.</param>
+/// <returns>A two byte character UTF-16 string.</returns>
+inline const utf16string& to_utf16string(const utf16string& value) { return value; }
+/// <summary>
+/// Converts to a UTF-16 from string.
+/// </summary>
+/// <param name="value">A two byte character UTF-16 string.</param>
+/// <returns>A two byte character UTF-16 string.</returns>
+inline utf16string&& to_utf16string(utf16string&& value) { return std::move(value); }
+
+/// <summary>
+/// Converts to a UTF-8 string.
+/// </summary>
+/// <param name="value">A single byte character UTF-8 string.</param>
+/// <returns>A single byte character UTF-8 string.</returns>
+inline std::string&& to_utf8string(std::string&& value) { return std::move(value); }
+
+/// <summary>
+/// Converts to a UTF-8 string.
+/// </summary>
+/// <param name="value">A single byte character UTF-8 string.</param>
+/// <returns>A single byte character UTF-8 string.</returns>
+inline const std::string& to_utf8string(const std::string& value) { return value; }
+
+/// <summary>
+/// Converts to a UTF-8 string.
+/// </summary>
+/// <param name="value">A two byte character UTF-16 string.</param>
+/// <returns>A single byte character UTF-8 string.</returns>
+_ASYNCRTIMP std::string __cdecl to_utf8string(const utf16string& value);
+
+/// <summary>
+/// Encode the given byte array into a base64 string
+/// </summary>
+_ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector<unsigned char>& data);
+
+/// <summary>
+/// Encode the given 8-byte integer into a base64 string
+/// </summary>
+_ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data);
+
+/// <summary>
+/// Decode the given base64 string to a byte array
+/// </summary>
+_ASYNCRTIMP std::vector<unsigned char> __cdecl from_base64(const utility::string_t& str);
+
+template<typename Source>
+CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
+                      "locale support is required.")
+utility::string_t print_string(const Source& val, const std::locale& loc = std::locale())
+{
+    utility::ostringstream_t oss;
+    oss.imbue(loc);
+    oss << val;
+    if (oss.bad())
+    {
+        throw std::bad_cast();
+    }
+    return oss.str();
+}
+
+CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
+                      "locale support is required.")
+inline utility::string_t print_string(const utility::string_t& val) { return val; }
+
+namespace details
+{
+#if defined(__ANDROID__)
+template<class T>
+inline std::string to_string(const T t)
+{
+    std::ostringstream os;
+    os.imbue(std::locale::classic());
+    os << t;
+    return os.str();
+}
+#endif
+
+template<class T>
+inline utility::string_t to_string_t(const T t)
+{
+#ifdef _UTF16_STRINGS
+    using std::to_wstring;
+    return to_wstring(t);
+#else
+#if !defined(__ANDROID__)
+    using std::to_string;
+#endif
+    return to_string(t);
+#endif
+}
+
+template<typename Source>
+utility::string_t print_string(const Source& val)
+{
+    utility::ostringstream_t oss;
+    oss.imbue(std::locale::classic());
+    oss << val;
+    if (oss.bad())
+    {
+        throw std::bad_cast();
+    }
+    return oss.str();
+}
+
+inline const utility::string_t& print_string(const utility::string_t& val) { return val; }
+
+template<typename Source>
+utf8string print_utf8string(const Source& val)
+{
+    return conversions::to_utf8string(print_string(val));
+}
+inline const utf8string& print_utf8string(const utf8string& val) { return val; }
+
+template<typename Target>
+Target scan_string(const utility::string_t& str)
+{
+    Target t;
+    utility::istringstream_t iss(str);
+    iss.imbue(std::locale::classic());
+    iss >> t;
+    if (iss.bad())
+    {
+        throw std::bad_cast();
+    }
+    return t;
+}
+
+inline const utility::string_t& scan_string(const utility::string_t& str) { return str; }
+} // namespace details
+
+template<typename Target>
+CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
+                      "locale support is required.")
+Target scan_string(const utility::string_t& str, const std::locale& loc = std::locale())
+{
+    Target t;
+    utility::istringstream_t iss(str);
+    iss.imbue(loc);
+    iss >> t;
+    if (iss.bad())
+    {
+        throw std::bad_cast();
+    }
+    return t;
+}
+
+CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
+                      "locale support is required.")
+inline utility::string_t scan_string(const utility::string_t& str) { return str; }
+} // namespace conversions
+
+namespace details
+{
+/// <summary>
+/// Cross platform RAII container for setting thread local locale.
+/// </summary>
+class scoped_c_thread_locale
+{
+public:
+    _ASYNCRTIMP scoped_c_thread_locale();
+    _ASYNCRTIMP ~scoped_c_thread_locale();
+
+#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269
+#ifdef _WIN32
+    typedef _locale_t xplat_locale;
+#else
+    typedef locale_t xplat_locale;
+#endif
+
+    static _ASYNCRTIMP xplat_locale __cdecl c_locale();
+#endif
+private:
+#ifdef _WIN32
+    std::string m_prevLocale;
+    int m_prevThreadSetting;
+#elif !(defined(ANDROID) || defined(__ANDROID__))
+    locale_t m_prevLocale;
+#endif
+    scoped_c_thread_locale(const scoped_c_thread_locale&);
+    scoped_c_thread_locale& operator=(const scoped_c_thread_locale&);
+};
+
+/// <summary>
+/// Our own implementation of alpha numeric instead of std::isalnum to avoid
+/// taking global lock for performance reasons.
+/// </summary>
+inline bool __cdecl is_alnum(const unsigned char uch) CPPREST_NOEXCEPT
+{ // test if uch is an alnum character
+    // special casing char to avoid branches
+    // clang-format off
+    static CPPREST_CONSTEXPR bool is_alnum_table[UCHAR_MAX + 1] = {
+        /*        X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */
+        /* 0X */   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        /* 1X */   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        /* 2X */   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        /* 3X */   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0-9 */
+        /* 4X */   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A-Z */
+        /* 5X */   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+        /* 6X */   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a-z */
+        /* 7X */   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
+        /* non-ASCII values initialized to 0 */
+    };
+    // clang-format on
+    return (is_alnum_table[uch]);
+}
+
+/// <summary>
+/// Our own implementation of alpha numeric instead of std::isalnum to avoid
+/// taking global lock for performance reasons.
+/// </summary>
+inline bool __cdecl is_alnum(const char ch) CPPREST_NOEXCEPT { return (is_alnum(static_cast<unsigned char>(ch))); }
+
+/// <summary>
+/// Our own implementation of alpha numeric instead of std::isalnum to avoid
+/// taking global lock for performance reasons.
+/// </summary>
+template<class Elem>
+inline bool __cdecl is_alnum(Elem ch) CPPREST_NOEXCEPT
+{
+    // assumes 'x' == L'x' for the ASCII range
+    typedef typename std::make_unsigned<Elem>::type UElem;
+    const auto uch = static_cast<UElem>(ch);
+    return (uch <= static_cast<UElem>('z') && is_alnum(static_cast<unsigned char>(uch)));
+}
+
+/// <summary>
+/// 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
+/// </summary>
+template<class Elem>
+inline bool __cdecl is_space(Elem ch) CPPREST_NOEXCEPT
+{
+    // assumes 'x' == L'x' for the ASCII range
+    typedef typename std::make_unsigned<Elem>::type UElem;
+    const auto uch = static_cast<UElem>(ch);
+    return uch == 0x20u || (uch >= 0x09u && uch <= 0x0Du);
+}
+
+/// <summary>
+/// Simplistic implementation of make_unique. A better implementation would be based on variadic templates
+/// and therefore not be compatible with Dev10.
+/// </summary>
+template<typename _Type>
+std::unique_ptr<_Type> make_unique()
+{
+    return std::unique_ptr<_Type>(new _Type());
+}
+
+template<typename _Type, typename _Arg1>
+std::unique_ptr<_Type> make_unique(_Arg1&& arg1)
+{
+    return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1)));
+}
+
+template<typename _Type, typename _Arg1, typename _Arg2>
+std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2)
+{
+    return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2)));
+}
+
+template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3>
+std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3)
+{
+    return std::unique_ptr<_Type>(
+        new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3)));
+}
+
+template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4>
+std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4)
+{
+    return std::unique_ptr<_Type>(new _Type(
+        std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4)));
+}
+
+template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4, typename _Arg5>
+std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5)
+{
+    return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1),
+                                            std::forward<_Arg2>(arg2),
+                                            std::forward<_Arg3>(arg3),
+                                            std::forward<_Arg4>(arg4),
+                                            std::forward<_Arg5>(arg5)));
+}
+
+template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4, typename _Arg5, typename _Arg6>
+std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5, _Arg6&& arg6)
+{
+    return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1),
+                                            std::forward<_Arg2>(arg2),
+                                            std::forward<_Arg3>(arg3),
+                                            std::forward<_Arg4>(arg4),
+                                            std::forward<_Arg5>(arg5),
+                                            std::forward<_Arg6>(arg6)));
+}
+
+/// <summary>
+/// Cross platform utility function for performing case insensitive string equality comparison.
+/// </summary>
+/// <param name="left">First string to compare.</param>
+/// <param name="right">Second strong to compare.</param>
+/// <returns>true if the strings are equivalent, false otherwise</returns>
+_ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT;
+
+/// <summary>
+/// Cross platform utility function for performing case insensitive string equality comparison.
+/// </summary>
+/// <param name="left">First string to compare.</param>
+/// <param name="right">Second strong to compare.</param>
+/// <returns>true if the strings are equivalent, false otherwise</returns>
+_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT;
+
+/// <summary>
+/// Cross platform utility function for performing case insensitive string less-than comparison.
+/// </summary>
+/// <param name="left">First string to compare.</param>
+/// <param name="right">Second strong to compare.</param>
+/// <returns>true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise,
+/// false.</returns>
+_ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT;
+
+/// <summary>
+/// Cross platform utility function for performing case insensitive string less-than comparison.
+/// </summary>
+/// <param name="left">First string to compare.</param>
+/// <param name="right">Second strong to compare.</param>
+/// <returns>true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise,
+/// false.</returns>
+_ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT;
+
+/// <summary>
+/// Convert a string to lowercase in place.
+/// </summary>
+/// <param name="target">The string to convert to lowercase.</param>
+_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT;
+
+/// <summary>
+/// Convert a string to lowercase in place.
+/// </summary>
+/// <param name="target">The string to convert to lowercase.</param>
+_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT;
+
+#ifdef _WIN32
+
+/// <summary>
+/// Category error type for Windows OS errors.
+/// </summary>
+class windows_category_impl : public std::error_category
+{
+public:
+    virtual const char* name() const CPPREST_NOEXCEPT { return "windows"; }
+
+    virtual std::string message(int errorCode) const CPPREST_NOEXCEPT;
+
+    virtual std::error_condition default_error_condition(int errorCode) const CPPREST_NOEXCEPT;
+};
+
+/// <summary>
+/// Gets the one global instance of the windows error category.
+/// </summary>
+/// </returns>An error category instance.</returns>
+_ASYNCRTIMP const std::error_category& __cdecl windows_category();
+
+#else
+
+/// <summary>
+/// Gets the one global instance of the linux error category.
+/// </summary>
+/// </returns>An error category instance.</returns>
+_ASYNCRTIMP const std::error_category& __cdecl linux_category();
+
+#endif
+
+/// <summary>
+/// Gets the one global instance of the current platform's error category.
+/// </summary>
+_ASYNCRTIMP const std::error_category& __cdecl platform_category();
+
+/// <summary>
+/// Creates an instance of std::system_error from a OS error code.
+/// </summary>
+inline std::system_error __cdecl create_system_error(unsigned long errorCode)
+{
+    std::error_code code((int)errorCode, platform_category());
+    return std::system_error(code, code.message());
+}
+
+/// <summary>
+/// Creates a std::error_code from a OS error code.
+/// </summary>
+inline std::error_code __cdecl create_error_code(unsigned long errorCode)
+{
+    return std::error_code((int)errorCode, platform_category());
+}
+
+/// <summary>
+/// Creates the corresponding error message from a OS error code.
+/// </summary>
+inline utility::string_t __cdecl create_error_message(unsigned long errorCode)
+{
+    return utility::conversions::to_string_t(create_error_code(errorCode).message());
+}
+
+} // namespace details
+
+class datetime
+{
+public:
+    typedef uint64_t interval_type;
+
+    /// <summary>
+    /// Defines the supported date and time string formats.
+    /// </summary>
+    enum date_format
+    {
+        RFC_1123,
+        ISO_8601
+    };
+
+    /// <summary>
+    /// Returns the current UTC time.
+    /// </summary>
+    static _ASYNCRTIMP datetime __cdecl utc_now();
+
+    /// <summary>
+    /// An invalid UTC timestamp value.
+    /// </summary>
+    enum : interval_type
+    {
+        utc_timestamp_invalid = static_cast<interval_type>(-1)
+    };
+
+    /// <summary>
+    /// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00.
+    /// If time is before epoch, utc_timestamp_invalid is returned.
+    /// </summary>
+    static interval_type utc_timestamp()
+    {
+        const auto seconds = utc_now().to_interval() / _secondTicks;
+        if (seconds >= 11644473600LL)
+        {
+            return seconds - 11644473600LL;
+        }
+        else
+        {
+            return utc_timestamp_invalid;
+        }
+    }
+
+    datetime() : m_interval(0) { }
+
+    /// <summary>
+    /// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 or ISO 8601 format.
+    /// </summary>
+    /// <returns>Returns a <c>datetime</c> of zero if not successful.</returns>
+    static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123);
+
+    /// <summary>
+    /// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 or ISO 8601 format.
+    /// </summary>
+    /// <returns>Returns <c>datetime::maximum()</c> if not successful.</returns>
+    static _ASYNCRTIMP datetime __cdecl from_string_maximum_error(const utility::string_t& timestring,
+                                                                  date_format format = RFC_1123);
+
+    /// <summary>
+    /// Returns a string representation of the <c>datetime</c>.
+    /// </summary>
+    _ASYNCRTIMP utility::string_t to_string(date_format format = RFC_1123) const;
+
+    /// <summary>
+    /// Returns the integral time value.
+    /// </summary>
+    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); }
+
+    bool operator==(datetime dt) const { return m_interval == dt.m_interval; }
+
+    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; }
+
+    static interval_type from_minutes(unsigned int minutes) { return minutes * _minuteTicks; }
+
+    static interval_type from_hours(unsigned int hours) { return hours * _hourTicks; }
+
+    static interval_type from_days(unsigned int days) { return days * _dayTicks; }
+
+    bool is_initialized() const { return m_interval != 0; }
+
+    static datetime maximum() { return datetime(static_cast<interval_type>(-1)); }
+
+private:
+    friend int operator-(datetime t1, datetime t2);
+
+    static const interval_type _msTicks = static_cast<interval_type>(10000);
+    static const interval_type _secondTicks = 1000 * _msTicks;
+    static const interval_type _minuteTicks = 60 * _secondTicks;
+    static const interval_type _hourTicks = 60 * 60 * _secondTicks;
+    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) { }
+
+    // Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns.
+    interval_type m_interval;
+};
+
+inline int operator-(datetime t1, datetime t2)
+{
+    auto diff = (t1.m_interval - t2.m_interval);
+
+    // Round it down to seconds
+    diff /= 10 * 1000 * 1000;
+
+    return static_cast<int>(diff);
+}
+
+/// <summary>
+/// Nonce string generator class.
+/// </summary>
+class nonce_generator
+{
+public:
+    /// <summary>
+    /// Define default nonce length.
+    /// </summary>
+    enum
+    {
+        default_length = 32
+    };
+
+    /// <summary>
+    /// Nonce generator constructor.
+    /// </summary>
+    /// <param name="length">Length of the generated nonce string.</param>
+    nonce_generator(int length = default_length)
+        : m_random(static_cast<unsigned int>(utility::datetime::utc_timestamp())), m_length(length)
+    {
+    }
+
+    /// <summary>
+    /// Generate a nonce string containing random alphanumeric characters (A-Za-z0-9).
+    /// Length of the generated string is set by length().
+    /// </summary>
+    /// <returns>The generated nonce string.</returns>
+    _ASYNCRTIMP utility::string_t generate();
+
+    /// <summary>
+    /// Get length of generated nonce string.
+    /// </summary>
+    /// <returns>Nonce string length.</returns>
+    int length() const { return m_length; }
+
+    /// <summary>
+    /// Set length of the generated nonce string.
+    /// </summary>
+    /// <param name="length">Lenght of nonce string.</param>
+    void set_length(int length) { m_length = length; }
+
+private:
+    std::mt19937 m_random;
+    int m_length;
+};
+
+} // namespace utility
diff --git a/Release/include/cpprest/base_uri.h b/Release/include/cpprest/base_uri.h
new file mode 100644 (file)
index 0000000..7c69431
--- /dev/null
@@ -0,0 +1,391 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Protocol independent support for URIs.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/details/basic_types.h"
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace web
+{
+namespace details
+{
+struct uri_components
+{
+    uri_components() : m_path(_XPLATSTR("/")), m_port(-1) {}
+
+    uri_components(const uri_components&) = default;
+    uri_components& operator=(const uri_components&) = default;
+
+    // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
+    uri_components(uri_components&& other) CPPREST_NOEXCEPT : m_scheme(std::move(other.m_scheme)),
+                                                              m_host(std::move(other.m_host)),
+                                                              m_user_info(std::move(other.m_user_info)),
+                                                              m_path(std::move(other.m_path)),
+                                                              m_query(std::move(other.m_query)),
+                                                              m_fragment(std::move(other.m_fragment)),
+                                                              m_port(other.m_port)
+    {
+    }
+
+    // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
+    uri_components& operator=(uri_components&& other) CPPREST_NOEXCEPT
+    {
+        if (this != &other)
+        {
+            m_scheme = std::move(other.m_scheme);
+            m_host = std::move(other.m_host);
+            m_user_info = std::move(other.m_user_info);
+            m_path = std::move(other.m_path);
+            m_query = std::move(other.m_query);
+            m_fragment = std::move(other.m_fragment);
+            m_port = other.m_port;
+        }
+        return *this;
+    }
+
+    _ASYNCRTIMP utility::string_t join();
+
+    utility::string_t m_scheme;
+    utility::string_t m_host;
+    utility::string_t m_user_info;
+    utility::string_t m_path;
+    utility::string_t m_query;
+    utility::string_t m_fragment;
+    int m_port;
+};
+} // namespace details
+
+/// <summary>
+/// A single exception type to represent errors in parsing, encoding, and decoding URIs.
+/// </summary>
+class uri_exception : public std::exception
+{
+public:
+    uri_exception(std::string msg) : m_msg(std::move(msg)) {}
+
+    ~uri_exception() CPPREST_NOEXCEPT {}
+
+    const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
+
+private:
+    std::string m_msg;
+};
+
+/// <summary>
+/// A flexible, protocol independent URI implementation.
+///
+/// URI instances are immutable. Querying the various fields on an empty URI will return empty strings. Querying
+/// various diagnostic members on an empty URI will return false.
+/// </summary>
+/// <remarks>
+/// This implementation accepts both URIs ('http://msn.com/path') and URI relative-references
+/// ('/path?query#frag').
+///
+/// This implementation does not provide any scheme-specific handling -- an example of this
+/// would be the following: 'http://path1/path'. This is a valid URI, but it's not a valid
+/// http-uri -- that is, it's syntactically correct but does not conform to the requirements
+/// of the http scheme (http requires a host).
+/// We could provide this by allowing a pluggable 'scheme' policy-class, which would provide
+/// extra capability for validating and canonicalizing a URI according to scheme, and would
+/// introduce a layer of type-safety for URIs of differing schemes, and thus differing semantics.
+///
+/// One issue with implementing a scheme-independent URI facility is that of comparing for equality.
+/// For instance, these URIs are considered equal 'http://msn.com', 'http://msn.com:80'. That is --
+/// the 'default' port can be either omitted or explicit. Since we don't have a way to map a scheme
+/// to it's default port, we don't have a way to know these are equal. This is just one of a class of
+/// issues with regard to scheme-specific behavior.
+/// </remarks>
+class uri
+{
+public:
+    /// <summary>
+    /// The various components of a URI. This enum is used to indicate which
+    /// URI component is being encoded to the encode_uri_component. This allows
+    /// specific encoding to be performed.
+    ///
+    /// Scheme and port don't allow '%' so they don't need to be encoded.
+    /// </summary>
+    class components
+    {
+    public:
+        enum component
+        {
+            user_info,
+            host,
+            path,
+            query,
+            fragment,
+            full_uri
+        };
+    };
+
+    /// <summary>
+    /// Encodes a URI component according to RFC 3986.
+    /// Note if a full URI is specified instead of an individual URI component all
+    /// characters not in the unreserved set are escaped.
+    /// </summary>
+    /// <param name="raw">The URI as a string.</param>
+    /// <returns>The encoded string.</returns>
+    _ASYNCRTIMP static utility::string_t __cdecl encode_uri(const utility::string_t& raw,
+                                                            uri::components::component = components::full_uri);
+
+    /// <summary>
+    /// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their
+    /// hexadecimal representation.
+    /// </summary>
+    /// <returns>The encoded string.</returns>
+    _ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t& data);
+
+    /// <summary>
+    /// Decodes an encoded string.
+    /// </summary>
+    /// <param name="encoded">The URI as a string.</param>
+    /// <returns>The decoded string.</returns>
+    _ASYNCRTIMP static utility::string_t __cdecl decode(const utility::string_t& encoded);
+
+    /// <summary>
+    /// Splits a path into its hierarchical components.
+    /// </summary>
+    /// <param name="path">The path as a string</param>
+    /// <returns>A <c>std::vector&lt;utility::string_t&gt;</c> containing the segments in the path.</returns>
+    _ASYNCRTIMP static std::vector<utility::string_t> __cdecl split_path(const utility::string_t& path);
+
+    /// <summary>
+    /// Splits a query into its key-value components.
+    /// </summary>
+    /// <param name="query">The query string</param>
+    /// <returns>A <c>std::map&lt;utility::string_t, utility::string_t&gt;</c> containing the key-value components of
+    /// the query.</returns>
+    _ASYNCRTIMP static std::map<utility::string_t, utility::string_t> __cdecl split_query(
+        const utility::string_t& query);
+
+    /// <summary>
+    /// Validates a string as a URI.
+    /// </summary>
+    /// <remarks>
+    /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query').
+    /// </remarks>
+    /// <param name="uri_string">The URI string to be validated.</param>
+    /// <returns><c>true</c> if the given string represents a valid URI, <c>false</c> otherwise.</returns>
+    _ASYNCRTIMP static bool __cdecl validate(const utility::string_t& uri_string);
+
+    /// <summary>
+    /// Creates an empty uri
+    /// </summary>
+    uri() : m_uri(_XPLATSTR("/")) {}
+
+    /// <summary>
+    /// Creates a URI from the given encoded string. This will throw an exception if the string
+    /// does not contain a valid URI. Use uri::validate if processing user-input.
+    /// </summary>
+    /// <param name="uri_string">A pointer to an encoded string to create the URI instance.</param>
+    _ASYNCRTIMP uri(const utility::char_t* uri_string);
+
+    /// <summary>
+    /// Creates a URI from the given encoded string. This will throw an exception if the string
+    /// does not contain a valid URI. Use uri::validate if processing user-input.
+    /// </summary>
+    /// <param name="uri_string">An encoded URI string to create the URI instance.</param>
+    _ASYNCRTIMP uri(const utility::string_t& uri_string);
+
+    /// <summary>
+    /// Copy constructor.
+    /// </summary>
+    uri(const uri&) = default;
+
+    /// <summary>
+    /// Copy assignment operator.
+    /// </summary>
+    uri& operator=(const uri&) = default;
+
+    /// <summary>
+    /// Move constructor.
+    /// </summary>
+    // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
+    uri(uri&& other) CPPREST_NOEXCEPT : m_uri(std::move(other.m_uri)), m_components(std::move(other.m_components)) {}
+
+    /// <summary>
+    /// Move assignment operator
+    /// </summary>
+    // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
+    uri& operator=(uri&& other) CPPREST_NOEXCEPT
+    {
+        if (this != &other)
+        {
+            m_uri = std::move(other.m_uri);
+            m_components = std::move(other.m_components);
+        }
+        return *this;
+    }
+
+    /// <summary>
+    /// Get the scheme component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI scheme as a string.</returns>
+    const utility::string_t& scheme() const { return m_components.m_scheme; }
+
+    /// <summary>
+    /// Get the user information component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI user information as a string.</returns>
+    const utility::string_t& user_info() const { return m_components.m_user_info; }
+
+    /// <summary>
+    /// Get the host component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI host as a string.</returns>
+    const utility::string_t& host() const { return m_components.m_host; }
+
+    /// <summary>
+    /// Get the port component of the URI. Returns -1 if no port is specified.
+    /// </summary>
+    /// <returns>The URI port as an integer.</returns>
+    int port() const { return m_components.m_port; }
+
+    /// <summary>
+    /// Get the path component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI path as a string.</returns>
+    const utility::string_t& path() const { return m_components.m_path; }
+
+    /// <summary>
+    /// Get the query component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI query as a string.</returns>
+    const utility::string_t& query() const { return m_components.m_query; }
+
+    /// <summary>
+    /// Get the fragment component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI fragment as a string.</returns>
+    const utility::string_t& fragment() const { return m_components.m_fragment; }
+
+    /// <summary>
+    /// Creates a new uri object with the same authority portion as this one, omitting the resource and query portions.
+    /// </summary>
+    /// <returns>The new uri object with the same authority.</returns>
+    _ASYNCRTIMP uri authority() const;
+
+    /// <summary>
+    /// Gets the path, query, and fragment portion of this uri, which may be empty.
+    /// </summary>
+    /// <returns>The new URI object with the path, query and fragment portion of this URI.</returns>
+    _ASYNCRTIMP uri resource() const;
+
+    /// <summary>
+    /// An empty URI specifies no components, and serves as a default value
+    /// </summary>
+    bool is_empty() const { return this->m_uri.empty() || this->m_uri == _XPLATSTR("/"); }
+
+    /// <summary>
+    /// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine.
+    /// </summary>
+    /// <remarks>
+    /// Examples include "localhost", or ip addresses in the loopback range (127.0.0.0/24).
+    /// </remarks>
+    /// <returns><c>true</c> if this URI references the local host, <c>false</c> otherwise.</returns>
+    bool is_host_loopback() const
+    {
+        return !is_empty() &&
+               ((host() == _XPLATSTR("localhost")) || (host().size() > 4 && host().substr(0, 4) == _XPLATSTR("127.")));
+    }
+
+    /// <summary>
+    /// A wildcard URI is one which refers to all hostnames that resolve to the local machine (using the * or +)
+    /// </summary>
+    /// <example>
+    /// http://*:80
+    /// </example>
+    bool is_host_wildcard() const
+    {
+        return !is_empty() && (this->host() == _XPLATSTR("*") || this->host() == _XPLATSTR("+"));
+    }
+
+    /// <summary>
+    /// A portable URI is one with a hostname that can be resolved globally (used from another machine).
+    /// </summary>
+    /// <returns><c>true</c> if this URI can be resolved globally (used from another machine), <c>false</c>
+    /// otherwise.</returns> <remarks> The hostname "localhost" is a reserved name that is guaranteed to resolve to the
+    /// local machine, and cannot be used for inter-machine communication. Likewise the hostnames "*" and "+" on Windows
+    /// represent wildcards, and do not map to a resolvable address.
+    /// </remarks>
+    bool is_host_portable() const { return !(is_empty() || is_host_loopback() || is_host_wildcard()); }
+
+    /// <summary>
+    /// A default port is one where the port is unspecified, and will be determined by the operating system.
+    /// The choice of default port may be dictated by the scheme (http -> 80) or not.
+    /// </summary>
+    /// <returns><c>true</c> if this URI instance has a default port, <c>false</c> otherwise.</returns>
+    bool is_port_default() const { return !is_empty() && this->port() == 0; }
+
+    /// <summary>
+    /// An "authority" URI is one with only a scheme, optional userinfo, hostname, and (optional) port.
+    /// </summary>
+    /// <returns><c>true</c> if this is an "authority" URI, <c>false</c> otherwise.</returns>
+    bool is_authority() const { return !is_empty() && is_path_empty() && query().empty() && fragment().empty(); }
+
+    /// <summary>
+    /// Returns whether the other URI has the same authority as this one
+    /// </summary>
+    /// <param name="other">The URI to compare the authority with.</param>
+    /// <returns><c>true</c> if both the URI's have the same authority, <c>false</c> otherwise.</returns>
+    bool has_same_authority(const uri& other) const { return !is_empty() && this->authority() == other.authority(); }
+
+    /// <summary>
+    /// Returns whether the path portion of this URI is empty
+    /// </summary>
+    /// <returns><c>true</c> if the path portion of this URI is empty, <c>false</c> otherwise.</returns>
+    bool is_path_empty() const { return path().empty() || path() == _XPLATSTR("/"); }
+
+    /// <summary>
+    /// Returns the full (encoded) URI as a string.
+    /// </summary>
+    /// <returns>The full encoded URI string.</returns>
+    utility::string_t to_string() const { return m_uri; }
+
+    /// <summary>
+    /// Returns an URI resolved against <c>this</c> as the base URI
+    /// according to RFC3986, Section 5 (https://tools.ietf.org/html/rfc3986#section-5).
+    /// </summary>
+    /// <param name="relativeUri">The relative URI to be resolved against <c>this</c> as base.</param>
+    /// <returns>The new resolved URI string.</returns>
+    _ASYNCRTIMP utility::string_t resolve_uri(const utility::string_t& relativeUri) const;
+
+    _ASYNCRTIMP bool operator==(const uri& other) const;
+
+    bool operator<(const uri& other) const { return m_uri < other.m_uri; }
+
+    bool operator!=(const uri& other) const { return !(this->operator==(other)); }
+
+private:
+    friend class uri_builder;
+
+    /// <summary>
+    /// Creates a URI from the given URI components.
+    /// </summary>
+    /// <param name="components">A URI components object to create the URI instance.</param>
+    _ASYNCRTIMP uri(const details::uri_components& components);
+
+    // Used by uri_builder
+    static utility::string_t __cdecl encode_query_impl(const utf8string& raw);
+
+    utility::string_t m_uri;
+    details::uri_components m_components;
+};
+
+} // namespace web
diff --git a/Release/include/cpprest/containerstream.h b/Release/include/cpprest/containerstream.h
new file mode 100644 (file)
index 0000000..6e949a7
--- /dev/null
@@ -0,0 +1,590 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * This file defines a basic STL-container-based stream buffer. Reading from the buffer will not remove any data
+ * from it and seeking is thus supported.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "cpprest/astreambuf.h"
+#include "cpprest/streams.h"
+#include "pplx/pplxtasks.h"
+#include <algorithm>
+#include <iterator>
+#include <queue>
+#include <vector>
+
+namespace Concurrency
+{
+namespace streams
+{
+// Forward declarations
+
+template<typename _CollectionType>
+class container_buffer;
+
+namespace details
+{
+/// <summary>
+/// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading
+/// sequences of characters.
+/// The class itself should not be used in application code, it is used by the stream definitions farther down in the
+/// header file.
+/// </summary>
+/// <remarks> When closed, neither writing nor reading is supported any longer. <c>basic_container_buffer</c> does not
+/// support simultaneous use of the buffer for reading and writing.</remarks>
+template<typename _CollectionType>
+class basic_container_buffer : public streams::details::streambuf_state_manager<typename _CollectionType::value_type>
+{
+public:
+    typedef typename _CollectionType::value_type _CharType;
+    typedef typename basic_streambuf<_CharType>::traits traits;
+    typedef typename basic_streambuf<_CharType>::int_type int_type;
+    typedef typename basic_streambuf<_CharType>::pos_type pos_type;
+    typedef typename basic_streambuf<_CharType>::off_type off_type;
+
+    /// <summary>
+    /// Returns the underlying data container
+    /// </summary>
+    _CollectionType& collection() { return m_data; }
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    virtual ~basic_container_buffer()
+    {
+        // Invoke the synchronous versions since we need to
+        // purge the request queue before deleting the buffer
+        this->_close_read();
+        this->_close_write();
+    }
+
+protected:
+    /// <summary>
+    /// can_seek is used to determine whether a stream buffer supports seeking.
+    /// </summary>
+    virtual bool can_seek() const { return this->is_open(); }
+
+    /// <summary>
+    /// <c>has_size<c/> is used to determine whether a stream buffer supports size().
+    /// </summary>
+    virtual bool has_size() const { return this->is_open(); }
+
+    /// <summary>
+    /// Gets the size of the stream, if known. Calls to <c>has_size</c> will determine whether
+    /// the result of <c>size</c> can be relied on.
+    /// </summary>
+    virtual utility::size64_t size() const { return utility::size64_t(m_data.size()); }
+
+    /// <summary>
+    /// Get the stream buffer size, if one has been set.
+    /// </summary>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
+    virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
+
+    /// <summary>
+    /// Sets the stream buffer implementation to buffer or not buffer.
+    /// </summary>
+    /// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
+    /// will not have any effect on what is returned by subsequent calls to <see cref="::buffer_size method"
+    /// />.</remarks>
+    virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
+
+    /// <summary>
+    /// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
+    /// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
+    /// incurring the overhead of using tasks.
+    /// </summary>
+    virtual size_t in_avail() const
+    {
+        // See the comment in seek around the restriction that we do not allow read head to
+        // seek beyond the current write_end.
+        _ASSERTE(m_current_position <= m_data.size());
+
+        msl::safeint3::SafeInt<size_t> readhead(m_current_position);
+        msl::safeint3::SafeInt<size_t> writeend(m_data.size());
+        return (size_t)(writeend - readhead);
+    }
+
+    virtual pplx::task<bool> _sync() { return pplx::task_from_result(true); }
+
+    virtual pplx::task<int_type> _putc(_CharType ch)
+    {
+        int_type retVal = (this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof();
+        return pplx::task_from_result<int_type>(retVal);
+    }
+
+    virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
+    {
+        return pplx::task_from_result<size_t>(this->write(ptr, count));
+    }
+
+    /// <summary>
+    /// Allocates a contiguous memory block and returns it.
+    /// </summary>
+    /// <param name="count">The number of characters to allocate.</param>
+    /// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
+    /// alloc/commit.</returns>
+    _CharType* _alloc(size_t count)
+    {
+        if (!this->can_write()) return nullptr;
+
+        // Allocate space
+        resize_for_write(m_current_position + count);
+
+        // Let the caller copy the data
+        return (_CharType*)&m_data[m_current_position];
+    }
+
+    /// <summary>
+    /// Submits a block already allocated by the stream buffer.
+    /// </summary>
+    /// <param name="count">The number of characters to be committed.</param>
+    void _commit(size_t actual)
+    {
+        // Update the write position and satisfy any pending reads
+        update_current_position(m_current_position + actual);
+    }
+
+    /// <summary>
+    /// Gets a pointer to the next already allocated contiguous block of data.
+    /// </summary>
+    /// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
+    /// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
+    /// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
+    /// <remarks>
+    /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
+    /// there is no block to return immediately or that the stream buffer does not support the operation.
+    /// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
+    /// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
+    /// a subsequent read will not succeed.
+    /// </remarks>
+    virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
+    {
+        ptr = nullptr;
+        count = 0;
+
+        if (!this->can_read()) return false;
+
+        count = in_avail();
+
+        if (count > 0)
+        {
+            ptr = (_CharType*)&m_data[m_current_position];
+            return true;
+        }
+        else
+        {
+            // Can only be open for read OR write, not both. If there is no data then
+            // we have reached the end of the stream so indicate such with true.
+            return true;
+        }
+    }
+
+    /// <summary>
+    /// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
+    /// de-allocate the memory, if it so desires. Move the read position ahead by the count.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be released.</param>
+    /// <param name="count">The number of characters that were read.</param>
+    virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
+    {
+        if (ptr != nullptr) update_current_position(m_current_position + count);
+    }
+
+    virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        return pplx::task_from_result(this->read(ptr, count));
+    }
+
+    size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return this->read(ptr, count); }
+
+    virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        return this->read(ptr, count, false);
+    }
+
+    virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result(this->read_byte(true)); }
+
+    virtual int_type _sbumpc() { return this->read_byte(true); }
+
+    virtual pplx::task<int_type> _getc() { return pplx::task_from_result(this->read_byte(false)); }
+
+    int_type _sgetc() { return this->read_byte(false); }
+
+    virtual pplx::task<int_type> _nextc()
+    {
+        this->read_byte(true);
+        return pplx::task_from_result(this->read_byte(false));
+    }
+
+    virtual pplx::task<int_type> _ungetc()
+    {
+        auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in);
+        if (pos == (pos_type)traits::eof()) return pplx::task_from_result(traits::eof());
+        return this->getc();
+    }
+
+    /// <summary>
+    /// Gets the current read or write position in the stream.
+    /// </summary>
+    /// <param name="direction">The I/O direction to seek (see remarks)</param>
+    /// <returns>The current position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the direction parameter defines whether to move the read or the write
+    ///          cursor.</remarks>
+    virtual pos_type getpos(std::ios_base::openmode mode) const
+    {
+        if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
+            return static_cast<pos_type>(traits::eof());
+
+        return static_cast<pos_type>(m_current_position);
+    }
+
+    /// <summary>
+    /// Seeks to the given position.
+    /// </summary>
+    /// <param name="pos">The offset from the beginning of the stream.</param>
+    /// <param name="direction">The I/O direction to seek (see remarks).</param>
+    /// <returns>The position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors. For such streams, the direction parameter
+    /// defines whether to move the read or the write cursor.</remarks>
+    virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode)
+    {
+        pos_type beg(0);
+
+        // In order to support relative seeking from the end position we need to fix an end position.
+        // Technically, there is no end for the stream buffer as new writes would just expand the buffer.
+        // For now, we assume that the current write_end is the end of the buffer. We use this artificial
+        // end to restrict the read head from seeking beyond what is available.
+
+        pos_type end(m_data.size());
+
+        if (position >= beg)
+        {
+            auto pos = static_cast<size_t>(position);
+
+            // Read head
+            if ((mode & std::ios_base::in) && this->can_read())
+            {
+                if (position <= end)
+                {
+                    // We do not allow reads to seek beyond the end or before the start position.
+                    update_current_position(pos);
+                    return static_cast<pos_type>(m_current_position);
+                }
+            }
+
+            // Write head
+            if ((mode & std::ios_base::out) && this->can_write())
+            {
+                // Allocate space
+                resize_for_write(pos);
+
+                // Nothing to really copy
+
+                // Update write head and satisfy read requests if any
+                update_current_position(pos);
+
+                return static_cast<pos_type>(m_current_position);
+            }
+        }
+
+        return static_cast<pos_type>(traits::eof());
+    }
+
+    /// <summary>
+    /// Seeks to a position given by a relative offset.
+    /// </summary>
+    /// <param name="offset">The relative position to seek to</param>
+    /// <param name="way">The starting point (beginning, end, current) for the seek.</param>
+    /// <param name="mode">The I/O direction to seek (see remarks)</param>
+    /// <returns>The position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
+    virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
+    {
+        pos_type beg = 0;
+        pos_type cur = static_cast<pos_type>(m_current_position);
+        pos_type end = static_cast<pos_type>(m_data.size());
+
+        switch (way)
+        {
+            case std::ios_base::beg: return seekpos(beg + offset, mode);
+
+            case std::ios_base::cur: return seekpos(cur + offset, mode);
+
+            case std::ios_base::end: return seekpos(end + offset, mode);
+
+            default: return static_cast<pos_type>(traits::eof());
+        }
+    }
+
+private:
+    template<typename _CollectionType1>
+    friend class streams::container_buffer;
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    basic_container_buffer(std::ios_base::openmode mode)
+        : streambuf_state_manager<typename _CollectionType::value_type>(mode), m_current_position(0)
+    {
+        validate_mode(mode);
+    }
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    basic_container_buffer(_CollectionType data, std::ios_base::openmode mode)
+        : streambuf_state_manager<typename _CollectionType::value_type>(mode)
+        , m_data(std::move(data))
+        , m_current_position((mode & std::ios_base::in) ? 0 : m_data.size())
+    {
+        validate_mode(mode);
+    }
+
+    static void validate_mode(std::ios_base::openmode mode)
+    {
+        // Disallow simultaneous use of the stream buffer for writing and reading.
+        if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
+            throw std::invalid_argument("this combination of modes on container stream not supported");
+    }
+
+    /// <summary>
+    /// Determine if the request can be satisfied.
+    /// </summary>
+    bool can_satisfy(size_t)
+    {
+        // We can always satisfy a read, at least partially, unless the
+        // read position is at the very end of the buffer.
+        return (in_avail() > 0);
+    }
+
+    /// <summary>
+    /// Reads a byte from the stream and returns it as int_type.
+    /// Note: This routine shall only be called if can_satisfy() returned true.
+    /// </summary>
+    int_type read_byte(bool advance = true)
+    {
+        _CharType value;
+        auto read_size = this->read(&value, 1, advance);
+        return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
+    }
+
+    /// <summary>
+    /// Reads up to count characters into ptr and returns the count of characters copied.
+    /// The return value (actual characters copied) could be <= count.
+    /// Note: This routine shall only be called if can_satisfy() returned true.
+    /// </summary>
+    size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
+    {
+        if (!can_satisfy(count)) return 0;
+
+        msl::safeint3::SafeInt<size_t> request_size(count);
+        msl::safeint3::SafeInt<size_t> read_size = request_size.Min(in_avail());
+
+        size_t newPos = m_current_position + read_size;
+
+        auto readBegin = std::begin(m_data) + m_current_position;
+        auto readEnd = std::begin(m_data) + newPos;
+
+#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
+        // Avoid warning C4996: Use checked iterators under SECURE_SCL
+        std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType*>(ptr, count));
+#else
+        std::copy(readBegin, readEnd, ptr);
+#endif // _WIN32
+
+        if (advance)
+        {
+            update_current_position(newPos);
+        }
+
+        return (size_t)read_size;
+    }
+
+    /// <summary>
+    /// Write count characters from the ptr into the stream buffer
+    /// </summary>
+    size_t write(const _CharType* ptr, size_t count)
+    {
+        if (!this->can_write() || (count == 0)) return 0;
+
+        auto newSize = m_current_position + count;
+
+        // Allocate space
+        resize_for_write(newSize);
+
+        // Copy the data
+        std::copy(ptr, ptr + count, std::begin(m_data) + m_current_position);
+
+        // Update write head and satisfy pending reads if any
+        update_current_position(newSize);
+
+        return count;
+    }
+
+    /// <summary>
+    /// Resize the underlying container to match the new write head
+    /// </summary>
+    void resize_for_write(size_t newPos)
+    {
+        // Resize the container if required
+        if (newPos > m_data.size())
+        {
+            m_data.resize(newPos);
+        }
+    }
+
+    /// <summary>
+    /// Updates the write head to the new position
+    /// </summary>
+    void update_current_position(size_t newPos)
+    {
+        // The new write head
+        m_current_position = newPos;
+        _ASSERTE(m_current_position <= m_data.size());
+    }
+
+    // The actual data store
+    _CollectionType m_data;
+
+    // Read/write head
+    size_t m_current_position;
+};
+
+} // namespace details
+
+/// <summary>
+/// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading
+/// sequences of characters. Note that it cannot be used as a consumer producer buffer.
+/// </summary>
+/// <typeparam name="_CollectionType">
+/// The type of the container.
+/// </typeparam>
+/// <remarks>
+/// This is a reference-counted version of <c>basic_container_buffer</c>.
+/// </remarks>
+template<typename _CollectionType>
+class container_buffer : public streambuf<typename _CollectionType::value_type>
+{
+public:
+    typedef typename _CollectionType::value_type char_type;
+
+    /// <summary>
+    /// Creates a container_buffer given a collection, copying its data into the buffer.
+    /// </summary>
+    /// <param name="data">The collection that is the starting point for the buffer</param>
+    /// <param name="mode">The I/O mode that the buffer should use (in / out)</param>
+    container_buffer(_CollectionType data, std::ios_base::openmode mode = std::ios_base::in)
+        : streambuf<typename _CollectionType::value_type>(
+              std::shared_ptr<details::basic_container_buffer<_CollectionType>>(
+                  new streams::details::basic_container_buffer<_CollectionType>(std::move(data), mode)))
+    {
+    }
+
+    /// <summary>
+    /// Creates a container_buffer starting from an empty collection.
+    /// </summary>
+    /// <param name="mode">The I/O mode that the buffer should use (in / out)</param>
+    container_buffer(std::ios_base::openmode mode = std::ios_base::out)
+        : streambuf<typename _CollectionType::value_type>(
+              std::shared_ptr<details::basic_container_buffer<_CollectionType>>(
+                  new details::basic_container_buffer<_CollectionType>(mode)))
+    {
+    }
+
+    _CollectionType& collection() const
+    {
+        auto listBuf = static_cast<details::basic_container_buffer<_CollectionType>*>(this->get_base().get());
+        return listBuf->collection();
+    }
+};
+
+/// <summary>
+/// A static class to allow users to create input and out streams based off STL
+/// collections. The sole purpose of this class to avoid users from having to know
+/// anything about stream buffers.
+/// </summary>
+/// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
+template<typename _CollectionType>
+class container_stream
+{
+public:
+    typedef typename _CollectionType::value_type char_type;
+    typedef container_buffer<_CollectionType> buffer_type;
+
+    /// <summary>
+    /// Creates an input stream given an STL container.
+    /// </summary>
+    /// </param name="data">STL container to back the input stream.</param>
+    /// <returns>An input stream.</returns>
+    static concurrency::streams::basic_istream<char_type> open_istream(_CollectionType data)
+    {
+        return concurrency::streams::basic_istream<char_type>(buffer_type(std::move(data), std::ios_base::in));
+    }
+
+    /// <summary>
+    /// Creates an output stream using an STL container as the storage.
+    /// </summary>
+    /// <returns>An output stream.</returns>
+    static concurrency::streams::basic_ostream<char_type> open_ostream()
+    {
+        return concurrency::streams::basic_ostream<char_type>(buffer_type(std::ios_base::out));
+    }
+};
+
+/// <summary>
+/// The stringstream allows an input stream to be constructed from std::string or std::wstring
+/// For output streams the underlying string container could be retrieved using <c>buf-&gt;collection().</c>
+/// </summary>
+typedef container_stream<std::basic_string<char>> stringstream;
+typedef stringstream::buffer_type stringstreambuf;
+
+typedef container_stream<utility::string_t> wstringstream;
+typedef wstringstream::buffer_type wstringstreambuf;
+
+/// <summary>
+/// The <c>bytestream</c> is a static class that allows an input stream to be constructed from any STL container.
+/// </summary>
+class bytestream
+{
+public:
+    /// <summary>
+    /// Creates a single byte character input stream given an STL container.
+    /// </summary>
+    /// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
+    /// <param name="data">STL container to back the input stream.</param>
+    /// <returns>An single byte character input stream.</returns>
+    template<typename _CollectionType>
+    static concurrency::streams::istream open_istream(_CollectionType data)
+    {
+        return concurrency::streams::istream(
+            streams::container_buffer<_CollectionType>(std::move(data), std::ios_base::in));
+    }
+
+    /// <summary>
+    /// Creates a single byte character output stream using an STL container as storage.
+    /// </summary>
+    /// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
+    /// <returns>A single byte character output stream.</returns>
+    template<typename _CollectionType>
+    static concurrency::streams::ostream open_ostream()
+    {
+        return concurrency::streams::ostream(streams::container_buffer<_CollectionType>());
+    }
+};
+
+} // namespace streams
+} // namespace Concurrency
diff --git a/Release/include/cpprest/details/SafeInt3.hpp b/Release/include/cpprest/details/SafeInt3.hpp
new file mode 100644 (file)
index 0000000..950ac80
--- /dev/null
@@ -0,0 +1,7484 @@
+/*-----------------------------------------------------------------------------------------------------------
+SafeInt.hpp
+Version 3.0.18p
+
+This software is licensed under the Microsoft Public License (Ms-PL).
+For more information about Microsoft open source licenses, refer to
+http://www.microsoft.com/opensource/licenses.mspx
+
+This license governs use of the accompanying software. If you use the software, you accept this license.
+If you do not accept the license, do not use the software.
+
+Definitions
+The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here
+as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to
+the software. A "contributor" is any person that distributes its contribution under this license.
+"Licensed patents" are a contributor's patent claims that read directly on its contribution.
+
+Grant of Rights
+(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations
+in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to
+reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution
+or any derivative works that you create.
+
+(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in
+section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed
+patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution
+in the software or derivative works of the contribution in the software.
+
+Conditions and Limitations
+(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo,
+    or trademarks.
+(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the
+    software, your patent license from such contributor to the software ends automatically.
+(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and
+    attribution notices that are present in the software.
+(D) If you distribute any portion of the software in source code form, you may do so only under this license
+    by including a complete copy of this license with your distribution. If you distribute any portion of the
+    software in compiled or object code form, you may only do so under a license that complies with this license.
+(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties,
+    guarantees, or conditions. You may have additional consumer rights under your local laws which this license
+    cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties
+    of merchantability, fitness for a particular purpose and non-infringement.
+
+
+Copyright (c) Microsoft Corporation.  All rights reserved.
+
+This header implements an integer handling class designed to catch
+unsafe integer operations
+
+This header compiles properly at Wall on Visual Studio, -Wall on gcc, and -Weverything on clang.
+
+Please read the leading comments before using the class.
+---------------------------------------------------------------*/
+#pragma once
+
+// It is a bit tricky to sort out what compiler we are actually using,
+// do this once here, and avoid cluttering the code
+#define VISUAL_STUDIO_COMPILER 0
+#define CLANG_COMPILER 1
+#define GCC_COMPILER 2
+#define UNKNOWN_COMPILER -1
+
+// Clang will sometimes pretend to be Visual Studio
+// and does pretend to be gcc. Check it first, as nothing else pretends to be clang
+#if defined __clang__
+#define SAFEINT_COMPILER CLANG_COMPILER
+#elif defined __GNUC__
+#define SAFEINT_COMPILER GCC_COMPILER
+#elif defined _MSC_VER
+#define SAFEINT_COMPILER VISUAL_STUDIO_COMPILER
+#else
+#define SAFEINT_COMPILER UNKNOWN_COMPILER
+#endif
+
+// Enable compiling with /Wall under VC
+#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
+#pragma warning(push)
+// Disable warnings coming from headers
+#pragma warning(disable : 4987 4820 4987 4820)
+
+#endif
+
+// Need this for ptrdiff_t on some compilers
+#include <cstddef>
+#include <cstdlib>
+
+#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER && defined _M_AMD64
+#include <intrin.h>
+#define SAFEINT_USE_INTRINSICS 1
+#else
+#define SAFEINT_USE_INTRINSICS 0
+#endif
+
+#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
+#pragma warning(pop)
+#endif
+
+// Various things needed for GCC
+#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER
+
+#define NEEDS_INT_DEFINED
+
+#if !defined NULL
+#define NULL 0
+#endif
+
+// GCC warning suppression
+#if SAFEINT_COMPILER == GCC_COMPILER
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+#endif
+
+#include <stdint.h>
+
+// clang only
+#if SAFEINT_COMPILER == CLANG_COMPILER
+
+#if __has_feature(cxx_nullptr)
+#define NEEDS_NULLPTR_DEFINED 0
+#endif
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#pragma clang diagnostic ignored "-Wold-style-cast"
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+#endif
+
+#endif
+
+// If the user made a choice, respect it #if !defined
+#if !defined NEEDS_NULLPTR_DEFINED
+// Visual Studio 2010 and higher support this
+#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
+#if (_MSC_VER < 1600)
+#define NEEDS_NULLPTR_DEFINED 1
+#else
+#define NEEDS_NULLPTR_DEFINED 0
+#endif
+#else
+// Let everything else trigger based on whether we use c++11 or above
+#if __cplusplus >= 201103L
+#define NEEDS_NULLPTR_DEFINED 0
+#else
+#define NEEDS_NULLPTR_DEFINED 1
+#endif
+#endif
+#endif
+
+#if NEEDS_NULLPTR_DEFINED
+#define nullptr NULL
+#endif
+
+#ifndef C_ASSERT
+#define C_ASSERT_DEFINED_SAFEINT
+#define C_ASSERT(e) typedef char __C_ASSERT__[(e) ? 1 : -1]
+#endif
+
+// Let's test some assumptions
+// We're assuming two's complement negative numbers
+C_ASSERT(-1 == static_cast<int>(0xffffffff));
+
+/************* Compiler Options
+*****************************************************************************************************
+
+SafeInt supports several compile-time options that can change the behavior of the class.
+
+Compiler options:
+SAFEINT_WARN_64BIT_PORTABILITY     - this re-enables various warnings that happen when /Wp64 is used. Enabling this
+option is not recommended. NEEDS_INT_DEFINED                  - if your compiler does not support __int8, __int16,
+__int32 and __int64, you can enable this. SAFEINT_ASSERT_ON_EXCEPTION        - it is often easier to stop on an assert
+and figure out a problem than to try and figure out how you landed in the catch block. SafeIntDefaultExceptionHandler -
+if you'd like to replace the exception handlers SafeInt provides, define your replacement and define this. Note - two
+built in (Windows-specific) options exist:
+                                     - SAFEINT_FAILFAST - bypasses all exception handlers, exits the app with an
+exception
+                                     - SAFEINT_RAISE_EXCEPTION - throws Win32 exceptions, which can be caught
+SAFEINT_DISALLOW_UNSIGNED_NEGATION - Invoking the unary negation operator creates warnings, but if you'd like it to
+completely fail to compile, define this. ANSI_CONVERSIONS                   - This changes the class to use default
+comparison behavior, which may be unsafe. Enabling this option is not recommended. SAFEINT_DISABLE_BINARY_ASSERT      -
+binary AND, OR or XOR operations on mixed size types can produce unexpected results. If you do this, the default is to
+assert. Set this if you prefer not to assert under these conditions. SIZE_T_CAST_NEEDED                 - some compilers
+complain if there is not a cast to size_t, others complain if there is one. This lets you not have your compiler
+complain. SAFEINT_DISABLE_SHIFT_ASSERT       - Set this option if you don't want to assert when shifting more bits than
+the type has. Enabling this option is not recommended.
+
+************************************************************************************************************************************/
+
+/*
+*  The SafeInt class is designed to have as low an overhead as possible
+*  while still ensuring that all integer operations are conducted safely.
+*  Nearly every operator has been overloaded, with a very few exceptions.
+*
+*  A usability-safety trade-off has been made to help ensure safety. This
+*  requires that every operation return either a SafeInt or a bool. If we
+*  allowed an operator to return a base integer type T, then the following
+*  can happen:
+*
+*  char i = SafeInt<char>(32) * 2 + SafeInt<char>(16) * 4;
+*
+*  The * operators take precedence, get overloaded, return a char, and then
+*  you have:
+*
+*  char i = (char)64 + (char)64; //overflow!
+*
+*  This situation would mean that safety would depend on usage, which isn't
+*  acceptable.
+*
+*  One key operator that is missing is an implicit cast to type T. The reason for
+*  this is that if there is an implicit cast operator, then we end up with
+*  an ambiguous compile-time precedence. Because of this amiguity, there
+*  are two methods that are provided:
+*
+*  Casting operators for every native integer type
+*  Version 3 note - it now compiles correctly for size_t without warnings
+*
+*  SafeInt::Ptr()   - returns the address of the internal integer
+*  Note - the '&' (address of) operator has been overloaded and returns
+*         the address of the internal integer.
+*
+*  The SafeInt class should be used in any circumstances where ensuring
+*  integrity of the calculations is more important than performance. See Performance
+*  Notes below for additional information.
+*
+*  Many of the conditionals will optimize out or be inlined for a release
+*  build (especially with /Ox), but it does have significantly more overhead,
+*  especially for signed numbers. If you do not _require_ negative numbers, use
+*  unsigned integer types - certain types of problems cannot occur, and this class
+*  performs most efficiently.
+*
+*  Here's an example of when the class should ideally be used -
+*
+*  void* AllocateMemForStructs(int StructSize, int HowMany)
+*  {
+*     SafeInt<unsigned long> s(StructSize);
+*
+*     s *= HowMany;
+*
+*     return malloc(s);
+*
+*  }
+*
+*  Here's when it should NOT be used:
+*
+*  void foo()
+*  {
+*    int i;
+*
+*    for(i = 0; i < 0xffff; i++)
+*      ....
+*  }
+*
+*  Error handling - a SafeInt class will throw exceptions if something
+*  objectionable happens. The exceptions are SafeIntException classes,
+*  which contain an enum as a code.
+*
+*  Typical usage might be:
+*
+*  bool foo()
+*  {
+*    SafeInt<unsigned long> s; //note that s == 0 unless set
+*
+*    try{
+*      s *= 23;
+*      ....
+*    }
+*    catch(SafeIntException err)
+*    {
+*       //handle errors here
+*    }
+*  }
+*
+*  Update for 3.0 - the exception class is now a template parameter.
+*  You can replace the exception class with any exception class you like. This is accomplished by:
+*  1) Create a class that has the following interface:
+*
+    template <> class YourSafeIntExceptionHandler < YourException >
+    {
+    public:
+        static __declspec(noreturn) void __stdcall SafeIntOnOverflow()
+        {
+            throw YourException( YourSafeIntArithmeticOverflowError );
+        }
+
+        static __declspec(noreturn) void __stdcall SafeIntOnDivZero()
+        {
+            throw YourException( YourSafeIntDivideByZeroError );
+        }
+    };
+*
+*  Note that you don't have to throw C++ exceptions, you can throw Win32 exceptions, or do
+*  anything you like, just don't return from the call back into the code.
+*
+*  2) Either explicitly declare SafeInts like so:
+*     SafeInt< int, YourSafeIntExceptionHandler > si;
+*  or
+*     #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler
+*
+*  Performance:
+*
+*  Due to the highly nested nature of this class, you can expect relatively poor
+*  performance in unoptimized code. In tests of optimized code vs. correct inline checks
+*  in native code, this class has been found to take approximately 8% more CPU time (this varies),
+*  most of which is due to exception handling. Solutions:
+*
+*  1) Compile optimized code - /Ox is best, /O2 also performs well. Interestingly, /O1
+*     (optimize for size) does not work as well.
+*  2) If that 8% hit is really a serious problem, walk through the code and inline the
+*     exact same checks as the class uses.
+*  3) Some operations are more difficult than others - avoid using signed integers, and if
+*     possible keep them all the same size. 64-bit integers are also expensive. Mixing
+*     different integer sizes and types may prove expensive. Be aware that literals are
+*     actually ints. For best performance, cast literals to the type desired.
+*
+*
+*  Performance update
+*  The current version of SafeInt uses template specialization to force the compiler to invoke only the
+*  operator implementation needed for any given pair of types. This will dramatically improve the perf
+*  of debug builds.
+*
+*  3.0 update - not only have we maintained the specialization, there were some cases that were overly complex,
+*  and using some additional cases (e.g. signed __int64 and unsigned __int64) resulted in some simplification.
+*  Additionally, there was a lot of work done to better optimize the 64-bit multiplication.
+*
+*  Binary Operators
+*
+*  All of the binary operators have certain assumptions built into the class design.
+*  This is to ensure correctness. Notes on each class of operator follow:
+*
+*  Arithmetic Operators (*,/,+,-,%)
+*  There are three possible variants:
+*  SafeInt< T, E > op SafeInt< T, E >
+*  SafeInt< T, E > op U
+*  U op SafeInt< T, E >
+*
+*  The SafeInt< T, E > op SafeInt< U, E > variant is explicitly not supported, and if you try to do
+*  this the compiler with throw the following error:
+*
+*  error C2593: 'operator *' is ambiguous
+*
+*  This is because the arithmetic operators are required to return a SafeInt of some type.
+*  The compiler cannot know whether you'd prefer to get a type T or a type U returned. If
+*  you need to do this, you need to extract the value contained within one of the two using
+*  the casting operator. For example:
+*
+*  SafeInt< T, E > t, result;
+*  SafeInt< U, E > u;
+*
+*  result = t * (U)u;
+*
+*  Comparison Operators
+*  Because each of these operators return type bool, mixing SafeInts of differing types is
+*  allowed.
+*
+*  Shift Operators
+*  Shift operators always return the type on the left hand side of the operator. Mixed type
+*  operations are allowed because the return type is always known.
+*
+*  Boolean Operators
+*  Like comparison operators, these overloads always return type bool, and mixed-type SafeInts
+*  are allowed. Additionally, specific overloads exist for type bool on both sides of the
+*  operator.
+*
+*  Binary Operators
+*  Mixed-type operations are discouraged, however some provision has been made in order to
+*  enable things like:
+*
+*  SafeInt<char> c = 2;
+*
+*  if(c & 0x02)
+*    ...
+*
+*  The "0x02" is actually an int, and it needs to work.
+*  In the case of binary operations on integers smaller than 32-bit, or of mixed type, corner
+*  cases do exist where you could get unexpected results. In any case where SafeInt returns a different
+*  result than the underlying operator, it will call assert(). You should examine your code and cast things
+*  properly so that you are not programming with side effects.
+*
+*  Documented issues:
+*
+*  This header compiles correctly at /W4 using VC++ 8 (Version 14.00.50727.42) and later.
+*  As of this writing, I believe it will also work for VC 7.1, but not for VC 7.0 or below.
+*  If you need a version that will work with lower level compilers, try version 1.0.7. None
+*  of them work with Visual C++ 6, and gcc didn't work very well, either, though this hasn't
+*  been tried recently.
+*
+*  It is strongly recommended that any code doing integer manipulation be compiled at /W4
+*  - there are a number of warnings which pertain to integer manipulation enabled that are
+*  not enabled at /W3 (default for VC++)
+*
+*  Perf note - postfix operators are slightly more costly than prefix operators.
+*  Unless you're actually assigning it to something, ++SafeInt is less expensive than SafeInt++
+*
+*  The comparison operator behavior in this class varies from the ANSI definition, which is
+*  arguably broken. As an example, consider the following:
+*
+*  unsigned int l = 0xffffffff;
+*  char c = -1;
+*
+*  if(c == l)
+*    printf("Why is -1 equal to 4 billion???\n");
+*
+*  The problem here is that c gets cast to an int, now has a value of 0xffffffff, and then gets
+*  cast again to an unsigned int, losing the true value. This behavior is despite the fact that
+*  an __int64 exists, and the following code will yield a different (and intuitively correct)
+*  answer:
+*
+*  if((__int64)c == (__int64)l))
+*    printf("Why is -1 equal to 4 billion???\n");
+*  else
+*    printf("Why doesn't the compiler upcast to 64-bits when needed?\n");
+*
+*  Note that combinations with smaller integers won't display the problem - if you
+*  changed "unsigned int" above to "unsigned short", you'd get the right answer.
+*
+*  If you prefer to retain the ANSI standard behavior insert
+*  #define ANSI_CONVERSIONS
+*  into your source. Behavior differences occur in the following cases:
+*  8, 16, and 32-bit signed int, unsigned 32-bit int
+*  any signed int, unsigned 64-bit int
+*  Note - the signed int must be negative to show the problem
+*
+*
+*  Revision history:
+*
+*  Oct 12, 2003 - Created
+*  Author - David LeBlanc - dleblanc@microsoft.com
+*
+*  Oct 27, 2003 - fixed numerous items pointed out by michmarc and bdawson
+*  Dec 28, 2003 - 1.0
+*                 added support for mixed-type operations
+*                 thanks to vikramh
+*                 also fixed broken __int64 multiplication section
+*                 added extended support for mixed-type operations where possible
+*  Jan 28, 2004 - 1.0.1
+*                 changed WCHAR to wchar_t
+*                 fixed a construct in two mixed-type assignment overloads that was
+*                 not compiling on some compilers
+*                 Also changed name of private method to comply with standards on
+*                 reserved names
+*                 Thanks to Niels Dekker for the input
+*  Feb 12, 2004 - 1.0.2
+*                 Minor changes to remove dependency on Windows headers
+*                 Consistently used __int16, __int32 and __int64 to ensure
+*                 portability
+*  May 10, 2004 - 1.0.3
+*                 Corrected bug in one case of GreaterThan
+*  July 22, 2004 - 1.0.4
+*                 Tightened logic in addition check (saving 2 instructions)
+*                 Pulled error handler out into function to enable user-defined replacement
+*                 Made internal type of SafeIntException an enum (as per Niels' suggestion)
+*                 Added casts for base integer types (as per Scott Meyers' suggestion)
+*                 Updated usage information - see important new perf notes.
+*                 Cleaned up several const issues (more thanks to Niels)
+*
+*  Oct 1, 2004 - 1.0.5
+*                 Added support for SEH exceptions instead of C++ exceptions - Win32 only
+*                 Made handlers for DIV0 and overflows individually overridable
+*                 Commented out the destructor - major perf gains here
+*                 Added cast operator for type long, since long != __int32
+*                  Corrected a couple of missing const modifiers
+*                 Fixed broken >= and <= operators for type U op SafeInt< T, E >
+*  Nov 5, 2004 - 1.0.6
+*                 Implemented new logic in binary operators to resolve issues with
+*                 implicit casts
+*                 Fixed casting operator because char != signed char
+*                 Defined __int32 as int instead of long
+*                 Removed unsafe SafeInt::Value method
+*                 Re-implemented casting operator as a result of removing Value method
+*  Dec 1, 2004 - 1.0.7
+*                 Implemented specialized operators for pointer arithmetic
+*                 Created overloads for cases of U op= SafeInt. What you do with U
+*                 after that may be dangerous.
+*                 Fixed bug in corner case of MixedSizeModulus
+*                 Fixed bug in MixedSizeMultiply and MixedSizeDivision with input of 0
+*                 Added throw() decorations
+*
+*  Apr 12, 2005 - 2.0
+*                 Extensive revisions to leverage template specialization.
+*  April, 2007    Extensive revisions for version 3.0
+*  Nov 22, 2009   Forked from MS internal code
+*                 Changes needed to support gcc compiler - many thanks to Niels Dekker
+*                 for determining not just the issues, but also suggesting fixes.
+*                 Also updating some of the header internals to be the same as the upcoming Visual Studio version.
+*
+*  Jan 16, 2010   64-bit gcc has long == __int64, which means that many of the existing 64-bit
+*                 templates are over-specialized. This forces a redefinition of all the 64-bit
+*                 multiplication routines to use pointers instead of references for return
+*                 values. Also, let's use some intrinsics for x64 Microsoft compiler to
+*                 reduce code size, and hopefully improve efficiency.
+*
+*  June 21, 2014  Better support for clang, higher warning levels supported for all 3 primary supported
+                  compilers (Visual Studio, clang, gcc).
+                  Also started to converge the code base such that the public CodePlex version will
+                  be a drop-in replacement for the Visual Studio version.
+
+*  Note about code style - throughout this class, casts will be written using C-style (T),
+*  not C++ style static_cast< T >. This is because the class is nearly always dealing with integer
+*  types, and in this case static_cast and a C cast are equivalent. Given the large number of casts,
+*  the code is a little more readable this way. In the event a cast is needed where static_cast couldn't
+*  be substituted, we'll use the new templatized cast to make it explicit what the operation is doing.
+*
+************************************************************************************************************
+* Version 3.0 changes:
+*
+* 1) The exception type thrown is now replacable, and you can throw your own exception types. This should help
+*    those using well-developed exception classes.
+* 2) The 64-bit multiplication code has had a lot of perf work done, and should be faster than 2.0.
+* 3) There is now limited floating point support. You can initialize a SafeInt with a floating point type,
+*    and you can cast it out (or assign) to a float as well.
+* 4) There is now an Align method. I noticed people use this a lot, and rarely check errors, so now you have one.
+*
+* Another major improvement is the addition of external functions - if you just want to check an operation, this can now
+happen:
+* All of the following can be invoked without dealing with creating a class, or managing exceptions. This is especially
+handy
+* for 64-bit porting, since SafeCast compiles away for a 32-bit cast from size_t to unsigned long, but checks it for
+64-bit.
+*
+* inline bool SafeCast( const T From, U& To ) throw()
+* inline bool SafeEquals( const T t, const U u ) throw()
+* inline bool SafeNotEquals( const T t, const U u ) throw()
+* inline bool SafeGreaterThan( const T t, const U u ) throw()
+* inline bool SafeGreaterThanEquals( const T t, const U u ) throw()
+* inline bool SafeLessThan( const T t, const U u ) throw()
+* inline bool SafeLessThanEquals( const T t, const U u ) throw()
+* inline bool SafeModulus( const T& t, const U& u, T& result ) throw()
+* inline bool SafeMultiply( T t, U u, T& result ) throw()
+* inline bool SafeDivide( T t, U u, T& result ) throw()
+* inline bool SafeAdd( T t, U u, T& result ) throw()
+* inline bool SafeSubtract( T t, U u, T& result ) throw()
+*
+*/
+
+// use these if the compiler does not support _intXX
+#ifdef NEEDS_INT_DEFINED
+#define __int8 char
+#define __int16 short
+#define __int32 int
+#define __int64 long long
+#endif
+
+namespace msl
+{
+namespace safeint3
+{
+// catch these to handle errors
+// Currently implemented code values:
+// ERROR_ARITHMETIC_OVERFLOW
+// EXCEPTION_INT_DIVIDE_BY_ZERO
+enum SafeIntError
+{
+    SafeIntNoError = 0,
+    SafeIntArithmeticOverflow,
+    SafeIntDivideByZero
+};
+
+} // namespace safeint3
+} // namespace msl
+
+/*
+ * Error handler classes
+ * Using classes to deal with exceptions is going to allow the most
+ * flexibility, and we can mix different error handlers in the same project
+ * or even the same file. It isn't advisable to do this in the same function
+ * because a SafeInt< int, MyExceptionHandler > isn't the same thing as
+ * SafeInt< int, YourExceptionHander >.
+ * If for some reason you have to translate between the two, cast one of them back to its
+ * native type.
+ *
+ * To use your own exception class with SafeInt, first create your exception class,
+ * which may look something like the SafeIntException class below. The second step is to
+ * create a template specialization that implements SafeIntOnOverflow and SafeIntOnDivZero.
+ * For example:
+ *
+ * template <> class SafeIntExceptionHandler < YourExceptionClass >
+ * {
+ *     static __declspec(noreturn) void __stdcall SafeIntOnOverflow()
+ *     {
+ *         throw YourExceptionClass( EXCEPTION_INT_OVERFLOW );
+ *     }
+ *
+ *     static __declspec(noreturn) void __stdcall SafeIntOnDivZero()
+ *     {
+ *         throw YourExceptionClass( EXCEPTION_INT_DIVIDE_BY_ZERO );
+ *     }
+ * };
+ *
+ * typedef SafeIntExceptionHandler < YourExceptionClass > YourSafeIntExceptionHandler
+ * You'd then declare your SafeInt objects like this:
+ * SafeInt< int, YourSafeIntExceptionHandler >
+ *
+ * Unfortunately, there is no such thing as partial template specialization in typedef
+ * statements, so you have three options if you find this cumbersome:
+ *
+ * 1) Create a holder class:
+ *
+ * template < typename T >
+ * class MySafeInt
+ * {
+ *   public:
+ *   SafeInt< T, MyExceptionClass> si;
+ * };
+ *
+ * You'd then declare an instance like so:
+ * MySafeInt< int > i;
+ *
+ * You'd lose handy things like initialization - it would have to be initialized as:
+ *
+ * i.si = 0;
+ *
+ * 2) You could create a typedef for every int type you deal with:
+ *
+ * typedef SafeInt< int, MyExceptionClass > MySafeInt;
+ * typedef SafeInt< char, MyExceptionClass > MySafeChar;
+ *
+ * and so on. The second approach is probably more usable, and will just drop into code
+ * better, which is the original intent of the SafeInt class.
+ *
+ * 3) If you're going to consistently use a different class to handle your exceptions,
+ *    you can override the default typedef like so:
+ *
+ *    #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler
+ *
+ *    Overall, this is probably the best approach.
+ * */
+
+// On the Microsoft compiler, violating a throw() annotation is a silent error.
+// Other compilers might turn these into exceptions, and some users may want to not have throw() enabled.
+// In addition, some error handlers may not throw C++ exceptions, which makes everything no throw.
+#if defined SAFEINT_REMOVE_NOTHROW
+#define SAFEINT_NOTHROW
+#else
+#define SAFEINT_NOTHROW throw()
+#endif
+
+namespace msl
+{
+namespace safeint3
+{
+// If you would like to use your own custom assert
+// Define SAFEINT_ASSERT
+#if !defined SAFEINT_ASSERT
+#include <assert.h>
+#define SAFEINT_ASSERT(x) assert(x)
+#endif
+
+#if defined SAFEINT_ASSERT_ON_EXCEPTION
+inline void SafeIntExceptionAssert() SAFEINT_NOTHROW { SAFEINT_ASSERT(false); }
+#else
+inline void SafeIntExceptionAssert() SAFEINT_NOTHROW {}
+#endif
+
+#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER
+#define SAFEINT_NORETURN __attribute__((noreturn))
+#define SAFEINT_STDCALL
+#define SAFEINT_VISIBLE __attribute__((__visibility__("default")))
+#define SAFEINT_WEAK __attribute__((weak))
+#else
+#define SAFEINT_NORETURN __declspec(noreturn)
+#define SAFEINT_STDCALL __stdcall
+#define SAFEINT_VISIBLE
+#define SAFEINT_WEAK
+#endif
+
+class SAFEINT_VISIBLE SafeIntException
+{
+public:
+    SafeIntException() SAFEINT_NOTHROW { m_code = SafeIntNoError; }
+    SafeIntException(SafeIntError code) SAFEINT_NOTHROW { m_code = code; }
+    SafeIntError m_code;
+};
+
+namespace SafeIntInternal
+{
+// Visual Studio version of SafeInt provides for two possible error
+// handlers:
+// SafeIntErrorPolicy_SafeIntException - C++ exception, default if not otherwise defined
+// SafeIntErrorPolicy_InvalidParameter - Calls fail fast (Windows-specific), bypasses any exception handlers,
+//                                       exits the app with a crash
+template<typename E>
+class SafeIntExceptionHandler;
+
+template<>
+class SafeIntExceptionHandler<SafeIntException>
+{
+public:
+    static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow()
+    {
+        SafeIntExceptionAssert();
+        throw SafeIntException(SafeIntArithmeticOverflow);
+    }
+
+    static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero()
+    {
+        SafeIntExceptionAssert();
+        throw SafeIntException(SafeIntDivideByZero);
+    }
+};
+
+#if !defined _CRT_SECURE_INVALID_PARAMETER
+// Calling fail fast is somewhat more robust than calling abort,
+// but abort is the closest we can manage without Visual Studio support
+// Need the header for abort()
+#include <stdlib.h>
+#define _CRT_SECURE_INVALID_PARAMETER(msg) abort()
+#endif
+
+class SafeInt_InvalidParameter
+{
+public:
+    static SAFEINT_NORETURN void SafeIntOnOverflow() SAFEINT_NOTHROW
+    {
+        SafeIntExceptionAssert();
+        _CRT_SECURE_INVALID_PARAMETER("SafeInt Arithmetic Overflow");
+    }
+
+    static SAFEINT_NORETURN void SafeIntOnDivZero() SAFEINT_NOTHROW
+    {
+        SafeIntExceptionAssert();
+        _CRT_SECURE_INVALID_PARAMETER("SafeInt Divide By Zero");
+    }
+};
+
+#if defined _WINDOWS_
+
+class SafeIntWin32ExceptionHandler
+{
+public:
+    static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() SAFEINT_NOTHROW
+    {
+        SafeIntExceptionAssert();
+        RaiseException(static_cast<DWORD>(EXCEPTION_INT_OVERFLOW), EXCEPTION_NONCONTINUABLE, 0, 0);
+    }
+
+    static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() SAFEINT_NOTHROW
+    {
+        SafeIntExceptionAssert();
+        RaiseException(static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), EXCEPTION_NONCONTINUABLE, 0, 0);
+    }
+};
+
+#endif
+
+} // namespace SafeIntInternal
+
+// both of these have cross-platform support
+typedef SafeIntInternal::SafeIntExceptionHandler<SafeIntException> CPlusPlusExceptionHandler;
+typedef SafeIntInternal::SafeInt_InvalidParameter InvalidParameterExceptionHandler;
+
+// This exception handler is no longer recommended, but is left here in order not to break existing users
+#if defined _WINDOWS_
+typedef SafeIntInternal::SafeIntWin32ExceptionHandler Win32ExceptionHandler;
+#endif
+
+// For Visual Studio compatibility
+#if defined VISUAL_STUDIO_SAFEINT_COMPAT
+typedef CPlusPlusExceptionHandler SafeIntErrorPolicy_SafeIntException;
+typedef InvalidParameterExceptionHandler SafeIntErrorPolicy_InvalidParameter;
+#endif
+
+// If the user hasn't defined a default exception handler,
+// define one now, depending on whether they would like Win32 or C++ exceptions
+
+// This library will use conditional noexcept soon, but not in this release
+// Some users might mix exception handlers, which is not advised, but is supported
+#if !defined SafeIntDefaultExceptionHandler
+#if defined SAFEINT_RAISE_EXCEPTION
+#if !defined _WINDOWS_
+#error Include windows.h in order to use Win32 exceptions
+#endif
+
+#define SafeIntDefaultExceptionHandler Win32ExceptionHandler
+#elif defined SAFEINT_FAILFAST
+#define SafeIntDefaultExceptionHandler InvalidParameterExceptionHandler
+#else
+#define SafeIntDefaultExceptionHandler CPlusPlusExceptionHandler
+#if !defined SAFEINT_EXCEPTION_HANDLER_CPP
+#define SAFEINT_EXCEPTION_HANDLER_CPP 1
+#endif
+#endif
+#endif
+
+#if !defined SAFEINT_EXCEPTION_HANDLER_CPP
+#define SAFEINT_EXCEPTION_HANDLER_CPP 0
+#endif
+
+// If an error handler is chosen other than C++ exceptions, such as Win32 exceptions, fail fast,
+// or abort, then all methods become no throw. Some teams track throw() annotations closely,
+// and the following option provides for this.
+#if SAFEINT_EXCEPTION_HANDLER_CPP
+#define SAFEINT_CPP_THROW
+#else
+#define SAFEINT_CPP_THROW SAFEINT_NOTHROW
+#endif
+
+// Turns out we can fool the compiler into not seeing compile-time constants with
+// a simple template specialization
+template<int method>
+class CompileConst;
+template<>
+class CompileConst<true>
+{
+public:
+    static bool Value() SAFEINT_NOTHROW { return true; }
+};
+template<>
+class CompileConst<false>
+{
+public:
+    static bool Value() SAFEINT_NOTHROW { return false; }
+};
+
+// The following template magic is because we're now not allowed
+// to cast a float to an enum. This means that if we happen to assign
+// an enum to a SafeInt of some type, it won't compile, unless we prevent
+//              isFloat = ( (T)( (float)1.1 ) > (T)1 )
+// from compiling in the case of an enum, which is the point of the specialization
+// that follows.
+
+// If we have support for std<typetraits>, then we can do this easily, and detect enums as well
+template<typename T>
+class NumericType;
+
+#if defined _LIBCPP_TYPE_TRAITS || defined _TYPE_TRAITS_
+// Continue to special case bool
+template<>
+class NumericType<bool>
+{
+public:
+    enum
+    {
+        isBool = true,
+        isFloat = false,
+        isInt = false
+    };
+};
+template<typename T>
+class NumericType
+{
+public:
+    enum
+    {
+        isBool = false, // We specialized out a bool
+        isFloat = std::is_floating_point<T>::value,
+        // If it is an enum, then consider it an int type
+        // This does allow someone to make a SafeInt from an enum type, which is not recommended,
+        // but it also allows someone to add an enum value to a SafeInt, which is handy.
+        isInt = std::is_integral<T>::value || std::is_enum<T>::value
+    };
+};
+
+#else
+
+template<>
+class NumericType<bool>
+{
+public:
+    enum
+    {
+        isBool = true,
+        isFloat = false,
+        isInt = false
+    };
+};
+template<>
+class NumericType<char>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+template<>
+class NumericType<unsigned char>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+template<>
+class NumericType<signed char>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+template<>
+class NumericType<short>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+template<>
+class NumericType<unsigned short>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+#if defined SAFEINT_USE_WCHAR_T || defined _NATIVE_WCHAR_T_DEFINED
+template<>
+class NumericType<wchar_t>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+#endif
+template<>
+class NumericType<int>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+template<>
+class NumericType<unsigned int>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+template<>
+class NumericType<long>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+template<>
+class NumericType<unsigned long>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+template<>
+class NumericType<__int64>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+template<>
+class NumericType<unsigned __int64>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = true
+    };
+};
+template<>
+class NumericType<float>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = true,
+        isInt = false
+    };
+};
+template<>
+class NumericType<double>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = true,
+        isInt = false
+    };
+};
+template<>
+class NumericType<long double>
+{
+public:
+    enum
+    {
+        isBool = false,
+        isFloat = true,
+        isInt = false
+    };
+};
+// Catch-all for anything not supported
+template<typename T>
+class NumericType
+{
+public:
+    // We have some unknown type, which could be an enum. For parity with the code that uses <type_traits>,
+    // We can try a static_cast<int> - it if compiles, then it might be an enum, and should work.
+    // If it is something else that just happens to have a constructor that takes an int, and a casting operator,
+    // then it is possible something will go wrong, and for best results, cast it directly to an int before letting it
+    // interact with a SafeInt
+
+    enum
+    {
+        isBool = false,
+        isFloat = false,
+        isInt = static_cast<int>(static_cast<T>(0)) == 0
+    };
+};
+#endif // type traits
+
+// Use this to avoid compile-time const truncation warnings
+template<int fSigned, int bits>
+class SafeIntMinMax;
+
+template<>
+class SafeIntMinMax<true, 8>
+{
+public:
+    const static signed __int8 min = (-0x7f - 1);
+    const static signed __int8 max = 0x7f;
+};
+template<>
+class SafeIntMinMax<true, 16>
+{
+public:
+    const static __int16 min = (-0x7fff - 1);
+    const static __int16 max = 0x7fff;
+};
+template<>
+class SafeIntMinMax<true, 32>
+{
+public:
+    const static __int32 min = (-0x7fffffff - 1);
+    const static __int32 max = 0x7fffffff;
+};
+template<>
+class SafeIntMinMax<true, 64>
+{
+public:
+    const static __int64 min = static_cast<__int64>(0x8000000000000000LL);
+    const static __int64 max = 0x7fffffffffffffffLL;
+};
+
+template<>
+class SafeIntMinMax<false, 8>
+{
+public:
+    const static unsigned __int8 min = 0;
+    const static unsigned __int8 max = 0xff;
+};
+template<>
+class SafeIntMinMax<false, 16>
+{
+public:
+    const static unsigned __int16 min = 0;
+    const static unsigned __int16 max = 0xffff;
+};
+template<>
+class SafeIntMinMax<false, 32>
+{
+public:
+    const static unsigned __int32 min = 0;
+    const static unsigned __int32 max = 0xffffffff;
+};
+template<>
+class SafeIntMinMax<false, 64>
+{
+public:
+    const static unsigned __int64 min = 0;
+    const static unsigned __int64 max = 0xffffffffffffffffULL;
+};
+
+template<typename T>
+class IntTraits
+{
+public:
+    C_ASSERT(NumericType<T>::isInt);
+    enum
+    {
+        isSigned = ((T)(-1) < 0),
+        is64Bit = (sizeof(T) == 8),
+        is32Bit = (sizeof(T) == 4),
+        is16Bit = (sizeof(T) == 2),
+        is8Bit = (sizeof(T) == 1),
+        isLT32Bit = (sizeof(T) < 4),
+        isLT64Bit = (sizeof(T) < 8),
+        isInt8 = (sizeof(T) == 1 && isSigned),
+        isUint8 = (sizeof(T) == 1 && !isSigned),
+        isInt16 = (sizeof(T) == 2 && isSigned),
+        isUint16 = (sizeof(T) == 2 && !isSigned),
+        isInt32 = (sizeof(T) == 4 && isSigned),
+        isUint32 = (sizeof(T) == 4 && !isSigned),
+        isInt64 = (sizeof(T) == 8 && isSigned),
+        isUint64 = (sizeof(T) == 8 && !isSigned),
+        bitCount = (sizeof(T) * 8),
+        isBool = ((T)2 == (T)1)
+    };
+
+    // On version 13.10 enums cannot define __int64 values
+    // so we'll use const statics instead!
+    // These must be cast to deal with the possibility of a SafeInt being given an enum as an argument
+    const static T maxInt = static_cast<T>(SafeIntMinMax<isSigned, bitCount>::max);
+    const static T minInt = static_cast<T>(SafeIntMinMax<isSigned, bitCount>::min);
+};
+
+template<typename T>
+const T IntTraits<T>::maxInt;
+template<typename T>
+const T IntTraits<T>::minInt;
+
+template<typename T, typename U>
+class SafeIntCompare
+{
+public:
+    enum
+    {
+        isBothSigned = (IntTraits<T>::isSigned && IntTraits<U>::isSigned),
+        isBothUnsigned = (!IntTraits<T>::isSigned && !IntTraits<U>::isSigned),
+        isLikeSigned = ((bool)(IntTraits<T>::isSigned) == (bool)(IntTraits<U>::isSigned)),
+        isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) || (IntTraits<T>::isSigned && sizeof(T) > sizeof(U))),
+        isBothLT32Bit = (IntTraits<T>::isLT32Bit && IntTraits<U>::isLT32Bit),
+        isBothLT64Bit = (IntTraits<T>::isLT64Bit && IntTraits<U>::isLT64Bit)
+    };
+};
+
+// all of the arithmetic operators can be solved by the same code within
+// each of these regions without resorting to compile-time constant conditionals
+// most operators collapse the problem into less than the 22 zones, but this is used
+// as the first cut
+// using this also helps ensure that we handle all of the possible cases correctly
+
+template<typename T, typename U>
+class IntRegion
+{
+public:
+    enum
+    {
+        // unsigned-unsigned zone
+        IntZone_UintLT32_UintLT32 = SafeIntCompare<T, U>::isBothUnsigned && SafeIntCompare<T, U>::isBothLT32Bit,
+        IntZone_Uint32_UintLT64 =
+            SafeIntCompare<T, U>::isBothUnsigned && IntTraits<T>::is32Bit && IntTraits<U>::isLT64Bit,
+        IntZone_UintLT32_Uint32 =
+            SafeIntCompare<T, U>::isBothUnsigned && IntTraits<T>::isLT32Bit && IntTraits<U>::is32Bit,
+        IntZone_Uint64_Uint = SafeIntCompare<T, U>::isBothUnsigned && IntTraits<T>::is64Bit,
+        IntZone_UintLT64_Uint64 =
+            SafeIntCompare<T, U>::isBothUnsigned && IntTraits<T>::isLT64Bit && IntTraits<U>::is64Bit,
+        // unsigned-signed
+        IntZone_UintLT32_IntLT32 =
+            !IntTraits<T>::isSigned && IntTraits<U>::isSigned && SafeIntCompare<T, U>::isBothLT32Bit,
+        IntZone_Uint32_IntLT64 = IntTraits<T>::isUint32 && IntTraits<U>::isSigned && IntTraits<U>::isLT64Bit,
+        IntZone_UintLT32_Int32 = !IntTraits<T>::isSigned && IntTraits<T>::isLT32Bit && IntTraits<U>::isInt32,
+        IntZone_Uint64_Int = IntTraits<T>::isUint64 && IntTraits<U>::isSigned && IntTraits<U>::isLT64Bit,
+        IntZone_UintLT64_Int64 = !IntTraits<T>::isSigned && IntTraits<T>::isLT64Bit && IntTraits<U>::isInt64,
+        IntZone_Uint64_Int64 = IntTraits<T>::isUint64 && IntTraits<U>::isInt64,
+        // signed-signed
+        IntZone_IntLT32_IntLT32 = SafeIntCompare<T, U>::isBothSigned && SafeIntCompare<T, U>::isBothLT32Bit,
+        IntZone_Int32_IntLT64 = SafeIntCompare<T, U>::isBothSigned && IntTraits<T>::is32Bit && IntTraits<U>::isLT64Bit,
+        IntZone_IntLT32_Int32 = SafeIntCompare<T, U>::isBothSigned && IntTraits<T>::isLT32Bit && IntTraits<U>::is32Bit,
+        IntZone_Int64_Int64 = SafeIntCompare<T, U>::isBothSigned && IntTraits<T>::isInt64 && IntTraits<U>::isInt64,
+        IntZone_Int64_Int = SafeIntCompare<T, U>::isBothSigned && IntTraits<T>::is64Bit && IntTraits<U>::isLT64Bit,
+        IntZone_IntLT64_Int64 = SafeIntCompare<T, U>::isBothSigned && IntTraits<T>::isLT64Bit && IntTraits<U>::is64Bit,
+        // signed-unsigned
+        IntZone_IntLT32_UintLT32 =
+            IntTraits<T>::isSigned && !IntTraits<U>::isSigned && SafeIntCompare<T, U>::isBothLT32Bit,
+        IntZone_Int32_UintLT32 = IntTraits<T>::isInt32 && !IntTraits<U>::isSigned && IntTraits<U>::isLT32Bit,
+        IntZone_IntLT64_Uint32 = IntTraits<T>::isSigned && IntTraits<T>::isLT64Bit && IntTraits<U>::isUint32,
+        IntZone_Int64_UintLT64 = IntTraits<T>::isInt64 && !IntTraits<U>::isSigned && IntTraits<U>::isLT64Bit,
+        IntZone_Int_Uint64 = IntTraits<T>::isSigned && IntTraits<U>::isUint64 && IntTraits<T>::isLT64Bit,
+        IntZone_Int64_Uint64 = IntTraits<T>::isInt64 && IntTraits<U>::isUint64
+    };
+};
+
+// In all of the following functions, we have two versions
+// One for SafeInt, which throws C++ (or possibly SEH) exceptions
+// The non-throwing versions are for use by the helper functions that return success and failure.
+// Some of the non-throwing functions are not used, but are maintained for completeness.
+
+// There's no real alternative to duplicating logic, but keeping the two versions
+// immediately next to one another will help reduce problems
+
+// useful function to help with getting the magnitude of a negative number
+enum AbsMethod
+{
+    AbsMethodInt,
+    AbsMethodInt64,
+    AbsMethodNoop
+};
+
+template<typename T>
+class GetAbsMethod
+{
+public:
+    enum
+    {
+        method = IntTraits<T>::isLT64Bit && IntTraits<T>::isSigned
+                     ? AbsMethodInt
+                     : IntTraits<T>::isInt64 ? AbsMethodInt64 : AbsMethodNoop
+    };
+};
+
+// let's go ahead and hard-code a dependency on the
+// representation of negative numbers to keep compilers from getting overly
+// happy with optimizing away things like -MIN_INT.
+template<typename T, int>
+class AbsValueHelper;
+
+template<typename T>
+class AbsValueHelper<T, AbsMethodInt>
+{
+public:
+    static unsigned __int32 Abs(T t) SAFEINT_NOTHROW
+    {
+        SAFEINT_ASSERT(t < 0);
+        return ~(unsigned __int32)t + 1;
+    }
+};
+
+template<typename T>
+class AbsValueHelper<T, AbsMethodInt64>
+{
+public:
+    static unsigned __int64 Abs(T t) SAFEINT_NOTHROW
+    {
+        SAFEINT_ASSERT(t < 0);
+        return ~(unsigned __int64)t + 1;
+    }
+};
+
+template<typename T>
+class AbsValueHelper<T, AbsMethodNoop>
+{
+public:
+    static T Abs(T t) SAFEINT_NOTHROW
+    {
+        // Why are you calling Abs on an unsigned number ???
+        SAFEINT_ASSERT(false);
+        return t;
+    }
+};
+
+template<typename T, bool>
+class NegationHelper;
+// Previous versions had an assert that the type being negated was 32-bit or higher
+// In retrospect, this seems like something to just document
+// Negation will normally upcast to int
+// For example -(unsigned short)0xffff == (int)0xffff0001
+// This class will retain the type, and will truncate, which may not be what
+// you wanted
+// If you want normal operator casting behavior, do this:
+// SafeInt<unsigned short> ss = 0xffff;
+// then:
+// -(SafeInt<int>(ss))
+// will then emit a signed int with the correct value and bitfield
+
+template<typename T>
+class NegationHelper<T, true> // Signed
+{
+public:
+    template<typename E>
+    static T NegativeThrow(T t) SAFEINT_CPP_THROW
+    {
+        // corner case
+        if (t != IntTraits<T>::minInt)
+        {
+            // cast prevents unneeded checks in the case of small ints
+            return -t;
+        }
+        E::SafeIntOnOverflow();
+    }
+
+    static bool Negative(T t, T& ret) SAFEINT_NOTHROW
+    {
+        // corner case
+        if (t != IntTraits<T>::minInt)
+        {
+            // cast prevents unneeded checks in the case of small ints
+            ret = -t;
+            return true;
+        }
+        return false;
+    }
+};
+
+// Helper classes to work keep compilers from
+// optimizing away negation
+template<typename T>
+class SignedNegation;
+
+template<>
+class SignedNegation<signed __int32>
+{
+public:
+    static signed __int32 Value(unsigned __int64 in) SAFEINT_NOTHROW
+    {
+        return (signed __int32)(~(unsigned __int32)in + 1);
+    }
+
+    static signed __int32 Value(unsigned __int32 in) SAFEINT_NOTHROW { return (signed __int32)(~in + 1); }
+};
+
+template<>
+class SignedNegation<signed __int64>
+{
+public:
+    static signed __int64 Value(unsigned __int64 in) SAFEINT_NOTHROW { return (signed __int64)(~in + 1); }
+};
+
+template<typename T>
+class NegationHelper<T, false> // unsigned
+{
+public:
+    template<typename E>
+    static T NegativeThrow(T t) SAFEINT_CPP_THROW
+    {
+#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION
+        C_ASSERT(sizeof(T) == 0);
+#endif
+
+#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
+#pragma warning(push)
+// this avoids warnings from the unary '-' operator being applied to unsigned numbers
+#pragma warning(disable : 4146)
+#endif
+        // Note - this could be quenched on gcc
+        // by doing something like:
+        // return (T)-((__int64)t);
+        // but it seems like you would want a warning when doing this.
+        return (T)-t;
+
+#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
+#pragma warning(pop)
+#endif
+    }
+
+    static bool Negative(T t, T& ret) SAFEINT_NOTHROW
+    {
+        if (IntTraits<T>::isLT32Bit)
+        {
+            // See above
+            SAFEINT_ASSERT(false);
+        }
+#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION
+        C_ASSERT(sizeof(T) == 0);
+#endif
+        // Do it this way to avoid warning
+        ret = -t;
+        return true;
+    }
+};
+
+// core logic to determine casting behavior
+enum CastMethod
+{
+    CastOK = 0,
+    CastCheckLTZero,
+    CastCheckGTMax,
+    CastCheckSafeIntMinMaxUnsigned,
+    CastCheckSafeIntMinMaxSigned,
+    CastToFloat,
+    CastFromFloat,
+    CastToBool,
+    CastFromBool
+};
+
+template<typename ToType, typename FromType>
+class GetCastMethod
+{
+public:
+    enum
+    {
+        method = (IntTraits<FromType>::isBool && !IntTraits<ToType>::isBool)
+                     ? CastFromBool
+                     :
+
+                     (!IntTraits<FromType>::isBool && IntTraits<ToType>::isBool)
+                         ? CastToBool
+                         :
+
+                         (SafeIntCompare<ToType, FromType>::isCastOK)
+                             ? CastOK
+                             :
+
+                             ((IntTraits<ToType>::isSigned && !IntTraits<FromType>::isSigned &&
+                               sizeof(FromType) >= sizeof(ToType)) ||
+                              (SafeIntCompare<ToType, FromType>::isBothUnsigned && sizeof(FromType) > sizeof(ToType)))
+                                 ? CastCheckGTMax
+                                 :
+
+                                 (!IntTraits<ToType>::isSigned && IntTraits<FromType>::isSigned &&
+                                  sizeof(ToType) >= sizeof(FromType))
+                                     ? CastCheckLTZero
+                                     :
+
+                                     (!IntTraits<ToType>::isSigned) ? CastCheckSafeIntMinMaxUnsigned
+                                                                    : CastCheckSafeIntMinMaxSigned
+    };
+};
+
+template<typename FromType>
+class GetCastMethod<float, FromType>
+{
+public:
+    enum
+    {
+        method = CastOK
+    };
+};
+
+template<typename FromType>
+class GetCastMethod<double, FromType>
+{
+public:
+    enum
+    {
+        method = CastOK
+    };
+};
+
+template<typename FromType>
+class GetCastMethod<long double, FromType>
+{
+public:
+    enum
+    {
+        method = CastOK
+    };
+};
+
+template<typename ToType>
+class GetCastMethod<ToType, float>
+{
+public:
+    enum
+    {
+        method = CastFromFloat
+    };
+};
+
+template<typename ToType>
+class GetCastMethod<ToType, double>
+{
+public:
+    enum
+    {
+        method = CastFromFloat
+    };
+};
+
+template<typename ToType>
+class GetCastMethod<ToType, long double>
+{
+public:
+    enum
+    {
+        method = CastFromFloat
+    };
+};
+
+template<typename T, typename U, int>
+class SafeCastHelper;
+
+template<typename T, typename U>
+class SafeCastHelper<T, U, CastOK>
+{
+public:
+    static bool Cast(U u, T& t) SAFEINT_NOTHROW
+    {
+        t = (T)u;
+        return true;
+    }
+
+    template<typename E>
+    static void CastThrow(U u, T& t) SAFEINT_CPP_THROW
+    {
+        t = (T)u;
+    }
+};
+
+// special case floats and doubles
+// tolerate loss of precision
+template<typename T, typename U>
+class SafeCastHelper<T, U, CastFromFloat>
+{
+public:
+    static bool Cast(U u, T& t) SAFEINT_NOTHROW
+    {
+        if (u <= (U)IntTraits<T>::maxInt && u >= (U)IntTraits<T>::minInt)
+        {
+            t = (T)u;
+            return true;
+        }
+        return false;
+    }
+
+    template<typename E>
+    static void CastThrow(U u, T& t) SAFEINT_CPP_THROW
+    {
+        if (u <= (U)IntTraits<T>::maxInt && u >= (U)IntTraits<T>::minInt)
+        {
+            t = (T)u;
+            return;
+        }
+        E::SafeIntOnOverflow();
+    }
+};
+
+// Match on any method where a bool is cast to type T
+template<typename T>
+class SafeCastHelper<T, bool, CastFromBool>
+{
+public:
+    static bool Cast(bool b, T& t) SAFEINT_NOTHROW
+    {
+        t = (T)(b ? 1 : 0);
+        return true;
+    }
+
+    template<typename E>
+    static void CastThrow(bool b, T& t) SAFEINT_CPP_THROW
+    {
+        t = (T)(b ? 1 : 0);
+    }
+};
+
+template<typename T>
+class SafeCastHelper<bool, T, CastToBool>
+{
+public:
+    static bool Cast(T t, bool& b) SAFEINT_NOTHROW
+    {
+        b = !!t;
+        return true;
+    }
+
+    template<typename E>
+    static void CastThrow(bool b, T& t) SAFEINT_CPP_THROW
+    {
+        b = !!t;
+    }
+};
+
+template<typename T, typename U>
+class SafeCastHelper<T, U, CastCheckLTZero>
+{
+public:
+    static bool Cast(U u, T& t) SAFEINT_NOTHROW
+    {
+        if (u < 0) return false;
+
+        t = (T)u;
+        return true;
+    }
+
+    template<typename E>
+    static void CastThrow(U u, T& t) SAFEINT_CPP_THROW
+    {
+        if (u < 0) E::SafeIntOnOverflow();
+
+        t = (T)u;
+    }
+};
+
+template<typename T, typename U>
+class SafeCastHelper<T, U, CastCheckGTMax>
+{
+public:
+    static bool Cast(U u, T& t) SAFEINT_NOTHROW
+    {
+        if (u > (U)IntTraits<T>::maxInt) return false;
+
+        t = (T)u;
+        return true;
+    }
+
+    template<typename E>
+    static void CastThrow(U u, T& t) SAFEINT_CPP_THROW
+    {
+        if (u > (U)IntTraits<T>::maxInt) E::SafeIntOnOverflow();
+
+        t = (T)u;
+    }
+};
+
+template<typename T, typename U>
+class SafeCastHelper<T, U, CastCheckSafeIntMinMaxUnsigned>
+{
+public:
+    static bool Cast(U u, T& t) SAFEINT_NOTHROW
+    {
+        // U is signed - T could be either signed or unsigned
+        if (u > IntTraits<T>::maxInt || u < 0) return false;
+
+        t = (T)u;
+        return true;
+    }
+
+    template<typename E>
+    static void CastThrow(U u, T& t) SAFEINT_CPP_THROW
+    {
+        // U is signed - T could be either signed or unsigned
+        if (u > IntTraits<T>::maxInt || u < 0) E::SafeIntOnOverflow();
+
+        t = (T)u;
+    }
+};
+
+template<typename T, typename U>
+class SafeCastHelper<T, U, CastCheckSafeIntMinMaxSigned>
+{
+public:
+    static bool Cast(U u, T& t) SAFEINT_NOTHROW
+    {
+        // T, U are signed
+        if (u > IntTraits<T>::maxInt || u < IntTraits<T>::minInt) return false;
+
+        t = (T)u;
+        return true;
+    }
+
+    template<typename E>
+    static void CastThrow(U u, T& t) SAFEINT_CPP_THROW
+    {
+        // T, U are signed
+        if (u > IntTraits<T>::maxInt || u < IntTraits<T>::minInt) E::SafeIntOnOverflow();
+
+        t = (T)u;
+    }
+};
+
+// core logic to determine whether a comparison is valid, or needs special treatment
+enum ComparisonMethod
+{
+    ComparisonMethod_Ok = 0,
+    ComparisonMethod_CastInt,
+    ComparisonMethod_CastInt64,
+    ComparisonMethod_UnsignedT,
+    ComparisonMethod_UnsignedU
+};
+
+// Note - the standard is arguably broken in the case of some integer
+// conversion operations
+// For example, signed char a = -1 = 0xff
+//              unsigned int b = 0xffffffff
+// If you then test if a < b, a value-preserving cast
+// is made, and you're essentially testing
+// (unsigned int)a < b == false
+//
+// I do not think this makes sense - if you perform
+// a cast to an __int64, which can clearly preserve both value and signedness
+// then you get a different and intuitively correct answer
+// IMHO, -1 should be less than 4 billion
+// If you prefer to retain the ANSI standard behavior
+// insert #define ANSI_CONVERSIONS into your source
+// Behavior differences occur in the following cases:
+// 8, 16, and 32-bit signed int, unsigned 32-bit int
+// any signed int, unsigned 64-bit int
+// Note - the signed int must be negative to show the problem
+
+template<typename T, typename U>
+class ValidComparison
+{
+public:
+    enum
+    {
+#ifdef ANSI_CONVERSIONS
+        method = ComparisonMethod_Ok
+#else
+        method = ((SafeIntCompare<T, U>::isLikeSigned)
+                      ? ComparisonMethod_Ok
+                      : ((IntTraits<T>::isSigned && sizeof(T) < 8 && sizeof(U) < 4) ||
+                         (IntTraits<U>::isSigned && sizeof(T) < 4 && sizeof(U) < 8))
+                            ? ComparisonMethod_CastInt
+                            : ((IntTraits<T>::isSigned && sizeof(U) < 8) || (IntTraits<U>::isSigned && sizeof(T) < 8))
+                                  ? ComparisonMethod_CastInt64
+                                  : (!IntTraits<T>::isSigned) ? ComparisonMethod_UnsignedT : ComparisonMethod_UnsignedU)
+#endif
+    };
+};
+
+template<typename T, typename U, int state>
+class EqualityTest;
+
+template<typename T, typename U>
+class EqualityTest<T, U, ComparisonMethod_Ok>
+{
+public:
+    static bool IsEquals(const T t, const U u) SAFEINT_NOTHROW { return (t == u); }
+};
+
+template<typename T, typename U>
+class EqualityTest<T, U, ComparisonMethod_CastInt>
+{
+public:
+    static bool IsEquals(const T t, const U u) SAFEINT_NOTHROW { return ((int)t == (int)u); }
+};
+
+template<typename T, typename U>
+class EqualityTest<T, U, ComparisonMethod_CastInt64>
+{
+public:
+    static bool IsEquals(const T t, const U u) SAFEINT_NOTHROW { return ((__int64)t == (__int64)u); }
+};
+
+template<typename T, typename U>
+class EqualityTest<T, U, ComparisonMethod_UnsignedT>
+{
+public:
+    static bool IsEquals(const T t, const U u) SAFEINT_NOTHROW
+    {
+        // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller
+        if (u < 0) return false;
+
+        // else safe to cast to type T
+        return (t == (T)u);
+    }
+};
+
+template<typename T, typename U>
+class EqualityTest<T, U, ComparisonMethod_UnsignedU>
+{
+public:
+    static bool IsEquals(const T t, const U u) SAFEINT_NOTHROW
+    {
+        // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller
+        if (t < 0) return false;
+
+        // else safe to cast to type U
+        return ((U)t == u);
+    }
+};
+
+template<typename T, typename U, int state>
+class GreaterThanTest;
+
+template<typename T, typename U>
+class GreaterThanTest<T, U, ComparisonMethod_Ok>
+{
+public:
+    static bool GreaterThan(const T t, const U u) SAFEINT_NOTHROW { return (t > u); }
+};
+
+template<typename T, typename U>
+class GreaterThanTest<T, U, ComparisonMethod_CastInt>
+{
+public:
+    static bool GreaterThan(const T t, const U u) SAFEINT_NOTHROW { return ((int)t > (int)u); }
+};
+
+template<typename T, typename U>
+class GreaterThanTest<T, U, ComparisonMethod_CastInt64>
+{
+public:
+    static bool GreaterThan(const T t, const U u) SAFEINT_NOTHROW { return ((__int64)t > (__int64)u); }
+};
+
+template<typename T, typename U>
+class GreaterThanTest<T, U, ComparisonMethod_UnsignedT>
+{
+public:
+    static bool GreaterThan(const T t, const U u) SAFEINT_NOTHROW
+    {
+        // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller
+        if (u < 0) return true;
+
+        // else safe to cast to type T
+        return (t > (T)u);
+    }
+};
+
+template<typename T, typename U>
+class GreaterThanTest<T, U, ComparisonMethod_UnsignedU>
+{
+public:
+    static bool GreaterThan(const T t, const U u) SAFEINT_NOTHROW
+    {
+        // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller
+        if (t < 0) return false;
+
+        // else safe to cast to type U
+        return ((U)t > u);
+    }
+};
+
+// Modulus is simpler than comparison, but follows much the same logic
+// using this set of functions, it can't fail except in a div 0 situation
+template<typename T, typename U, int method>
+class ModulusHelper;
+
+template<typename T, typename U>
+class ModulusHelper<T, U, ComparisonMethod_Ok>
+{
+public:
+    static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+    {
+        if (u == 0) return SafeIntDivideByZero;
+
+        // trap corner case
+        if (CompileConst<IntTraits<U>::isSigned>::Value())
+        {
+            // Some compilers don't notice that this only compiles when u is signed
+            // Add cast to make them happy
+            if (u == (U)-1)
+            {
+                result = 0;
+                return SafeIntNoError;
+            }
+        }
+
+        result = (T)(t % u);
+        return SafeIntNoError;
+    }
+
+    template<typename E>
+    static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW
+    {
+        if (u == 0) E::SafeIntOnDivZero();
+
+        // trap corner case
+        if (CompileConst<IntTraits<U>::isSigned>::Value())
+        {
+            if (u == (U)-1)
+            {
+                result = 0;
+                return;
+            }
+        }
+
+        result = (T)(t % u);
+    }
+};
+
+template<typename T, typename U>
+class ModulusHelper<T, U, ComparisonMethod_CastInt>
+{
+public:
+    static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+    {
+        if (u == 0) return SafeIntDivideByZero;
+
+        // trap corner case
+        if (CompileConst<IntTraits<U>::isSigned>::Value())
+        {
+            if (u == (U)-1)
+            {
+                result = 0;
+                return SafeIntNoError;
+            }
+        }
+
+        result = (T)(t % u);
+        return SafeIntNoError;
+    }
+
+    template<typename E>
+    static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW
+    {
+        if (u == 0) E::SafeIntOnDivZero();
+
+        // trap corner case
+        if (CompileConst<IntTraits<U>::isSigned>::Value())
+        {
+            if (u == (U)-1)
+            {
+                result = 0;
+                return;
+            }
+        }
+
+        result = (T)(t % u);
+    }
+};
+
+template<typename T, typename U>
+class ModulusHelper<T, U, ComparisonMethod_CastInt64>
+{
+public:
+    static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+    {
+        if (u == 0) return SafeIntDivideByZero;
+
+        // trap corner case
+        if (CompileConst<IntTraits<U>::isSigned>::Value())
+        {
+            if (u == (U)-1)
+            {
+                result = 0;
+                return SafeIntNoError;
+            }
+        }
+
+        result = (T)((__int64)t % (__int64)u);
+        return SafeIntNoError;
+    }
+
+    template<typename E>
+    static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW
+    {
+        if (u == 0) E::SafeIntOnDivZero();
+
+        if (CompileConst<IntTraits<U>::isSigned>::Value())
+        {
+            if (u == (U)-1)
+            {
+                result = 0;
+                return;
+            }
+        }
+
+        result = (T)((__int64)t % (__int64)u);
+    }
+};
+
+// T is unsigned __int64, U is any signed int
+template<typename T, typename U>
+class ModulusHelper<T, U, ComparisonMethod_UnsignedT>
+{
+public:
+    static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+    {
+        if (u == 0) return SafeIntDivideByZero;
+
+        // u could be negative - if so, need to convert to positive
+        // casts below are always safe due to the way modulus works
+        if (u < 0)
+            result = (T)(t % AbsValueHelper<U, GetAbsMethod<U>::method>::Abs(u));
+        else
+            result = (T)(t % u);
+
+        return SafeIntNoError;
+    }
+
+    template<typename E>
+    static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW
+    {
+        if (u == 0) E::SafeIntOnDivZero();
+
+        // u could be negative - if so, need to convert to positive
+        if (u < 0)
+            result = (T)(t % AbsValueHelper<U, GetAbsMethod<U>::method>::Abs(u));
+        else
+            result = (T)(t % u);
+    }
+};
+
+// U is unsigned __int64, T any signed int
+template<typename T, typename U>
+class ModulusHelper<T, U, ComparisonMethod_UnsignedU>
+{
+public:
+    static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+    {
+        if (u == 0) return SafeIntDivideByZero;
+
+        // t could be negative - if so, need to convert to positive
+        if (t < 0)
+            result = (T)(~(AbsValueHelper<T, GetAbsMethod<T>::method>::Abs(t) % u) + 1);
+        else
+            result = (T)((T)t % u);
+
+        return SafeIntNoError;
+    }
+
+    template<typename E>
+    static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW
+    {
+        if (u == 0) E::SafeIntOnDivZero();
+
+        // t could be negative - if so, need to convert to positive
+        if (t < 0)
+            result = (T)(~(AbsValueHelper<T, GetAbsMethod<T>::method>::Abs(t) % u) + 1);
+        else
+            result = (T)((T)t % u);
+    }
+};
+
+// core logic to determine method to check multiplication
+enum MultiplicationState
+{
+    MultiplicationState_CastInt = 0,  // One or both signed, smaller than 32-bit
+    MultiplicationState_CastInt64,    // One or both signed, smaller than 64-bit
+    MultiplicationState_CastUint,     // Both are unsigned, smaller than 32-bit
+    MultiplicationState_CastUint64,   // Both are unsigned, both 32-bit or smaller
+    MultiplicationState_Uint64Uint,   // Both are unsigned, lhs 64-bit, rhs 32-bit or smaller
+    MultiplicationState_Uint64Uint64, // Both are unsigned int64
+    MultiplicationState_Uint64Int,    // lhs is unsigned int64, rhs int32
+    MultiplicationState_Uint64Int64,  // lhs is unsigned int64, rhs signed int64
+    MultiplicationState_UintUint64,   // Both are unsigned, lhs 32-bit or smaller, rhs 64-bit
+    MultiplicationState_UintInt64,    // lhs unsigned 32-bit or less, rhs int64
+    MultiplicationState_Int64Uint,    // lhs int64, rhs unsigned int32
+    MultiplicationState_Int64Int64,   // lhs int64, rhs int64
+    MultiplicationState_Int64Int,     // lhs int64, rhs int32
+    MultiplicationState_IntUint64,    // lhs int, rhs unsigned int64
+    MultiplicationState_IntInt64,     // lhs int, rhs int64
+    MultiplicationState_Int64Uint64,  // lhs int64, rhs uint64
+    MultiplicationState_Error
+};
+
+template<typename T, typename U>
+class MultiplicationMethod
+{
+public:
+    enum
+    {
+        // unsigned-unsigned
+        method =
+            (IntRegion<T, U>::IntZone_UintLT32_UintLT32
+                 ? MultiplicationState_CastUint
+                 : (IntRegion<T, U>::IntZone_Uint32_UintLT64 || IntRegion<T, U>::IntZone_UintLT32_Uint32)
+                       ? MultiplicationState_CastUint64
+                       : SafeIntCompare<T, U>::isBothUnsigned && IntTraits<T>::isUint64 && IntTraits<U>::isUint64
+                             ? MultiplicationState_Uint64Uint64
+                             : (IntRegion<T, U>::IntZone_Uint64_Uint)
+                                   ? MultiplicationState_Uint64Uint
+                                   : (IntRegion<T, U>::IntZone_UintLT64_Uint64) ? MultiplicationState_UintUint64 :
+                                                                                // unsigned-signed
+                                         (IntRegion<T, U>::IntZone_UintLT32_IntLT32)
+                                             ? MultiplicationState_CastInt
+                                             : (IntRegion<T, U>::IntZone_Uint32_IntLT64 ||
+                                                IntRegion<T, U>::IntZone_UintLT32_Int32)
+                                                   ? MultiplicationState_CastInt64
+                                                   : (IntRegion<T, U>::IntZone_Uint64_Int)
+                                                         ? MultiplicationState_Uint64Int
+                                                         : (IntRegion<T, U>::IntZone_UintLT64_Int64)
+                                                               ? MultiplicationState_UintInt64
+                                                               : (IntRegion<T, U>::IntZone_Uint64_Int64)
+                                                                     ? MultiplicationState_Uint64Int64
+                                                                     :
+                                                                     // signed-signed
+                                                                     (IntRegion<T, U>::IntZone_IntLT32_IntLT32)
+                                                                         ? MultiplicationState_CastInt
+                                                                         : (IntRegion<T, U>::IntZone_Int32_IntLT64 ||
+                                                                            IntRegion<T, U>::IntZone_IntLT32_Int32)
+                                                                               ? MultiplicationState_CastInt64
+                                                                               : (IntRegion<T, U>::IntZone_Int64_Int64)
+                                                                                     ? MultiplicationState_Int64Int64
+                                                                                     : (IntRegion<T,
+                                                                                                  U>::IntZone_Int64_Int)
+                                                                                           ? MultiplicationState_Int64Int
+                                                                                           : (IntRegion<T, U>::
+                                                                                                  IntZone_IntLT64_Int64)
+                                                                                                 ? MultiplicationState_IntInt64
+                                                                                                 :
+                                                                                                 // signed-unsigned
+                                                                                                 (IntRegion<T, U>::
+                                                                                                      IntZone_IntLT32_UintLT32)
+                                                                                                     ? MultiplicationState_CastInt
+                                                                                                     : (IntRegion<T,
+                                                                                                                  U>::
+                                                                                                            IntZone_Int32_UintLT32 ||
+                                                                                                        IntRegion<T,
+                                                                                                                  U>::
+                                                                                                            IntZone_IntLT64_Uint32)
+                                                                                                           ? MultiplicationState_CastInt64
+                                                                                                           : (IntRegion<
+                                                                                                                 T,
+                                                                                                                 U>::
+                                                                                                                  IntZone_Int64_UintLT64)
+                                                                                                                 ? MultiplicationState_Int64Uint
+                                                                                                                 : (IntRegion<
+                                                                                                                       T,
+                                                                                                                       U>::
+                                                                                                                        IntZone_Int_Uint64)
+                                                                                                                       ? MultiplicationState_IntUint64
+                                                                                                                       : (IntRegion<
+                                                                                                                              T,
+                                                                                                                              U>::IntZone_Int64_Uint64
+                                                                                                                              ? MultiplicationState_Int64Uint64
+                                                                                                                              : MultiplicationState_Error))
+    };
+};
+
+template<typename T, typename U, int state>
+class MultiplicationHelper;
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_CastInt>
+{
+public:
+    // accepts signed, both less than 32-bit
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        int tmp = t * u;
+
+        if (tmp > IntTraits<T>::maxInt || tmp < IntTraits<T>::minInt) return false;
+
+        ret = (T)tmp;
+        return true;
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        int tmp = t * u;
+
+        if (tmp > IntTraits<T>::maxInt || tmp < IntTraits<T>::minInt) E::SafeIntOnOverflow();
+
+        ret = (T)tmp;
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_CastUint>
+{
+public:
+    // accepts unsigned, both less than 32-bit
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        unsigned int tmp = (unsigned int)(t * u);
+
+        if (tmp > IntTraits<T>::maxInt) return false;
+
+        ret = (T)tmp;
+        return true;
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        unsigned int tmp = (unsigned int)(t * u);
+
+        if (tmp > IntTraits<T>::maxInt) E::SafeIntOnOverflow();
+
+        ret = (T)tmp;
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_CastInt64>
+{
+public:
+    // mixed signed or both signed where at least one argument is 32-bit, and both a 32-bit or less
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        __int64 tmp = (__int64)t * (__int64)u;
+
+        if (tmp > (__int64)IntTraits<T>::maxInt || tmp < (__int64)IntTraits<T>::minInt) return false;
+
+        ret = (T)tmp;
+        return true;
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        __int64 tmp = (__int64)t * (__int64)u;
+
+        if (tmp > (__int64)IntTraits<T>::maxInt || tmp < (__int64)IntTraits<T>::minInt) E::SafeIntOnOverflow();
+
+        ret = (T)tmp;
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_CastUint64>
+{
+public:
+    // both unsigned where at least one argument is 32-bit, and both are 32-bit or less
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u;
+
+        if (tmp > (unsigned __int64)IntTraits<T>::maxInt) return false;
+
+        ret = (T)tmp;
+        return true;
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u;
+
+        if (tmp > (unsigned __int64)IntTraits<T>::maxInt) E::SafeIntOnOverflow();
+
+        ret = (T)tmp;
+    }
+};
+
+// T = left arg and return type
+// U = right arg
+template<typename T, typename U>
+class LargeIntRegMultiply;
+
+#if SAFEINT_USE_INTRINSICS
+// As usual, unsigned is easy
+inline bool IntrinsicMultiplyUint64(const unsigned __int64& a,
+                                    const unsigned __int64& b,
+                                    unsigned __int64* pRet) SAFEINT_NOTHROW
+{
+    unsigned __int64 ulHigh = 0;
+    *pRet = _umul128(a, b, &ulHigh);
+    return ulHigh == 0;
+}
+
+// Signed, is not so easy
+inline bool IntrinsicMultiplyInt64(const signed __int64& a,
+                                   const signed __int64& b,
+                                   signed __int64* pRet) SAFEINT_NOTHROW
+{
+    __int64 llHigh = 0;
+    *pRet = _mul128(a, b, &llHigh);
+
+    // Now we need to figure out what we expect
+    // If llHigh is 0, then treat *pRet as unsigned
+    // If llHigh is < 0, then treat *pRet as signed
+
+    if ((a ^ b) < 0)
+    {
+        // Negative result expected
+        if (llHigh == -1 && *pRet < 0 || llHigh == 0 && *pRet == 0)
+        {
+            // Everything is within range
+            return true;
+        }
+    }
+    else
+    {
+        // Result should be positive
+        // Check for overflow
+        if (llHigh == 0 && (unsigned __int64)*pRet <= IntTraits<signed __int64>::maxInt) return true;
+    }
+    return false;
+}
+
+#endif
+
+template<>
+class LargeIntRegMultiply<unsigned __int64, unsigned __int64>
+{
+public:
+    static bool RegMultiply(const unsigned __int64& a,
+                            const unsigned __int64& b,
+                            unsigned __int64* pRet) SAFEINT_NOTHROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        return IntrinsicMultiplyUint64(a, b, pRet);
+#else
+        unsigned __int32 aHigh, aLow, bHigh, bLow;
+
+        // Consider that a*b can be broken up into:
+        // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow)
+        // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow)
+        // Note - same approach applies for 128 bit math on a 64-bit system
+
+        aHigh = (unsigned __int32)(a >> 32);
+        aLow = (unsigned __int32)a;
+        bHigh = (unsigned __int32)(b >> 32);
+        bLow = (unsigned __int32)b;
+
+        *pRet = 0;
+
+        if (aHigh == 0)
+        {
+            if (bHigh != 0)
+            {
+                *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh;
+            }
+        }
+        else if (bHigh == 0)
+        {
+            if (aHigh != 0)
+            {
+                *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow;
+            }
+        }
+        else
+        {
+            return false;
+        }
+
+        if (*pRet != 0)
+        {
+            unsigned __int64 tmp;
+
+            if ((unsigned __int32)(*pRet >> 32) != 0) return false;
+
+            *pRet <<= 32;
+            tmp = (unsigned __int64)aLow * (unsigned __int64)bLow;
+            *pRet += tmp;
+
+            if (*pRet < tmp) return false;
+
+            return true;
+        }
+
+        *pRet = (unsigned __int64)aLow * (unsigned __int64)bLow;
+        return true;
+#endif
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(const unsigned __int64& a,
+                                 const unsigned __int64& b,
+                                 unsigned __int64* pRet) SAFEINT_CPP_THROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        if (!IntrinsicMultiplyUint64(a, b, pRet)) E::SafeIntOnOverflow();
+#else
+        unsigned __int32 aHigh, aLow, bHigh, bLow;
+
+        // Consider that a*b can be broken up into:
+        // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow)
+        // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow)
+        // Note - same approach applies for 128 bit math on a 64-bit system
+
+        aHigh = (unsigned __int32)(a >> 32);
+        aLow = (unsigned __int32)a;
+        bHigh = (unsigned __int32)(b >> 32);
+        bLow = (unsigned __int32)b;
+
+        *pRet = 0;
+
+        if (aHigh == 0)
+        {
+            if (bHigh != 0)
+            {
+                *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh;
+            }
+        }
+        else if (bHigh == 0)
+        {
+            if (aHigh != 0)
+            {
+                *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow;
+            }
+        }
+        else
+        {
+            E::SafeIntOnOverflow();
+        }
+
+        if (*pRet != 0)
+        {
+            unsigned __int64 tmp;
+
+            if ((unsigned __int32)(*pRet >> 32) != 0) E::SafeIntOnOverflow();
+
+            *pRet <<= 32;
+            tmp = (unsigned __int64)aLow * (unsigned __int64)bLow;
+            *pRet += tmp;
+
+            if (*pRet < tmp) E::SafeIntOnOverflow();
+
+            return;
+        }
+
+        *pRet = (unsigned __int64)aLow * (unsigned __int64)bLow;
+#endif
+    }
+};
+
+template<>
+class LargeIntRegMultiply<unsigned __int64, unsigned __int32>
+{
+public:
+    static bool RegMultiply(const unsigned __int64& a, unsigned __int32 b, unsigned __int64* pRet) SAFEINT_NOTHROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        return IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet);
+#else
+        unsigned __int32 aHigh, aLow;
+
+        // Consider that a*b can be broken up into:
+        // (aHigh * 2^32 + aLow) * b
+        // => (aHigh * b * 2^32) + (aLow * b)
+
+        aHigh = (unsigned __int32)(a >> 32);
+        aLow = (unsigned __int32)a;
+
+        *pRet = 0;
+
+        if (aHigh != 0)
+        {
+            *pRet = (unsigned __int64)aHigh * (unsigned __int64)b;
+
+            unsigned __int64 tmp;
+
+            if ((unsigned __int32)(*pRet >> 32) != 0) return false;
+
+            *pRet <<= 32;
+            tmp = (unsigned __int64)aLow * (unsigned __int64)b;
+            *pRet += tmp;
+
+            if (*pRet < tmp) return false;
+
+            return true;
+        }
+
+        *pRet = (unsigned __int64)aLow * (unsigned __int64)b;
+        return true;
+#endif
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(const unsigned __int64& a,
+                                 unsigned __int32 b,
+                                 unsigned __int64* pRet) SAFEINT_CPP_THROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        if (!IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet)) E::SafeIntOnOverflow();
+#else
+        unsigned __int32 aHigh, aLow;
+
+        // Consider that a*b can be broken up into:
+        // (aHigh * 2^32 + aLow) * b
+        // => (aHigh * b * 2^32) + (aLow * b)
+
+        aHigh = (unsigned __int32)(a >> 32);
+        aLow = (unsigned __int32)a;
+
+        *pRet = 0;
+
+        if (aHigh != 0)
+        {
+            *pRet = (unsigned __int64)aHigh * (unsigned __int64)b;
+
+            unsigned __int64 tmp;
+
+            if ((unsigned __int32)(*pRet >> 32) != 0) E::SafeIntOnOverflow();
+
+            *pRet <<= 32;
+            tmp = (unsigned __int64)aLow * (unsigned __int64)b;
+            *pRet += tmp;
+
+            if (*pRet < tmp) E::SafeIntOnOverflow();
+
+            return;
+        }
+
+        *pRet = (unsigned __int64)aLow * (unsigned __int64)b;
+        return;
+#endif
+    }
+};
+
+template<>
+class LargeIntRegMultiply<unsigned __int64, signed __int32>
+{
+public:
+    // Intrinsic not needed
+    static bool RegMultiply(const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet) SAFEINT_NOTHROW
+    {
+        if (b < 0 && a != 0) return false;
+
+#if SAFEINT_USE_INTRINSICS
+        return IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet);
+#else
+        return LargeIntRegMultiply<unsigned __int64, unsigned __int32>::RegMultiply(a, (unsigned __int32)b, pRet);
+#endif
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet) SAFEINT_CPP_THROW
+    {
+        if (b < 0 && a != 0) E::SafeIntOnOverflow();
+
+#if SAFEINT_USE_INTRINSICS
+        if (!IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet)) E::SafeIntOnOverflow();
+#else
+        LargeIntRegMultiply<unsigned __int64, unsigned __int32>::template RegMultiplyThrow<E>(
+            a, (unsigned __int32)b, pRet);
+#endif
+    }
+};
+
+template<>
+class LargeIntRegMultiply<unsigned __int64, signed __int64>
+{
+public:
+    static bool RegMultiply(const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet) SAFEINT_NOTHROW
+    {
+        if (b < 0 && a != 0) return false;
+
+#if SAFEINT_USE_INTRINSICS
+        return IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet);
+#else
+        return LargeIntRegMultiply<unsigned __int64, unsigned __int64>::RegMultiply(a, (unsigned __int64)b, pRet);
+#endif
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet) SAFEINT_CPP_THROW
+    {
+        if (b < 0 && a != 0) E::SafeIntOnOverflow();
+
+#if SAFEINT_USE_INTRINSICS
+        if (!IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet)) E::SafeIntOnOverflow();
+#else
+        LargeIntRegMultiply<unsigned __int64, unsigned __int64>::template RegMultiplyThrow<E>(
+            a, (unsigned __int64)b, pRet);
+#endif
+    }
+};
+
+template<>
+class LargeIntRegMultiply<signed __int32, unsigned __int64>
+{
+public:
+    // Devolves into ordinary 64-bit calculation
+    static bool RegMultiply(signed __int32 a, const unsigned __int64& b, signed __int32* pRet) SAFEINT_NOTHROW
+    {
+        unsigned __int32 bHigh, bLow;
+        bool fIsNegative = false;
+
+        // Consider that a*b can be broken up into:
+        // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow)
+        // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow)
+        // aHigh == 0 implies:
+        // ( aLow * bHigh * 2^32 ) + ( aLow + bLow )
+        // If the first part is != 0, fail
+
+        bHigh = (unsigned __int32)(b >> 32);
+        bLow = (unsigned __int32)b;
+
+        *pRet = 0;
+
+        if (bHigh != 0 && a != 0) return false;
+
+        if (a < 0)
+        {
+            a = (signed __int32)AbsValueHelper<signed __int32, GetAbsMethod<signed __int32>::method>::Abs(a);
+            fIsNegative = true;
+        }
+
+        unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow;
+
+        if (!fIsNegative)
+        {
+            if (tmp <= (unsigned __int64)IntTraits<signed __int32>::maxInt)
+            {
+                *pRet = (signed __int32)tmp;
+                return true;
+            }
+        }
+        else
+        {
+            if (tmp <= (unsigned __int64)IntTraits<signed __int32>::maxInt + 1)
+            {
+                *pRet = SignedNegation<signed __int32>::Value(tmp);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(signed __int32 a, const unsigned __int64& b, signed __int32* pRet) SAFEINT_CPP_THROW
+    {
+        unsigned __int32 bHigh, bLow;
+        bool fIsNegative = false;
+
+        // Consider that a*b can be broken up into:
+        // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow)
+        // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow)
+
+        bHigh = (unsigned __int32)(b >> 32);
+        bLow = (unsigned __int32)b;
+
+        *pRet = 0;
+
+        if (bHigh != 0 && a != 0) E::SafeIntOnOverflow();
+
+        if (a < 0)
+        {
+            a = (signed __int32)AbsValueHelper<signed __int32, GetAbsMethod<signed __int32>::method>::Abs(a);
+            fIsNegative = true;
+        }
+
+        unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow;
+
+        if (!fIsNegative)
+        {
+            if (tmp <= (unsigned __int64)IntTraits<signed __int32>::maxInt)
+            {
+                *pRet = (signed __int32)tmp;
+                return;
+            }
+        }
+        else
+        {
+            if (tmp <= (unsigned __int64)IntTraits<signed __int32>::maxInt + 1)
+            {
+                *pRet = SignedNegation<signed __int32>::Value(tmp);
+                return;
+            }
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<>
+class LargeIntRegMultiply<unsigned __int32, unsigned __int64>
+{
+public:
+    // Becomes ordinary 64-bit multiplication, intrinsic not needed
+    static bool RegMultiply(unsigned __int32 a, const unsigned __int64& b, unsigned __int32* pRet) SAFEINT_NOTHROW
+    {
+        // Consider that a*b can be broken up into:
+        // (bHigh * 2^32 + bLow) * a
+        // => (bHigh * a * 2^32) + (bLow * a)
+        // In this case, the result must fit into 32-bits
+        // If bHigh != 0 && a != 0, immediate error.
+
+        if ((unsigned __int32)(b >> 32) != 0 && a != 0) return false;
+
+        unsigned __int64 tmp = b * (unsigned __int64)a;
+
+        if ((unsigned __int32)(tmp >> 32) != 0) // overflow
+            return false;
+
+        *pRet = (unsigned __int32)tmp;
+        return true;
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(unsigned __int32 a,
+                                 const unsigned __int64& b,
+                                 unsigned __int32* pRet) SAFEINT_CPP_THROW
+    {
+        if ((unsigned __int32)(b >> 32) != 0 && a != 0) E::SafeIntOnOverflow();
+
+        unsigned __int64 tmp = b * (unsigned __int64)a;
+
+        if ((unsigned __int32)(tmp >> 32) != 0) // overflow
+            E::SafeIntOnOverflow();
+
+        *pRet = (unsigned __int32)tmp;
+    }
+};
+
+template<>
+class LargeIntRegMultiply<unsigned __int32, signed __int64>
+{
+public:
+    static bool RegMultiply(unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet) SAFEINT_NOTHROW
+    {
+        if (b < 0 && a != 0) return false;
+        return LargeIntRegMultiply<unsigned __int32, unsigned __int64>::RegMultiply(a, (unsigned __int64)b, pRet);
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet) SAFEINT_CPP_THROW
+    {
+        if (b < 0 && a != 0) E::SafeIntOnOverflow();
+
+        LargeIntRegMultiply<unsigned __int32, unsigned __int64>::template RegMultiplyThrow<E>(
+            a, (unsigned __int64)b, pRet);
+    }
+};
+
+template<>
+class LargeIntRegMultiply<signed __int64, signed __int64>
+{
+public:
+    static bool RegMultiply(const signed __int64& a, const signed __int64& b, signed __int64* pRet) SAFEINT_NOTHROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        return IntrinsicMultiplyInt64(a, b, pRet);
+#else
+        bool aNegative = false;
+        bool bNegative = false;
+
+        unsigned __int64 tmp;
+        __int64 a1 = a;
+        __int64 b1 = b;
+
+        if (a1 < 0)
+        {
+            aNegative = true;
+            a1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(a1);
+        }
+
+        if (b1 < 0)
+        {
+            bNegative = true;
+            b1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(b1);
+        }
+
+        if (LargeIntRegMultiply<unsigned __int64, unsigned __int64>::RegMultiply(
+                (unsigned __int64)a1, (unsigned __int64)b1, &tmp))
+        {
+            // The unsigned multiplication didn't overflow
+            if (aNegative ^ bNegative)
+            {
+                // Result must be negative
+                if (tmp <= (unsigned __int64)IntTraits<signed __int64>::minInt)
+                {
+                    *pRet = SignedNegation<signed __int64>::Value(tmp);
+                    return true;
+                }
+            }
+            else
+            {
+                // Result must be positive
+                if (tmp <= (unsigned __int64)IntTraits<signed __int64>::maxInt)
+                {
+                    *pRet = (signed __int64)tmp;
+                    return true;
+                }
+            }
+        }
+
+        return false;
+#endif
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(const signed __int64& a,
+                                 const signed __int64& b,
+                                 signed __int64* pRet) SAFEINT_CPP_THROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        if (!IntrinsicMultiplyInt64(a, b, pRet)) E::SafeIntOnOverflow();
+#else
+        bool aNegative = false;
+        bool bNegative = false;
+
+        unsigned __int64 tmp;
+        __int64 a1 = a;
+        __int64 b1 = b;
+
+        if (a1 < 0)
+        {
+            aNegative = true;
+            a1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(a1);
+        }
+
+        if (b1 < 0)
+        {
+            bNegative = true;
+            b1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(b1);
+        }
+
+        LargeIntRegMultiply<unsigned __int64, unsigned __int64>::template RegMultiplyThrow<E>(
+            (unsigned __int64)a1, (unsigned __int64)b1, &tmp);
+
+        // The unsigned multiplication didn't overflow or we'd be in the exception handler
+        if (aNegative ^ bNegative)
+        {
+            // Result must be negative
+            if (tmp <= (unsigned __int64)IntTraits<signed __int64>::minInt)
+            {
+                *pRet = SignedNegation<signed __int64>::Value(tmp);
+                return;
+            }
+        }
+        else
+        {
+            // Result must be positive
+            if (tmp <= (unsigned __int64)IntTraits<signed __int64>::maxInt)
+            {
+                *pRet = (signed __int64)tmp;
+                return;
+            }
+        }
+
+        E::SafeIntOnOverflow();
+#endif
+    }
+};
+
+template<>
+class LargeIntRegMultiply<signed __int64, unsigned __int32>
+{
+public:
+    static bool RegMultiply(const signed __int64& a, unsigned __int32 b, signed __int64* pRet) SAFEINT_NOTHROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        return IntrinsicMultiplyInt64(a, (signed __int64)b, pRet);
+#else
+        bool aNegative = false;
+        unsigned __int64 tmp;
+        __int64 a1 = a;
+
+        if (a1 < 0)
+        {
+            aNegative = true;
+            a1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(a1);
+        }
+
+        if (LargeIntRegMultiply<unsigned __int64, unsigned __int32>::RegMultiply((unsigned __int64)a1, b, &tmp))
+        {
+            // The unsigned multiplication didn't overflow
+            if (aNegative)
+            {
+                // Result must be negative
+                if (tmp <= (unsigned __int64)IntTraits<signed __int64>::minInt)
+                {
+                    *pRet = SignedNegation<signed __int64>::Value(tmp);
+                    return true;
+                }
+            }
+            else
+            {
+                // Result must be positive
+                if (tmp <= (unsigned __int64)IntTraits<signed __int64>::maxInt)
+                {
+                    *pRet = (signed __int64)tmp;
+                    return true;
+                }
+            }
+        }
+
+        return false;
+#endif
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(const signed __int64& a, unsigned __int32 b, signed __int64* pRet) SAFEINT_CPP_THROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        if (!IntrinsicMultiplyInt64(a, (signed __int64)b, pRet)) E::SafeIntOnOverflow();
+#else
+        bool aNegative = false;
+        unsigned __int64 tmp;
+        __int64 a1 = a;
+
+        if (a1 < 0)
+        {
+            aNegative = true;
+            a1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(a1);
+        }
+
+        LargeIntRegMultiply<unsigned __int64, unsigned __int32>::template RegMultiplyThrow<E>(
+            (unsigned __int64)a1, b, &tmp);
+
+        // The unsigned multiplication didn't overflow
+        if (aNegative)
+        {
+            // Result must be negative
+            if (tmp <= (unsigned __int64)IntTraits<signed __int64>::minInt)
+            {
+                *pRet = SignedNegation<signed __int64>::Value(tmp);
+                return;
+            }
+        }
+        else
+        {
+            // Result must be positive
+            if (tmp <= (unsigned __int64)IntTraits<signed __int64>::maxInt)
+            {
+                *pRet = (signed __int64)tmp;
+                return;
+            }
+        }
+
+        E::SafeIntOnOverflow();
+#endif
+    }
+};
+
+template<>
+class LargeIntRegMultiply<signed __int64, signed __int32>
+{
+public:
+    static bool RegMultiply(const signed __int64& a, signed __int32 b, signed __int64* pRet) SAFEINT_NOTHROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        return IntrinsicMultiplyInt64(a, (signed __int64)b, pRet);
+#else
+        bool aNegative = false;
+        bool bNegative = false;
+
+        unsigned __int64 tmp;
+        __int64 a1 = a;
+        __int64 b1 = b;
+
+        if (a1 < 0)
+        {
+            aNegative = true;
+            a1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(a1);
+        }
+
+        if (b1 < 0)
+        {
+            bNegative = true;
+            b1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(b1);
+        }
+
+        if (LargeIntRegMultiply<unsigned __int64, unsigned __int32>::RegMultiply(
+                (unsigned __int64)a1, (unsigned __int32)b1, &tmp))
+        {
+            // The unsigned multiplication didn't overflow
+            if (aNegative ^ bNegative)
+            {
+                // Result must be negative
+                if (tmp <= (unsigned __int64)IntTraits<signed __int64>::minInt)
+                {
+                    *pRet = SignedNegation<signed __int64>::Value(tmp);
+                    return true;
+                }
+            }
+            else
+            {
+                // Result must be positive
+                if (tmp <= (unsigned __int64)IntTraits<signed __int64>::maxInt)
+                {
+                    *pRet = (signed __int64)tmp;
+                    return true;
+                }
+            }
+        }
+
+        return false;
+#endif
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(signed __int64 a, signed __int32 b, signed __int64* pRet) SAFEINT_CPP_THROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        if (!IntrinsicMultiplyInt64(a, (signed __int64)b, pRet)) E::SafeIntOnOverflow();
+#else
+        bool aNegative = false;
+        bool bNegative = false;
+
+        unsigned __int64 tmp;
+
+        if (a < 0)
+        {
+            aNegative = true;
+            a = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(a);
+        }
+
+        if (b < 0)
+        {
+            bNegative = true;
+            b = (signed __int32)AbsValueHelper<signed __int32, GetAbsMethod<signed __int32>::method>::Abs(b);
+        }
+
+        LargeIntRegMultiply<unsigned __int64, unsigned __int32>::template RegMultiplyThrow<E>(
+            (unsigned __int64)a, (unsigned __int32)b, &tmp);
+
+        // The unsigned multiplication didn't overflow
+        if (aNegative ^ bNegative)
+        {
+            // Result must be negative
+            if (tmp <= (unsigned __int64)IntTraits<signed __int64>::minInt)
+            {
+                *pRet = SignedNegation<signed __int64>::Value(tmp);
+                return;
+            }
+        }
+        else
+        {
+            // Result must be positive
+            if (tmp <= (unsigned __int64)IntTraits<signed __int64>::maxInt)
+            {
+                *pRet = (signed __int64)tmp;
+                return;
+            }
+        }
+
+        E::SafeIntOnOverflow();
+#endif
+    }
+};
+
+template<>
+class LargeIntRegMultiply<signed __int32, signed __int64>
+{
+public:
+    static bool RegMultiply(signed __int32 a, const signed __int64& b, signed __int32* pRet) SAFEINT_NOTHROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        __int64 tmp;
+
+        if (IntrinsicMultiplyInt64(a, b, &tmp))
+        {
+            if (tmp > IntTraits<signed __int32>::maxInt || tmp < IntTraits<signed __int32>::minInt)
+            {
+                return false;
+            }
+
+            *pRet = (__int32)tmp;
+            return true;
+        }
+        return false;
+#else
+        bool aNegative = false;
+        bool bNegative = false;
+
+        unsigned __int32 tmp;
+        __int64 b1 = b;
+
+        if (a < 0)
+        {
+            aNegative = true;
+            a = (signed __int32)AbsValueHelper<signed __int32, GetAbsMethod<signed __int32>::method>::Abs(a);
+        }
+
+        if (b1 < 0)
+        {
+            bNegative = true;
+            b1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(b1);
+        }
+
+        if (LargeIntRegMultiply<unsigned __int32, unsigned __int64>::RegMultiply(
+                (unsigned __int32)a, (unsigned __int64)b1, &tmp))
+        {
+            // The unsigned multiplication didn't overflow
+            if (aNegative ^ bNegative)
+            {
+                // Result must be negative
+                if (tmp <= (unsigned __int32)IntTraits<signed __int32>::minInt)
+                {
+                    *pRet = SignedNegation<signed __int32>::Value(tmp);
+                    return true;
+                }
+            }
+            else
+            {
+                // Result must be positive
+                if (tmp <= (unsigned __int32)IntTraits<signed __int32>::maxInt)
+                {
+                    *pRet = (signed __int32)tmp;
+                    return true;
+                }
+            }
+        }
+
+        return false;
+#endif
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(signed __int32 a, const signed __int64& b, signed __int32* pRet) SAFEINT_CPP_THROW
+    {
+#if SAFEINT_USE_INTRINSICS
+        __int64 tmp;
+
+        if (IntrinsicMultiplyInt64(a, b, &tmp))
+        {
+            if (tmp > IntTraits<signed __int32>::maxInt || tmp < IntTraits<signed __int32>::minInt)
+            {
+                E::SafeIntOnOverflow();
+            }
+
+            *pRet = (__int32)tmp;
+            return;
+        }
+        E::SafeIntOnOverflow();
+#else
+        bool aNegative = false;
+        bool bNegative = false;
+
+        unsigned __int32 tmp;
+        signed __int64 b2 = b;
+
+        if (a < 0)
+        {
+            aNegative = true;
+            a = (signed __int32)AbsValueHelper<signed __int32, GetAbsMethod<signed __int32>::method>::Abs(a);
+        }
+
+        if (b < 0)
+        {
+            bNegative = true;
+            b2 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(b2);
+        }
+
+        LargeIntRegMultiply<unsigned __int32, unsigned __int64>::template RegMultiplyThrow<E>(
+            (unsigned __int32)a, (unsigned __int64)b2, &tmp);
+
+        // The unsigned multiplication didn't overflow
+        if (aNegative ^ bNegative)
+        {
+            // Result must be negative
+            if (tmp <= (unsigned __int32)IntTraits<signed __int32>::minInt)
+            {
+                *pRet = SignedNegation<signed __int32>::Value(tmp);
+                return;
+            }
+        }
+        else
+        {
+            // Result must be positive
+            if (tmp <= (unsigned __int32)IntTraits<signed __int32>::maxInt)
+            {
+                *pRet = (signed __int32)tmp;
+                return;
+            }
+        }
+
+        E::SafeIntOnOverflow();
+#endif
+    }
+};
+
+template<>
+class LargeIntRegMultiply<signed __int64, unsigned __int64>
+{
+public:
+    // Leave this one as-is - will call unsigned intrinsic internally
+    static bool RegMultiply(const signed __int64& a, const unsigned __int64& b, signed __int64* pRet) SAFEINT_NOTHROW
+    {
+        bool aNegative = false;
+
+        unsigned __int64 tmp;
+        __int64 a1 = a;
+
+        if (a1 < 0)
+        {
+            aNegative = true;
+            a1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(a1);
+        }
+
+        if (LargeIntRegMultiply<unsigned __int64, unsigned __int64>::RegMultiply(
+                (unsigned __int64)a1, (unsigned __int64)b, &tmp))
+        {
+            // The unsigned multiplication didn't overflow
+            if (aNegative)
+            {
+                // Result must be negative
+                if (tmp <= (unsigned __int64)IntTraits<signed __int64>::minInt)
+                {
+                    *pRet = SignedNegation<signed __int64>::Value(tmp);
+                    return true;
+                }
+            }
+            else
+            {
+                // Result must be positive
+                if (tmp <= (unsigned __int64)IntTraits<signed __int64>::maxInt)
+                {
+                    *pRet = (signed __int64)tmp;
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void RegMultiplyThrow(const signed __int64& a,
+                                 const unsigned __int64& b,
+                                 signed __int64* pRet) SAFEINT_CPP_THROW
+    {
+        bool aNegative = false;
+        unsigned __int64 tmp;
+        __int64 a1 = a;
+
+        if (a1 < 0)
+        {
+            aNegative = true;
+            a1 = (signed __int64)AbsValueHelper<signed __int64, GetAbsMethod<signed __int64>::method>::Abs(a1);
+        }
+
+        if (LargeIntRegMultiply<unsigned __int64, unsigned __int64>::RegMultiply(
+                (unsigned __int64)a1, (unsigned __int64)b, &tmp))
+        {
+            // The unsigned multiplication didn't overflow
+            if (aNegative)
+            {
+                // Result must be negative
+                if (tmp <= (unsigned __int64)IntTraits<signed __int64>::minInt)
+                {
+                    *pRet = SignedNegation<signed __int64>::Value(tmp);
+                    return;
+                }
+            }
+            else
+            {
+                // Result must be positive
+                if (tmp <= (unsigned __int64)IntTraits<signed __int64>::maxInt)
+                {
+                    *pRet = (signed __int64)tmp;
+                    return;
+                }
+            }
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+// In all of the following functions where LargeIntRegMultiply methods are called,
+// we need to properly transition types. The methods need __int64, __int32, etc.
+// but the variables being passed to us could be long long, long int, or long, depending on
+// the compiler. Microsoft compiler knows that long long is the same type as __int64, but gcc doesn't
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_Uint64Uint64>
+{
+public:
+    // T, U are unsigned __int64
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isUint64 && IntTraits<U>::isUint64);
+        unsigned __int64 t1 = t;
+        unsigned __int64 u1 = u;
+        return LargeIntRegMultiply<unsigned __int64, unsigned __int64>::RegMultiply(
+            t1, u1, reinterpret_cast<unsigned __int64*>(&ret));
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const unsigned __int64& t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isUint64 && IntTraits<U>::isUint64);
+        unsigned __int64 t1 = t;
+        unsigned __int64 u1 = u;
+        LargeIntRegMultiply<unsigned __int64, unsigned __int64>::template RegMultiplyThrow<E>(
+            t1, u1, reinterpret_cast<unsigned __int64*>(&ret));
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_Uint64Uint>
+{
+public:
+    // T is unsigned __int64
+    // U is any unsigned int 32-bit or less
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isUint64);
+        unsigned __int64 t1 = t;
+        return LargeIntRegMultiply<unsigned __int64, unsigned __int32>::RegMultiply(
+            t1, (unsigned __int32)u, reinterpret_cast<unsigned __int64*>(&ret));
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isUint64);
+        unsigned __int64 t1 = t;
+        LargeIntRegMultiply<unsigned __int64, unsigned __int32>::template RegMultiplyThrow<E>(
+            t1, (unsigned __int32)u, reinterpret_cast<unsigned __int64*>(&ret));
+    }
+};
+
+// converse of the previous function
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_UintUint64>
+{
+public:
+    // T is any unsigned int up to 32-bit
+    // U is unsigned __int64
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<U>::isUint64);
+        unsigned __int64 u1 = u;
+        unsigned __int32 tmp;
+
+        if (LargeIntRegMultiply<unsigned __int32, unsigned __int64>::RegMultiply(t, u1, &tmp) &&
+            SafeCastHelper<T, unsigned __int32, GetCastMethod<T, unsigned __int32>::method>::Cast(tmp, ret))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<U>::isUint64);
+        unsigned __int64 u1 = u;
+        unsigned __int32 tmp;
+
+        LargeIntRegMultiply<unsigned __int32, unsigned __int64>::template RegMultiplyThrow<E>(t, u1, &tmp);
+        SafeCastHelper<T, unsigned __int32, GetCastMethod<T, unsigned __int32>::method>::template CastThrow<E>(tmp,
+                                                                                                               ret);
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_Uint64Int>
+{
+public:
+    // T is unsigned __int64
+    // U is any signed int, up to 64-bit
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isUint64);
+        unsigned __int64 t1 = t;
+        return LargeIntRegMultiply<unsigned __int64, signed __int32>::RegMultiply(
+            t1, (signed __int32)u, reinterpret_cast<unsigned __int64*>(&ret));
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isUint64);
+        unsigned __int64 t1 = t;
+        LargeIntRegMultiply<unsigned __int64, signed __int32>::template RegMultiplyThrow<E>(
+            t1, (signed __int32)u, reinterpret_cast<unsigned __int64*>(&ret));
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_Uint64Int64>
+{
+public:
+    // T is unsigned __int64
+    // U is __int64
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isUint64 && IntTraits<U>::isInt64);
+        unsigned __int64 t1 = t;
+        __int64 u1 = u;
+        return LargeIntRegMultiply<unsigned __int64, __int64>::RegMultiply(
+            t1, u1, reinterpret_cast<unsigned __int64*>(&ret));
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isUint64 && IntTraits<U>::isInt64);
+        unsigned __int64 t1 = t;
+        __int64 u1 = u;
+        LargeIntRegMultiply<unsigned __int64, __int64>::template RegMultiplyThrow<E>(
+            t1, u1, reinterpret_cast<unsigned __int64*>(&ret));
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_UintInt64>
+{
+public:
+    // T is unsigned up to 32-bit
+    // U is __int64
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<U>::isInt64);
+        __int64 u1 = u;
+        unsigned __int32 tmp;
+
+        if (LargeIntRegMultiply<unsigned __int32, __int64>::RegMultiply((unsigned __int32)t, u1, &tmp) &&
+            SafeCastHelper<T, unsigned __int32, GetCastMethod<T, unsigned __int32>::method>::Cast(tmp, ret))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<U>::isInt64);
+        __int64 u1 = u;
+        unsigned __int32 tmp;
+
+        LargeIntRegMultiply<unsigned __int32, __int64>::template RegMultiplyThrow<E>((unsigned __int32)t, u1, &tmp);
+        SafeCastHelper<T, unsigned __int32, GetCastMethod<T, unsigned __int32>::method>::template CastThrow<E>(tmp,
+                                                                                                               ret);
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_Int64Uint>
+{
+public:
+    // T is __int64
+    // U is unsigned up to 32-bit
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64);
+        __int64 t1 = t;
+        return LargeIntRegMultiply<__int64, unsigned __int32>::RegMultiply(
+            t1, (unsigned __int32)u, reinterpret_cast<__int64*>(&ret));
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64);
+        __int64 t1 = t;
+        LargeIntRegMultiply<__int64, unsigned __int32>::template RegMultiplyThrow<E>(
+            t1, (unsigned __int32)u, reinterpret_cast<__int64*>(&ret));
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_Int64Int64>
+{
+public:
+    // T, U are __int64
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64 && IntTraits<U>::isInt64);
+        __int64 t1 = t;
+        __int64 u1 = u;
+        return LargeIntRegMultiply<__int64, __int64>::RegMultiply(t1, u1, reinterpret_cast<__int64*>(&ret));
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64 && IntTraits<U>::isInt64);
+        __int64 t1 = t;
+        __int64 u1 = u;
+        LargeIntRegMultiply<__int64, __int64>::template RegMultiplyThrow<E>(t1, u1, reinterpret_cast<__int64*>(&ret));
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_Int64Int>
+{
+public:
+    // T is __int64
+    // U is signed up to 32-bit
+    static bool Multiply(const T& t, U u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64);
+        __int64 t1 = t;
+        return LargeIntRegMultiply<__int64, __int32>::RegMultiply(t1, (__int32)u, reinterpret_cast<__int64*>(&ret));
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const __int64& t, U u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64);
+        __int64 t1 = t;
+        LargeIntRegMultiply<__int64, __int32>::template RegMultiplyThrow<E>(
+            t1, (__int32)u, reinterpret_cast<__int64*>(&ret));
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_IntUint64>
+{
+public:
+    // T is signed up to 32-bit
+    // U is unsigned __int64
+    static bool Multiply(T t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<U>::isUint64);
+        unsigned __int64 u1 = u;
+        __int32 tmp;
+
+        if (LargeIntRegMultiply<__int32, unsigned __int64>::RegMultiply((__int32)t, u1, &tmp) &&
+            SafeCastHelper<T, __int32, GetCastMethod<T, __int32>::method>::Cast(tmp, ret))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void MultiplyThrow(T t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<U>::isUint64);
+        unsigned __int64 u1 = u;
+        __int32 tmp;
+
+        LargeIntRegMultiply<__int32, unsigned __int64>::template RegMultiplyThrow<E>((__int32)t, u1, &tmp);
+        SafeCastHelper<T, __int32, GetCastMethod<T, __int32>::method>::template CastThrow<E>(tmp, ret);
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_Int64Uint64>
+{
+public:
+    // T is __int64
+    // U is unsigned __int64
+    static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64 && IntTraits<U>::isUint64);
+        __int64 t1 = t;
+        unsigned __int64 u1 = u;
+        return LargeIntRegMultiply<__int64, unsigned __int64>::RegMultiply(t1, u1, reinterpret_cast<__int64*>(&ret));
+    }
+
+    template<typename E>
+    static void MultiplyThrow(const __int64& t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64 && IntTraits<U>::isUint64);
+        __int64 t1 = t;
+        unsigned __int64 u1 = u;
+        LargeIntRegMultiply<__int64, unsigned __int64>::template RegMultiplyThrow<E>(
+            t1, u1, reinterpret_cast<__int64*>(&ret));
+    }
+};
+
+template<typename T, typename U>
+class MultiplicationHelper<T, U, MultiplicationState_IntInt64>
+{
+public:
+    // T is signed, up to 32-bit
+    // U is __int64
+    static bool Multiply(T t, const U& u, T& ret) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<U>::isInt64);
+        __int64 u1 = u;
+        __int32 tmp;
+
+        if (LargeIntRegMultiply<__int32, __int64>::RegMultiply((__int32)t, u1, &tmp) &&
+            SafeCastHelper<T, __int32, GetCastMethod<T, __int32>::method>::Cast(tmp, ret))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void MultiplyThrow(T t, const U& u, T& ret) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<U>::isInt64);
+        __int64 u1 = u;
+        __int32 tmp;
+
+        LargeIntRegMultiply<__int32, __int64>::template RegMultiplyThrow<E>((__int32)t, u1, &tmp);
+        SafeCastHelper<T, __int32, GetCastMethod<T, __int32>::method>::template CastThrow<E>(tmp, ret);
+    }
+};
+
+enum DivisionState
+{
+    DivisionState_OK,
+    DivisionState_UnsignedSigned,
+    DivisionState_SignedUnsigned32,
+    DivisionState_SignedUnsigned64,
+    DivisionState_SignedUnsigned,
+    DivisionState_SignedSigned
+};
+
+template<typename T, typename U>
+class DivisionMethod
+{
+public:
+    enum
+    {
+        method =
+            (SafeIntCompare<T, U>::isBothUnsigned
+                 ? DivisionState_OK
+                 : (!IntTraits<T>::isSigned && IntTraits<U>::isSigned)
+                       ? DivisionState_UnsignedSigned
+                       : (IntTraits<T>::isSigned && IntTraits<U>::isUint32 && IntTraits<T>::isLT64Bit)
+                             ? DivisionState_SignedUnsigned32
+                             : (IntTraits<T>::isSigned && IntTraits<U>::isUint64)
+                                   ? DivisionState_SignedUnsigned64
+                                   : (IntTraits<T>::isSigned && !IntTraits<U>::isSigned) ? DivisionState_SignedUnsigned
+                                                                                         : DivisionState_SignedSigned)
+    };
+};
+
+template<typename T, typename U, int state>
+class DivisionHelper;
+
+template<typename T, typename U>
+class DivisionHelper<T, U, DivisionState_OK>
+{
+public:
+    static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+    {
+        if (u == 0) return SafeIntDivideByZero;
+
+        if (t == 0)
+        {
+            result = 0;
+            return SafeIntNoError;
+        }
+
+        result = (T)(t / u);
+        return SafeIntNoError;
+    }
+
+    template<typename E>
+    static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW
+    {
+        if (u == 0) E::SafeIntOnDivZero();
+
+        if (t == 0)
+        {
+            result = 0;
+            return;
+        }
+
+        result = (T)(t / u);
+    }
+};
+
+template<typename T, typename U>
+class DivisionHelper<T, U, DivisionState_UnsignedSigned>
+{
+public:
+    static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+    {
+        if (u == 0) return SafeIntDivideByZero;
+
+        if (t == 0)
+        {
+            result = 0;
+            return SafeIntNoError;
+        }
+
+        if (u > 0)
+        {
+            result = (T)(t / u);
+            return SafeIntNoError;
+        }
+
+        // it is always an error to try and divide an unsigned number by a negative signed number
+        // unless u is bigger than t
+        if (AbsValueHelper<U, GetAbsMethod<U>::method>::Abs(u) > t)
+        {
+            result = 0;
+            return SafeIntNoError;
+        }
+
+        return SafeIntArithmeticOverflow;
+    }
+
+    template<typename E>
+    static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW
+    {
+        if (u == 0) E::SafeIntOnDivZero();
+
+        if (t == 0)
+        {
+            result = 0;
+            return;
+        }
+
+        if (u > 0)
+        {
+            result = (T)(t / u);
+            return;
+        }
+
+        // it is always an error to try and divide an unsigned number by a negative signed number
+        // unless u is bigger than t
+        if (AbsValueHelper<U, GetAbsMethod<U>::method>::Abs(u) > t)
+        {
+            result = 0;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class DivisionHelper<T, U, DivisionState_SignedUnsigned32>
+{
+public:
+    static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+    {
+        if (u == 0) return SafeIntDivideByZero;
+
+        if (t == 0)
+        {
+            result = 0;
+            return SafeIntNoError;
+        }
+
+        // Test for t > 0
+        // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors
+        // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional
+
+        if (t > 0)
+            result = (T)(t / u);
+        else
+            result = (T)((__int64)t / (__int64)u);
+
+        return SafeIntNoError;
+    }
+
+    template<typename E>
+    static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW
+    {
+        if (u == 0)
+        {
+            E::SafeIntOnDivZero();
+        }
+
+        if (t == 0)
+        {
+            result = 0;
+            return;
+        }
+
+        // Test for t > 0
+        // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors
+        // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional
+
+        if (t > 0)
+            result = (T)(t / u);
+        else
+            result = (T)((__int64)t / (__int64)u);
+    }
+};
+
+template<typename T, typename U>
+class DivisionHelper<T, U, DivisionState_SignedUnsigned64>
+{
+public:
+    static SafeIntError Divide(const T& t, const unsigned __int64& u, T& result) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<U>::isUint64);
+
+        if (u == 0)
+        {
+            return SafeIntDivideByZero;
+        }
+
+        if (t == 0)
+        {
+            result = 0;
+            return SafeIntNoError;
+        }
+
+        if (u <= (unsigned __int64)IntTraits<T>::maxInt)
+        {
+            // Else u can safely be cast to T
+            if (CompileConst<sizeof(T) < sizeof(__int64)>::Value())
+                result = (T)((int)t / (int)u);
+            else
+                result = (T)((__int64)t / (__int64)u);
+        }
+        else // Corner case
+            if (t == IntTraits<T>::minInt && u == (unsigned __int64)IntTraits<T>::minInt)
+        {
+            // Min int divided by it's own magnitude is -1
+            result = -1;
+        }
+        else
+        {
+            result = 0;
+        }
+        return SafeIntNoError;
+    }
+
+    template<typename E>
+    static void DivideThrow(const T& t, const unsigned __int64& u, T& result) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<U>::isUint64);
+
+        if (u == 0)
+        {
+            E::SafeIntOnDivZero();
+        }
+
+        if (t == 0)
+        {
+            result = 0;
+            return;
+        }
+
+        if (u <= (unsigned __int64)IntTraits<T>::maxInt)
+        {
+            // Else u can safely be cast to T
+            if (CompileConst<sizeof(T) < sizeof(__int64)>::Value())
+                result = (T)((int)t / (int)u);
+            else
+                result = (T)((__int64)t / (__int64)u);
+        }
+        else // Corner case
+            if (t == IntTraits<T>::minInt && u == (unsigned __int64)IntTraits<T>::minInt)
+        {
+            // Min int divided by it's own magnitude is -1
+            result = -1;
+        }
+        else
+        {
+            result = 0;
+        }
+    }
+};
+
+template<typename T, typename U>
+class DivisionHelper<T, U, DivisionState_SignedUnsigned>
+{
+public:
+    // T is any signed, U is unsigned and smaller than 32-bit
+    // In this case, standard operator casting is correct
+    static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+    {
+        if (u == 0)
+        {
+            return SafeIntDivideByZero;
+        }
+
+        if (t == 0)
+        {
+            result = 0;
+            return SafeIntNoError;
+        }
+
+        result = (T)(t / u);
+        return SafeIntNoError;
+    }
+
+    template<typename E>
+    static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW
+    {
+        if (u == 0)
+        {
+            E::SafeIntOnDivZero();
+        }
+
+        if (t == 0)
+        {
+            result = 0;
+            return;
+        }
+
+        result = (T)(t / u);
+    }
+};
+
+template<typename T, typename U>
+class DivisionHelper<T, U, DivisionState_SignedSigned>
+{
+public:
+    static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+    {
+        if (u == 0)
+        {
+            return SafeIntDivideByZero;
+        }
+
+        if (t == 0)
+        {
+            result = 0;
+            return SafeIntNoError;
+        }
+
+        // Must test for corner case
+        if (t == IntTraits<T>::minInt && u == (U)-1) return SafeIntArithmeticOverflow;
+
+        result = (T)(t / u);
+        return SafeIntNoError;
+    }
+
+    template<typename E>
+    static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW
+    {
+        if (u == 0)
+        {
+            E::SafeIntOnDivZero();
+        }
+
+        if (t == 0)
+        {
+            result = 0;
+            return;
+        }
+
+        // Must test for corner case
+        if (t == IntTraits<T>::minInt && u == (U)-1) E::SafeIntOnOverflow();
+
+        result = (T)(t / u);
+    }
+};
+
+enum AdditionState
+{
+    AdditionState_CastIntCheckMax,
+    AdditionState_CastUintCheckOverflow,
+    AdditionState_CastUintCheckOverflowMax,
+    AdditionState_CastUint64CheckOverflow,
+    AdditionState_CastUint64CheckOverflowMax,
+    AdditionState_CastIntCheckSafeIntMinMax,
+    AdditionState_CastInt64CheckSafeIntMinMax,
+    AdditionState_CastInt64CheckMax,
+    AdditionState_CastUint64CheckSafeIntMinMax,
+    AdditionState_CastUint64CheckSafeIntMinMax2,
+    AdditionState_CastInt64CheckOverflow,
+    AdditionState_CastInt64CheckOverflowSafeIntMinMax,
+    AdditionState_CastInt64CheckOverflowMax,
+    AdditionState_ManualCheckInt64Uint64,
+    AdditionState_ManualCheck,
+    AdditionState_Error
+};
+
+template<typename T, typename U>
+class AdditionMethod
+{
+public:
+    enum
+    {
+        // unsigned-unsigned
+        method =
+            (IntRegion<T, U>::IntZone_UintLT32_UintLT32
+                 ? AdditionState_CastIntCheckMax
+                 : (IntRegion<T, U>::IntZone_Uint32_UintLT64)
+                       ? AdditionState_CastUintCheckOverflow
+                       : (IntRegion<T, U>::IntZone_UintLT32_Uint32)
+                             ? AdditionState_CastUintCheckOverflowMax
+                             : (IntRegion<T, U>::IntZone_Uint64_Uint)
+                                   ? AdditionState_CastUint64CheckOverflow
+                                   : (IntRegion<T, U>::IntZone_UintLT64_Uint64)
+                                         ? AdditionState_CastUint64CheckOverflowMax
+                                         :
+                                         // unsigned-signed
+                                         (IntRegion<T, U>::IntZone_UintLT32_IntLT32)
+                                             ? AdditionState_CastIntCheckSafeIntMinMax
+                                             : (IntRegion<T, U>::IntZone_Uint32_IntLT64 ||
+                                                IntRegion<T, U>::IntZone_UintLT32_Int32)
+                                                   ? AdditionState_CastInt64CheckSafeIntMinMax
+                                                   : (IntRegion<T, U>::IntZone_Uint64_Int ||
+                                                      IntRegion<T, U>::IntZone_Uint64_Int64)
+                                                         ? AdditionState_CastUint64CheckSafeIntMinMax
+                                                         : (IntRegion<T, U>::IntZone_UintLT64_Int64)
+                                                               ? AdditionState_CastUint64CheckSafeIntMinMax2
+                                                               :
+                                                               // signed-signed
+                                                               (IntRegion<T, U>::IntZone_IntLT32_IntLT32)
+                                                                   ? AdditionState_CastIntCheckSafeIntMinMax
+                                                                   : (IntRegion<T, U>::IntZone_Int32_IntLT64 ||
+                                                                      IntRegion<T, U>::IntZone_IntLT32_Int32)
+                                                                         ? AdditionState_CastInt64CheckSafeIntMinMax
+                                                                         : (IntRegion<T, U>::IntZone_Int64_Int ||
+                                                                            IntRegion<T, U>::IntZone_Int64_Int64)
+                                                                               ? AdditionState_CastInt64CheckOverflow
+                                                                               : (IntRegion<T,
+                                                                                            U>::IntZone_IntLT64_Int64)
+                                                                                     ? AdditionState_CastInt64CheckOverflowSafeIntMinMax
+                                                                                     :
+                                                                                     // signed-unsigned
+                                                                                     (IntRegion<T, U>::
+                                                                                          IntZone_IntLT32_UintLT32)
+                                                                                         ? AdditionState_CastIntCheckMax
+                                                                                         : (IntRegion<T, U>::
+                                                                                                IntZone_Int32_UintLT32 ||
+                                                                                            IntRegion<T, U>::
+                                                                                                IntZone_IntLT64_Uint32)
+                                                                                               ? AdditionState_CastInt64CheckMax
+                                                                                               : (IntRegion<T, U>::
+                                                                                                      IntZone_Int64_UintLT64)
+                                                                                                     ? AdditionState_CastInt64CheckOverflowMax
+                                                                                                     : (IntRegion<T,
+                                                                                                                  U>::
+                                                                                                            IntZone_Int64_Uint64)
+                                                                                                           ? AdditionState_ManualCheckInt64Uint64
+                                                                                                           : (IntRegion<
+                                                                                                                 T,
+                                                                                                                 U>::
+                                                                                                                  IntZone_Int_Uint64)
+                                                                                                                 ? AdditionState_ManualCheck
+                                                                                                                 : AdditionState_Error)
+    };
+};
+
+template<typename T, typename U, int method>
+class AdditionHelper;
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastIntCheckMax>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // 16-bit or less unsigned addition
+        __int32 tmp = lhs + rhs;
+
+        if (tmp <= (__int32)IntTraits<T>::maxInt)
+        {
+            result = (T)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // 16-bit or less unsigned addition
+        __int32 tmp = lhs + rhs;
+
+        if (tmp <= (__int32)IntTraits<T>::maxInt)
+        {
+            result = (T)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastUintCheckOverflow>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // 32-bit or less - both are unsigned
+        unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs;
+
+        // we added didn't get smaller
+        if (tmp >= lhs)
+        {
+            result = (T)tmp;
+            return true;
+        }
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // 32-bit or less - both are unsigned
+        unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs;
+
+        // we added didn't get smaller
+        if (tmp >= lhs)
+        {
+            result = (T)tmp;
+            return;
+        }
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastUintCheckOverflowMax>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // 32-bit or less - both are unsigned
+        unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs;
+
+        // We added and it didn't get smaller or exceed maxInt
+        if (tmp >= lhs && tmp <= IntTraits<T>::maxInt)
+        {
+            result = (T)tmp;
+            return true;
+        }
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // 32-bit or less - both are unsigned
+        unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs;
+
+        // We added and it didn't get smaller or exceed maxInt
+        if (tmp >= lhs && tmp <= IntTraits<T>::maxInt)
+        {
+            result = (T)tmp;
+            return;
+        }
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastUint64CheckOverflow>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs unsigned __int64, rhs unsigned
+        unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
+
+        // We added and it didn't get smaller
+        if (tmp >= lhs)
+        {
+            result = (T)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs unsigned __int64, rhs unsigned
+        unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
+
+        // We added and it didn't get smaller
+        if (tmp >= lhs)
+        {
+            result = (T)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastUint64CheckOverflowMax>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs unsigned __int64, rhs unsigned
+        unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
+
+        // We added and it didn't get smaller
+        if (tmp >= lhs && tmp <= IntTraits<T>::maxInt)
+        {
+            result = (T)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs unsigned __int64, rhs unsigned
+        unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
+
+        // We added and it didn't get smaller
+        if (tmp >= lhs && tmp <= IntTraits<T>::maxInt)
+        {
+            result = (T)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastIntCheckSafeIntMinMax>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // 16-bit or less - one or both are signed
+        __int32 tmp = lhs + rhs;
+
+        if (tmp <= (__int32)IntTraits<T>::maxInt && tmp >= (__int32)IntTraits<T>::minInt)
+        {
+            result = (T)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // 16-bit or less - one or both are signed
+        __int32 tmp = lhs + rhs;
+
+        if (tmp <= (__int32)IntTraits<T>::maxInt && tmp >= (__int32)IntTraits<T>::minInt)
+        {
+            result = (T)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastInt64CheckSafeIntMinMax>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // 32-bit or less - one or both are signed
+        __int64 tmp = (__int64)lhs + (__int64)rhs;
+
+        if (tmp <= (__int64)IntTraits<T>::maxInt && tmp >= (__int64)IntTraits<T>::minInt)
+        {
+            result = (T)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // 32-bit or less - one or both are signed
+        __int64 tmp = (__int64)lhs + (__int64)rhs;
+
+        if (tmp <= (__int64)IntTraits<T>::maxInt && tmp >= (__int64)IntTraits<T>::minInt)
+        {
+            result = (T)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastInt64CheckMax>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // 32-bit or less - lhs signed, rhs unsigned
+        __int64 tmp = (__int64)lhs + (__int64)rhs;
+
+        if (tmp <= IntTraits<T>::maxInt)
+        {
+            result = (T)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // 32-bit or less - lhs signed, rhs unsigned
+        __int64 tmp = (__int64)lhs + (__int64)rhs;
+
+        if (tmp <= IntTraits<T>::maxInt)
+        {
+            result = (T)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastUint64CheckSafeIntMinMax>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is unsigned __int64, rhs signed
+        unsigned __int64 tmp;
+
+        if (rhs < 0)
+        {
+            // So we're effectively subtracting
+            tmp = AbsValueHelper<U, GetAbsMethod<U>::method>::Abs(rhs);
+
+            if (tmp <= lhs)
+            {
+                result = lhs - tmp;
+                return true;
+            }
+        }
+        else
+        {
+            // now we know that rhs can be safely cast into an unsigned __int64
+            tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
+
+            // We added and it did not become smaller
+            if (tmp >= lhs)
+            {
+                result = (T)tmp;
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is unsigned __int64, rhs signed
+        unsigned __int64 tmp;
+
+        if (rhs < 0)
+        {
+            // So we're effectively subtracting
+            tmp = AbsValueHelper<U, GetAbsMethod<U>::method>::Abs(rhs);
+
+            if (tmp <= lhs)
+            {
+                result = lhs - tmp;
+                return;
+            }
+        }
+        else
+        {
+            // now we know that rhs can be safely cast into an unsigned __int64
+            tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
+
+            // We added and it did not become smaller
+            if (tmp >= lhs)
+            {
+                result = (T)tmp;
+                return;
+            }
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastUint64CheckSafeIntMinMax2>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is unsigned and < 64-bit, rhs signed __int64
+        if (rhs < 0)
+        {
+            if (lhs >= ~(unsigned __int64)(rhs) + 1) // negation is safe, since rhs is 64-bit
+            {
+                result = (T)(lhs + rhs);
+                return true;
+            }
+        }
+        else
+        {
+            // now we know that rhs can be safely cast into an unsigned __int64
+            unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
+
+            // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff
+            // it is not possible for the operation above to overflow, so just check max
+            if (tmp <= IntTraits<T>::maxInt)
+            {
+                result = (T)tmp;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is unsigned and < 64-bit, rhs signed __int64
+        if (rhs < 0)
+        {
+            if (lhs >= ~(unsigned __int64)(rhs) + 1) // negation is safe, since rhs is 64-bit
+            {
+                result = (T)(lhs + rhs);
+                return;
+            }
+        }
+        else
+        {
+            // now we know that rhs can be safely cast into an unsigned __int64
+            unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
+
+            // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff
+            // it is not possible for the operation above to overflow, so just check max
+            if (tmp <= IntTraits<T>::maxInt)
+            {
+                result = (T)tmp;
+                return;
+            }
+        }
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastInt64CheckOverflow>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is signed __int64, rhs signed
+        __int64 tmp = (__int64)((unsigned __int64)lhs + (unsigned __int64)rhs);
+
+        if (lhs >= 0)
+        {
+            // mixed sign cannot overflow
+            if (rhs >= 0 && tmp < lhs) return false;
+        }
+        else
+        {
+            // lhs negative
+            if (rhs < 0 && tmp > lhs) return false;
+        }
+
+        result = (T)tmp;
+        return true;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is signed __int64, rhs signed
+        __int64 tmp = (__int64)((unsigned __int64)lhs + (unsigned __int64)rhs);
+
+        if (lhs >= 0)
+        {
+            // mixed sign cannot overflow
+            if (rhs >= 0 && tmp < lhs) E::SafeIntOnOverflow();
+        }
+        else
+        {
+            // lhs negative
+            if (rhs < 0 && tmp > lhs) E::SafeIntOnOverflow();
+        }
+
+        result = (T)tmp;
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastInt64CheckOverflowSafeIntMinMax>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // rhs is signed __int64, lhs signed
+        __int64 tmp;
+
+        if (AdditionHelper<__int64, __int64, AdditionState_CastInt64CheckOverflow>::Addition(
+                (__int64)lhs, (__int64)rhs, tmp) &&
+            tmp <= IntTraits<T>::maxInt && tmp >= IntTraits<T>::minInt)
+        {
+            result = (T)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // rhs is signed __int64, lhs signed
+        __int64 tmp;
+
+        AdditionHelper<__int64, __int64, AdditionState_CastInt64CheckOverflow>::AdditionThrow<E>(
+            (__int64)lhs, (__int64)rhs, tmp);
+
+        if (tmp <= IntTraits<T>::maxInt && tmp >= IntTraits<T>::minInt)
+        {
+            result = (T)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_CastInt64CheckOverflowMax>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is signed __int64, rhs unsigned < 64-bit
+        unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
+
+        if ((__int64)tmp >= lhs)
+        {
+            result = (T)(__int64)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is signed __int64, rhs unsigned < 64-bit
+        // Some compilers get optimization-happy, let's thwart them
+
+        unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
+
+        if ((__int64)tmp >= lhs)
+        {
+            result = (T)(__int64)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_ManualCheckInt64Uint64>
+{
+public:
+    static bool Addition(const __int64& lhs, const unsigned __int64& rhs, __int64& result) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64 && IntTraits<U>::isUint64);
+        // rhs is unsigned __int64, lhs __int64
+        // cast everything to unsigned, perform addition, then
+        // cast back for check - this is done to stop optimizers from removing the code
+        unsigned __int64 tmp = (unsigned __int64)lhs + rhs;
+
+        if ((__int64)tmp >= lhs)
+        {
+            result = (__int64)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const __int64& lhs, const unsigned __int64& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64 && IntTraits<U>::isUint64);
+        // rhs is unsigned __int64, lhs __int64
+        unsigned __int64 tmp = (unsigned __int64)lhs + rhs;
+
+        if ((__int64)tmp >= lhs)
+        {
+            result = (__int64)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class AdditionHelper<T, U, AdditionState_ManualCheck>
+{
+public:
+    static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // rhs is unsigned __int64, lhs signed, 32-bit or less
+        if ((unsigned __int32)(rhs >> 32) == 0)
+        {
+            // Now it just happens to work out that the standard behavior does what we want
+            // Adding explicit casts to show exactly what's happening here
+            // Note - this is tweaked to keep optimizers from tossing out the code.
+            unsigned __int32 tmp = (unsigned __int32)rhs + (unsigned __int32)lhs;
+
+            if ((__int32)tmp >= lhs &&
+                SafeCastHelper<T, __int32, GetCastMethod<T, __int32>::method>::Cast((__int32)tmp, result))
+                return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // rhs is unsigned __int64, lhs signed, 32-bit or less
+
+        if ((unsigned __int32)(rhs >> 32) == 0)
+        {
+            // Now it just happens to work out that the standard behavior does what we want
+            // Adding explicit casts to show exactly what's happening here
+            unsigned __int32 tmp = (unsigned __int32)rhs + (unsigned __int32)lhs;
+
+            if ((__int32)tmp >= lhs)
+            {
+                SafeCastHelper<T, __int32, GetCastMethod<T, __int32>::method>::template CastThrow<E>((__int32)tmp,
+                                                                                                     result);
+                return;
+            }
+        }
+        E::SafeIntOnOverflow();
+    }
+};
+
+enum SubtractionState
+{
+    SubtractionState_BothUnsigned,
+    SubtractionState_CastIntCheckSafeIntMinMax,
+    SubtractionState_CastIntCheckMin,
+    SubtractionState_CastInt64CheckSafeIntMinMax,
+    SubtractionState_CastInt64CheckMin,
+    SubtractionState_Uint64Int,
+    SubtractionState_UintInt64,
+    SubtractionState_Int64Int,
+    SubtractionState_IntInt64,
+    SubtractionState_Int64Uint,
+    SubtractionState_IntUint64,
+    SubtractionState_Int64Uint64,
+    // states for SubtractionMethod2
+    SubtractionState_BothUnsigned2,
+    SubtractionState_CastIntCheckSafeIntMinMax2,
+    SubtractionState_CastInt64CheckSafeIntMinMax2,
+    SubtractionState_Uint64Int2,
+    SubtractionState_UintInt642,
+    SubtractionState_Int64Int2,
+    SubtractionState_IntInt642,
+    SubtractionState_Int64Uint2,
+    SubtractionState_IntUint642,
+    SubtractionState_Int64Uint642,
+    SubtractionState_Error
+};
+
+template<typename T, typename U>
+class SubtractionMethod
+{
+public:
+    enum
+    {
+        // unsigned-unsigned
+        method =
+            ((IntRegion<T, U>::IntZone_UintLT32_UintLT32 || (IntRegion<T, U>::IntZone_Uint32_UintLT64) ||
+              (IntRegion<T, U>::IntZone_UintLT32_Uint32) || (IntRegion<T, U>::IntZone_Uint64_Uint) ||
+              (IntRegion<T, U>::IntZone_UintLT64_Uint64))
+                 ? SubtractionState_BothUnsigned
+                 :
+                 // unsigned-signed
+                 (IntRegion<T, U>::IntZone_UintLT32_IntLT32)
+                     ? SubtractionState_CastIntCheckSafeIntMinMax
+                     : (IntRegion<T, U>::IntZone_Uint32_IntLT64 || IntRegion<T, U>::IntZone_UintLT32_Int32)
+                           ? SubtractionState_CastInt64CheckSafeIntMinMax
+                           : (IntRegion<T, U>::IntZone_Uint64_Int || IntRegion<T, U>::IntZone_Uint64_Int64)
+                                 ? SubtractionState_Uint64Int
+                                 : (IntRegion<T, U>::IntZone_UintLT64_Int64) ? SubtractionState_UintInt64 :
+                                                                             // signed-signed
+                                       (IntRegion<T, U>::IntZone_IntLT32_IntLT32)
+                                           ? SubtractionState_CastIntCheckSafeIntMinMax
+                                           : (IntRegion<T, U>::IntZone_Int32_IntLT64 ||
+                                              IntRegion<T, U>::IntZone_IntLT32_Int32)
+                                                 ? SubtractionState_CastInt64CheckSafeIntMinMax
+                                                 : (IntRegion<T, U>::IntZone_Int64_Int ||
+                                                    IntRegion<T, U>::IntZone_Int64_Int64)
+                                                       ? SubtractionState_Int64Int
+                                                       : (IntRegion<T, U>::IntZone_IntLT64_Int64)
+                                                             ? SubtractionState_IntInt64
+                                                             :
+                                                             // signed-unsigned
+                                                             (IntRegion<T, U>::IntZone_IntLT32_UintLT32)
+                                                                 ? SubtractionState_CastIntCheckMin
+                                                                 : (IntRegion<T, U>::IntZone_Int32_UintLT32 ||
+                                                                    IntRegion<T, U>::IntZone_IntLT64_Uint32)
+                                                                       ? SubtractionState_CastInt64CheckMin
+                                                                       : (IntRegion<T, U>::IntZone_Int64_UintLT64)
+                                                                             ? SubtractionState_Int64Uint
+                                                                             : (IntRegion<T, U>::IntZone_Int_Uint64)
+                                                                                   ? SubtractionState_IntUint64
+                                                                                   : (IntRegion<T, U>::
+                                                                                          IntZone_Int64_Uint64)
+                                                                                         ? SubtractionState_Int64Uint64
+                                                                                         : SubtractionState_Error)
+    };
+};
+
+// this is for the case of U - SafeInt< T, E >
+template<typename T, typename U>
+class SubtractionMethod2
+{
+public:
+    enum
+    {
+        // unsigned-unsigned
+        method =
+            ((IntRegion<T, U>::IntZone_UintLT32_UintLT32 || (IntRegion<T, U>::IntZone_Uint32_UintLT64) ||
+              (IntRegion<T, U>::IntZone_UintLT32_Uint32) || (IntRegion<T, U>::IntZone_Uint64_Uint) ||
+              (IntRegion<T, U>::IntZone_UintLT64_Uint64))
+                 ? SubtractionState_BothUnsigned2
+                 :
+                 // unsigned-signed
+                 (IntRegion<T, U>::IntZone_UintLT32_IntLT32)
+                     ? SubtractionState_CastIntCheckSafeIntMinMax2
+                     : (IntRegion<T, U>::IntZone_Uint32_IntLT64 || IntRegion<T, U>::IntZone_UintLT32_Int32)
+                           ? SubtractionState_CastInt64CheckSafeIntMinMax2
+                           : (IntRegion<T, U>::IntZone_Uint64_Int || IntRegion<T, U>::IntZone_Uint64_Int64)
+                                 ? SubtractionState_Uint64Int2
+                                 : (IntRegion<T, U>::IntZone_UintLT64_Int64) ? SubtractionState_UintInt642 :
+                                                                             // signed-signed
+                                       (IntRegion<T, U>::IntZone_IntLT32_IntLT32)
+                                           ? SubtractionState_CastIntCheckSafeIntMinMax2
+                                           : (IntRegion<T, U>::IntZone_Int32_IntLT64 ||
+                                              IntRegion<T, U>::IntZone_IntLT32_Int32)
+                                                 ? SubtractionState_CastInt64CheckSafeIntMinMax2
+                                                 : (IntRegion<T, U>::IntZone_Int64_Int ||
+                                                    IntRegion<T, U>::IntZone_Int64_Int64)
+                                                       ? SubtractionState_Int64Int2
+                                                       : (IntRegion<T, U>::IntZone_IntLT64_Int64)
+                                                             ? SubtractionState_IntInt642
+                                                             :
+                                                             // signed-unsigned
+                                                             (IntRegion<T, U>::IntZone_IntLT32_UintLT32)
+                                                                 ? SubtractionState_CastIntCheckSafeIntMinMax2
+                                                                 : (IntRegion<T, U>::IntZone_Int32_UintLT32 ||
+                                                                    IntRegion<T, U>::IntZone_IntLT64_Uint32)
+                                                                       ? SubtractionState_CastInt64CheckSafeIntMinMax2
+                                                                       : (IntRegion<T, U>::IntZone_Int64_UintLT64)
+                                                                             ? SubtractionState_Int64Uint2
+                                                                             : (IntRegion<T, U>::IntZone_Int_Uint64)
+                                                                                   ? SubtractionState_IntUint642
+                                                                                   : (IntRegion<T, U>::
+                                                                                          IntZone_Int64_Uint64)
+                                                                                         ? SubtractionState_Int64Uint642
+                                                                                         : SubtractionState_Error)
+    };
+};
+
+template<typename T, typename U, int method>
+class SubtractionHelper;
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_BothUnsigned>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // both are unsigned - easy case
+        if (rhs <= lhs)
+        {
+            result = (T)(lhs - rhs);
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // both are unsigned - easy case
+        if (rhs <= lhs)
+        {
+            result = (T)(lhs - rhs);
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_BothUnsigned2>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, U& result) SAFEINT_NOTHROW
+    {
+        // both are unsigned - easy case
+        // Except we do have to check for overflow - lhs could be larger than result can hold
+        if (rhs <= lhs)
+        {
+            T tmp = (T)(lhs - rhs);
+            return SafeCastHelper<U, T, GetCastMethod<U, T>::method>::Cast(tmp, result);
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, U& result) SAFEINT_CPP_THROW
+    {
+        // both are unsigned - easy case
+        if (rhs <= lhs)
+        {
+            T tmp = (T)(lhs - rhs);
+            SafeCastHelper<U, T, GetCastMethod<U, T>::method>::template CastThrow<E>(tmp, result);
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_CastIntCheckSafeIntMinMax>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // both values are 16-bit or less
+        // rhs is signed, so could end up increasing or decreasing
+        __int32 tmp = lhs - rhs;
+
+        if (SafeCastHelper<T, __int32, GetCastMethod<T, __int32>::method>::Cast(tmp, result))
+        {
+            result = (T)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // both values are 16-bit or less
+        // rhs is signed, so could end up increasing or decreasing
+        __int32 tmp = lhs - rhs;
+
+        SafeCastHelper<T, __int32, GetCastMethod<T, __int32>::method>::template CastThrow<E>(tmp, result);
+    }
+};
+
+template<typename U, typename T>
+class SubtractionHelper<U, T, SubtractionState_CastIntCheckSafeIntMinMax2>
+{
+public:
+    static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // both values are 16-bit or less
+        // rhs is signed, so could end up increasing or decreasing
+        __int32 tmp = lhs - rhs;
+
+        return SafeCastHelper<T, __int32, GetCastMethod<T, __int32>::method>::Cast(tmp, result);
+    }
+
+    template<typename E>
+    static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // both values are 16-bit or less
+        // rhs is signed, so could end up increasing or decreasing
+        __int32 tmp = lhs - rhs;
+
+        SafeCastHelper<T, __int32, GetCastMethod<T, __int32>::method>::template CastThrow<E>(tmp, result);
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_CastIntCheckMin>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // both values are 16-bit or less
+        // rhs is unsigned - check only minimum
+        __int32 tmp = lhs - rhs;
+
+        if (tmp >= (__int32)IntTraits<T>::minInt)
+        {
+            result = (T)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // both values are 16-bit or less
+        // rhs is unsigned - check only minimum
+        __int32 tmp = lhs - rhs;
+
+        if (tmp >= (__int32)IntTraits<T>::minInt)
+        {
+            result = (T)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_CastInt64CheckSafeIntMinMax>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // both values are 32-bit or less
+        // rhs is signed, so could end up increasing or decreasing
+        __int64 tmp = (__int64)lhs - (__int64)rhs;
+
+        return SafeCastHelper<T, __int64, GetCastMethod<T, __int64>::method>::Cast(tmp, result);
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // both values are 32-bit or less
+        // rhs is signed, so could end up increasing or decreasing
+        __int64 tmp = (__int64)lhs - (__int64)rhs;
+
+        SafeCastHelper<T, __int64, GetCastMethod<T, __int64>::method>::template CastThrow<E>(tmp, result);
+    }
+};
+
+template<typename U, typename T>
+class SubtractionHelper<U, T, SubtractionState_CastInt64CheckSafeIntMinMax2>
+{
+public:
+    static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // both values are 32-bit or less
+        // rhs is signed, so could end up increasing or decreasing
+        __int64 tmp = (__int64)lhs - (__int64)rhs;
+
+        return SafeCastHelper<T, __int64, GetCastMethod<T, __int64>::method>::Cast(tmp, result);
+    }
+
+    template<typename E>
+    static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // both values are 32-bit or less
+        // rhs is signed, so could end up increasing or decreasing
+        __int64 tmp = (__int64)lhs - (__int64)rhs;
+
+        SafeCastHelper<T, __int64, GetCastMethod<T, __int64>::method>::template CastThrow<E>(tmp, result);
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_CastInt64CheckMin>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // both values are 32-bit or less
+        // rhs is unsigned - check only minimum
+        __int64 tmp = (__int64)lhs - (__int64)rhs;
+
+        if (tmp >= (__int64)IntTraits<T>::minInt)
+        {
+            result = (T)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // both values are 32-bit or less
+        // rhs is unsigned - check only minimum
+        __int64 tmp = (__int64)lhs - (__int64)rhs;
+
+        if (tmp >= (__int64)IntTraits<T>::minInt)
+        {
+            result = (T)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_Uint64Int>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is an unsigned __int64, rhs signed
+        // must first see if rhs is positive or negative
+        if (rhs >= 0)
+        {
+            if ((unsigned __int64)rhs <= lhs)
+            {
+                result = (T)(lhs - (unsigned __int64)rhs);
+                return true;
+            }
+        }
+        else
+        {
+            T tmp = lhs;
+            // we're now effectively adding
+            result = lhs + AbsValueHelper<U, GetAbsMethod<U>::method>::Abs(rhs);
+
+            if (result >= tmp) return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is an unsigned __int64, rhs signed
+        // must first see if rhs is positive or negative
+        if (rhs >= 0)
+        {
+            if ((unsigned __int64)rhs <= lhs)
+            {
+                result = (T)(lhs - (unsigned __int64)rhs);
+                return;
+            }
+        }
+        else
+        {
+            T tmp = lhs;
+            // we're now effectively adding
+            result = lhs + AbsValueHelper<U, GetAbsMethod<U>::method>::Abs(rhs);
+
+            if (result >= tmp) return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename U, typename T>
+class SubtractionHelper<U, T, SubtractionState_Uint64Int2>
+{
+public:
+    static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // U is unsigned __int64, T is signed
+        if (rhs < 0)
+        {
+            // treat this as addition
+            unsigned __int64 tmp;
+
+            tmp = lhs + (unsigned __int64)AbsValueHelper<T, GetAbsMethod<T>::method>::Abs(rhs);
+
+            // must check for addition overflow and max
+            if (tmp >= lhs && tmp <= IntTraits<T>::maxInt)
+            {
+                result = (T)tmp;
+                return true;
+            }
+        }
+        else if ((unsigned __int64)rhs > lhs) // now both are positive, so comparison always works
+        {
+            // result is negative
+            // implies that lhs must fit into T, and result cannot overflow
+            // Also allows us to drop to 32-bit math, which is faster on a 32-bit system
+            result = (T)lhs - (T)rhs;
+            return true;
+        }
+        else
+        {
+            // result is positive
+            unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs;
+
+            if (tmp <= IntTraits<T>::maxInt)
+            {
+                result = (T)tmp;
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // U is unsigned __int64, T is signed
+        if (rhs < 0)
+        {
+            // treat this as addition
+            unsigned __int64 tmp;
+
+            tmp = lhs + (unsigned __int64)AbsValueHelper<T, GetAbsMethod<T>::method>::Abs(rhs);
+
+            // must check for addition overflow and max
+            if (tmp >= lhs && tmp <= IntTraits<T>::maxInt)
+            {
+                result = (T)tmp;
+                return;
+            }
+        }
+        else if ((unsigned __int64)rhs > lhs) // now both are positive, so comparison always works
+        {
+            // result is negative
+            // implies that lhs must fit into T, and result cannot overflow
+            // Also allows us to drop to 32-bit math, which is faster on a 32-bit system
+            result = (T)lhs - (T)rhs;
+            return;
+        }
+        else
+        {
+            // result is positive
+            unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs;
+
+            if (tmp <= IntTraits<T>::maxInt)
+            {
+                result = (T)tmp;
+                return;
+            }
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_UintInt64>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is an unsigned int32 or smaller, rhs signed __int64
+        // must first see if rhs is positive or negative
+        if (rhs >= 0)
+        {
+            if ((unsigned __int64)rhs <= lhs)
+            {
+                result = (T)(lhs - (T)rhs);
+                return true;
+            }
+        }
+        else
+        {
+            // we're now effectively adding
+            // since lhs is 32-bit, and rhs cannot exceed 2^63
+            // this addition cannot overflow
+            unsigned __int64 tmp = lhs + ~(unsigned __int64)(rhs) + 1; // negation safe
+
+            // but we could exceed MaxInt
+            if (tmp <= IntTraits<T>::maxInt)
+            {
+                result = (T)tmp;
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is an unsigned int32 or smaller, rhs signed __int64
+        // must first see if rhs is positive or negative
+        if (rhs >= 0)
+        {
+            if ((unsigned __int64)rhs <= lhs)
+            {
+                result = (T)(lhs - (T)rhs);
+                return;
+            }
+        }
+        else
+        {
+            // we're now effectively adding
+            // since lhs is 32-bit, and rhs cannot exceed 2^63
+            // this addition cannot overflow
+            unsigned __int64 tmp = lhs + ~(unsigned __int64)(rhs) + 1; // negation safe
+
+            // but we could exceed MaxInt
+            if (tmp <= IntTraits<T>::maxInt)
+            {
+                result = (T)tmp;
+                return;
+            }
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename U, typename T>
+class SubtractionHelper<U, T, SubtractionState_UintInt642>
+{
+public:
+    static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // U unsigned 32-bit or less, T __int64
+        if (rhs >= 0)
+        {
+            // overflow not possible
+            result = (T)((__int64)lhs - rhs);
+            return true;
+        }
+        else
+        {
+            // we effectively have an addition
+            // which cannot overflow internally
+            unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)(-rhs);
+
+            if (tmp <= (unsigned __int64)IntTraits<T>::maxInt)
+            {
+                result = (T)tmp;
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // U unsigned 32-bit or less, T __int64
+        if (rhs >= 0)
+        {
+            // overflow not possible
+            result = (T)((__int64)lhs - rhs);
+            return;
+        }
+        else
+        {
+            // we effectively have an addition
+            // which cannot overflow internally
+            unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)(-rhs);
+
+            if (tmp <= (unsigned __int64)IntTraits<T>::maxInt)
+            {
+                result = (T)tmp;
+                return;
+            }
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_Int64Int>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is an __int64, rhs signed (up to 64-bit)
+        // we have essentially 4 cases:
+        //
+        // 1) lhs positive, rhs positive - overflow not possible
+        // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error
+        // 3) lhs negative, rhs positive - check result <= lhs
+        // 4) lhs negative, rhs negative - overflow not possible
+
+        __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs);
+
+        // Note - ideally, we can order these so that true conditionals
+        // lead to success, which enables better pipelining
+        // It isn't practical here
+        if ((lhs >= 0 && rhs < 0 && tmp < lhs) || // condition 2
+            (rhs >= 0 && tmp > lhs))              // condition 3
+        {
+            return false;
+        }
+
+        result = (T)tmp;
+        return true;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is an __int64, rhs signed (up to 64-bit)
+        // we have essentially 4 cases:
+        //
+        // 1) lhs positive, rhs positive - overflow not possible
+        // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error
+        // 3) lhs negative, rhs positive - check result <= lhs
+        // 4) lhs negative, rhs negative - overflow not possible
+
+        __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs);
+
+        // Note - ideally, we can order these so that true conditionals
+        // lead to success, which enables better pipelining
+        // It isn't practical here
+        if ((lhs >= 0 && rhs < 0 && tmp < lhs) || // condition 2
+            (rhs >= 0 && tmp > lhs))              // condition 3
+        {
+            E::SafeIntOnOverflow();
+        }
+
+        result = (T)tmp;
+    }
+};
+
+template<typename U, typename T>
+class SubtractionHelper<U, T, SubtractionState_Int64Int2>
+{
+public:
+    static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs __int64, rhs any signed int (including __int64)
+        __int64 tmp = lhs - rhs;
+
+        // we have essentially 4 cases:
+        //
+        // 1) lhs positive, rhs positive - overflow not possible in tmp
+        // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error
+        // 3) lhs negative, rhs positive - check result <= lhs
+        // 4) lhs negative, rhs negative - overflow not possible in tmp
+
+        if (lhs >= 0)
+        {
+            // if both positive, overflow to negative not possible
+            // which is why we'll explicitly check maxInt, and not call SafeCast
+            if ((IntTraits<T>::isLT64Bit && tmp > IntTraits<T>::maxInt) || (rhs < 0 && tmp < lhs))
+            {
+                return false;
+            }
+        }
+        else
+        {
+            // lhs negative
+            if ((IntTraits<T>::isLT64Bit && tmp < IntTraits<T>::minInt) || (rhs >= 0 && tmp > lhs))
+            {
+                return false;
+            }
+        }
+
+        result = (T)tmp;
+        return true;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs __int64, rhs any signed int (including __int64)
+        __int64 tmp = lhs - rhs;
+
+        // we have essentially 4 cases:
+        //
+        // 1) lhs positive, rhs positive - overflow not possible in tmp
+        // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error
+        // 3) lhs negative, rhs positive - check result <= lhs
+        // 4) lhs negative, rhs negative - overflow not possible in tmp
+
+        if (lhs >= 0)
+        {
+            // if both positive, overflow to negative not possible
+            // which is why we'll explicitly check maxInt, and not call SafeCast
+            if ((CompileConst<IntTraits<T>::isLT64Bit>::Value() && tmp > IntTraits<T>::maxInt) ||
+                (rhs < 0 && tmp < lhs))
+            {
+                E::SafeIntOnOverflow();
+            }
+        }
+        else
+        {
+            // lhs negative
+            if ((CompileConst<IntTraits<T>::isLT64Bit>::Value() && tmp < IntTraits<T>::minInt) ||
+                (rhs >= 0 && tmp > lhs))
+            {
+                E::SafeIntOnOverflow();
+            }
+        }
+
+        result = (T)tmp;
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_IntInt64>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is a 32-bit int or less, rhs __int64
+        // we have essentially 4 cases:
+        //
+        // lhs positive, rhs positive - rhs could be larger than lhs can represent
+        // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int
+        // lhs negative, rhs positive - check tmp <= lhs and tmp < min int
+        // lhs negative, rhs negative - addition cannot internally overflow, check against max
+
+        __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs);
+
+        if (lhs >= 0)
+        {
+            // first case
+            if (rhs >= 0)
+            {
+                if (tmp >= IntTraits<T>::minInt)
+                {
+                    result = (T)tmp;
+                    return true;
+                }
+            }
+            else
+            {
+                // second case
+                if (tmp >= lhs && tmp <= IntTraits<T>::maxInt)
+                {
+                    result = (T)tmp;
+                    return true;
+                }
+            }
+        }
+        else
+        {
+            // lhs < 0
+            // third case
+            if (rhs >= 0)
+            {
+                if (tmp <= lhs && tmp >= IntTraits<T>::minInt)
+                {
+                    result = (T)tmp;
+                    return true;
+                }
+            }
+            else
+            {
+                // fourth case
+                if (tmp <= IntTraits<T>::maxInt)
+                {
+                    result = (T)tmp;
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is a 32-bit int or less, rhs __int64
+        // we have essentially 4 cases:
+        //
+        // lhs positive, rhs positive - rhs could be larger than lhs can represent
+        // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int
+        // lhs negative, rhs positive - check tmp <= lhs and tmp < min int
+        // lhs negative, rhs negative - addition cannot internally overflow, check against max
+
+        __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs);
+
+        if (lhs >= 0)
+        {
+            // first case
+            if (rhs >= 0)
+            {
+                if (tmp >= IntTraits<T>::minInt)
+                {
+                    result = (T)tmp;
+                    return;
+                }
+            }
+            else
+            {
+                // second case
+                if (tmp >= lhs && tmp <= IntTraits<T>::maxInt)
+                {
+                    result = (T)tmp;
+                    return;
+                }
+            }
+        }
+        else
+        {
+            // lhs < 0
+            // third case
+            if (rhs >= 0)
+            {
+                if (tmp <= lhs && tmp >= IntTraits<T>::minInt)
+                {
+                    result = (T)tmp;
+                    return;
+                }
+            }
+            else
+            {
+                // fourth case
+                if (tmp <= IntTraits<T>::maxInt)
+                {
+                    result = (T)tmp;
+                    return;
+                }
+            }
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename U, typename T>
+class SubtractionHelper<U, T, SubtractionState_IntInt642>
+{
+public:
+    static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is any signed int32 or smaller, rhs is int64
+        __int64 tmp = (__int64)lhs - rhs;
+
+        if ((lhs >= 0 && rhs < 0 && tmp < lhs) || (rhs > 0 && tmp > lhs))
+        {
+            return false;
+            // else OK
+        }
+
+        result = (T)tmp;
+        return true;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is any signed int32 or smaller, rhs is int64
+        __int64 tmp = (__int64)lhs - rhs;
+
+        if ((lhs >= 0 && rhs < 0 && tmp < lhs) || (rhs > 0 && tmp > lhs))
+        {
+            E::SafeIntOnOverflow();
+            // else OK
+        }
+
+        result = (T)tmp;
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_Int64Uint>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is a 64-bit int, rhs unsigned int32 or smaller
+        // perform test as unsigned to prevent unwanted optimizations
+        unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs;
+
+        if ((__int64)tmp <= lhs)
+        {
+            result = (T)(__int64)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is a 64-bit int, rhs unsigned int32 or smaller
+        // perform test as unsigned to prevent unwanted optimizations
+        unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs;
+
+        if ((__int64)tmp <= lhs)
+        {
+            result = (T)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename U, typename T>
+class SubtractionHelper<U, T, SubtractionState_Int64Uint2>
+{
+public:
+    // lhs is __int64, rhs is unsigned 32-bit or smaller
+    static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // Do this as unsigned to prevent unwanted optimizations
+        unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs;
+
+        if ((__int64)tmp <= IntTraits<T>::maxInt && (__int64)tmp >= IntTraits<T>::minInt)
+        {
+            result = (T)(__int64)tmp;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // Do this as unsigned to prevent unwanted optimizations
+        unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs;
+
+        if ((__int64)tmp <= IntTraits<T>::maxInt && (__int64)tmp >= IntTraits<T>::minInt)
+        {
+            result = (T)(__int64)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_IntUint64>
+{
+public:
+    static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // lhs is any signed int, rhs unsigned int64
+        // check against available range
+
+        // We need the absolute value of IntTraits< T >::minInt
+        // This will give it to us without extraneous compiler warnings
+        const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits<T>::maxInt + 1;
+
+        if (lhs < 0)
+        {
+            if (rhs <= AbsMinIntT - AbsValueHelper<T, GetAbsMethod<T>::method>::Abs(lhs))
+            {
+                result = (T)(lhs - rhs);
+                return true;
+            }
+        }
+        else
+        {
+            if (rhs <= AbsMinIntT + (unsigned __int64)lhs)
+            {
+                result = (T)(lhs - rhs);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // lhs is any signed int, rhs unsigned int64
+        // check against available range
+
+        // We need the absolute value of IntTraits< T >::minInt
+        // This will give it to us without extraneous compiler warnings
+        const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits<T>::maxInt + 1;
+
+        if (lhs < 0)
+        {
+            if (rhs <= AbsMinIntT - AbsValueHelper<T, GetAbsMethod<T>::method>::Abs(lhs))
+            {
+                result = (T)(lhs - rhs);
+                return;
+            }
+        }
+        else
+        {
+            if (rhs <= AbsMinIntT + (unsigned __int64)lhs)
+            {
+                result = (T)(lhs - rhs);
+                return;
+            }
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename U, typename T>
+class SubtractionHelper<U, T, SubtractionState_IntUint642>
+{
+public:
+    static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW
+    {
+        // We run into upcasting problems on comparison - needs 2 checks
+        if (lhs >= 0 && (T)lhs >= rhs)
+        {
+            result = (T)((U)lhs - (U)rhs);
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        // We run into upcasting problems on comparison - needs 2 checks
+        if (lhs >= 0 && (T)lhs >= rhs)
+        {
+            result = (T)((U)lhs - (U)rhs);
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U>
+class SubtractionHelper<T, U, SubtractionState_Int64Uint64>
+{
+public:
+    static bool Subtract(const __int64& lhs, const unsigned __int64& rhs, __int64& result) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64 && IntTraits<U>::isUint64);
+        // if we subtract, and it gets larger, there's a problem
+        // Perform test as unsigned to prevent unwanted optimizations
+        unsigned __int64 tmp = (unsigned __int64)lhs - rhs;
+
+        if ((__int64)tmp <= lhs)
+        {
+            result = (__int64)tmp;
+            return true;
+        }
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const __int64& lhs, const unsigned __int64& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isInt64 && IntTraits<U>::isUint64);
+        // if we subtract, and it gets larger, there's a problem
+        // Perform test as unsigned to prevent unwanted optimizations
+        unsigned __int64 tmp = (unsigned __int64)lhs - rhs;
+
+        if ((__int64)tmp <= lhs)
+        {
+            result = (__int64)tmp;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename U, typename T>
+class SubtractionHelper<U, T, SubtractionState_Int64Uint642>
+{
+public:
+    // If lhs is negative, immediate problem - return must be positive, and subtracting only makes it
+    // get smaller. If rhs > lhs, then it would also go negative, which is the other case
+    static bool Subtract(const __int64& lhs, const unsigned __int64& rhs, T& result) SAFEINT_NOTHROW
+    {
+        C_ASSERT(IntTraits<T>::isUint64 && IntTraits<U>::isInt64);
+        if (lhs >= 0 && (unsigned __int64)lhs >= rhs)
+        {
+            result = (unsigned __int64)lhs - rhs;
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename E>
+    static void SubtractThrow(const __int64& lhs, const unsigned __int64& rhs, T& result) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(IntTraits<T>::isUint64 && IntTraits<U>::isInt64);
+        if (lhs >= 0 && (unsigned __int64)lhs >= rhs)
+        {
+            result = (unsigned __int64)lhs - rhs;
+            return;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+enum BinaryState
+{
+    BinaryState_OK,
+    BinaryState_Int8,
+    BinaryState_Int16,
+    BinaryState_Int32
+};
+
+template<typename T, typename U>
+class BinaryMethod
+{
+public:
+    enum
+    {
+        // If both operands are unsigned OR
+        //    return type is smaller than rhs OR
+        //    return type is larger and rhs is unsigned
+        // Then binary operations won't produce unexpected results
+        method = (sizeof(T) <= sizeof(U) || SafeIntCompare<T, U>::isBothUnsigned || !IntTraits<U>::isSigned)
+                     ? BinaryState_OK
+                     : IntTraits<U>::isInt8 ? BinaryState_Int8
+                                            : IntTraits<U>::isInt16 ? BinaryState_Int16 : BinaryState_Int32
+    };
+};
+
+#ifdef SAFEINT_DISABLE_BINARY_ASSERT
+#define BinaryAssert(x)
+#else
+#define BinaryAssert(x) SAFEINT_ASSERT(x)
+#endif
+
+template<typename T, typename U, int method>
+class BinaryAndHelper;
+
+template<typename T, typename U>
+class BinaryAndHelper<T, U, BinaryState_OK>
+{
+public:
+    static T And(T lhs, U rhs) SAFEINT_NOTHROW { return (T)(lhs & rhs); }
+};
+
+template<typename T, typename U>
+class BinaryAndHelper<T, U, BinaryState_Int8>
+{
+public:
+    static T And(T lhs, U rhs) SAFEINT_NOTHROW
+    {
+        // cast forces sign extension to be zeros
+        BinaryAssert((lhs & rhs) == (lhs & (unsigned __int8)rhs));
+        return (T)(lhs & (unsigned __int8)rhs);
+    }
+};
+
+template<typename T, typename U>
+class BinaryAndHelper<T, U, BinaryState_Int16>
+{
+public:
+    static T And(T lhs, U rhs) SAFEINT_NOTHROW
+    {
+        // cast forces sign extension to be zeros
+        BinaryAssert((lhs & rhs) == (lhs & (unsigned __int16)rhs));
+        return (T)(lhs & (unsigned __int16)rhs);
+    }
+};
+
+template<typename T, typename U>
+class BinaryAndHelper<T, U, BinaryState_Int32>
+{
+public:
+    static T And(T lhs, U rhs) SAFEINT_NOTHROW
+    {
+        // cast forces sign extension to be zeros
+        BinaryAssert((lhs & rhs) == (lhs & (unsigned __int32)rhs));
+        return (T)(lhs & (unsigned __int32)rhs);
+    }
+};
+
+template<typename T, typename U, int method>
+class BinaryOrHelper;
+
+template<typename T, typename U>
+class BinaryOrHelper<T, U, BinaryState_OK>
+{
+public:
+    static T Or(T lhs, U rhs) SAFEINT_NOTHROW { return (T)(lhs | rhs); }
+};
+
+template<typename T, typename U>
+class BinaryOrHelper<T, U, BinaryState_Int8>
+{
+public:
+    static T Or(T lhs, U rhs) SAFEINT_NOTHROW
+    {
+        // cast forces sign extension to be zeros
+        BinaryAssert((lhs | rhs) == (lhs | (unsigned __int8)rhs));
+        return (T)(lhs | (unsigned __int8)rhs);
+    }
+};
+
+template<typename T, typename U>
+class BinaryOrHelper<T, U, BinaryState_Int16>
+{
+public:
+    static T Or(T lhs, U rhs) SAFEINT_NOTHROW
+    {
+        // cast forces sign extension to be zeros
+        BinaryAssert((lhs | rhs) == (lhs | (unsigned __int16)rhs));
+        return (T)(lhs | (unsigned __int16)rhs);
+    }
+};
+
+template<typename T, typename U>
+class BinaryOrHelper<T, U, BinaryState_Int32>
+{
+public:
+    static T Or(T lhs, U rhs) SAFEINT_NOTHROW
+    {
+        // cast forces sign extension to be zeros
+        BinaryAssert((lhs | rhs) == (lhs | (unsigned __int32)rhs));
+        return (T)(lhs | (unsigned __int32)rhs);
+    }
+};
+
+template<typename T, typename U, int method>
+class BinaryXorHelper;
+
+template<typename T, typename U>
+class BinaryXorHelper<T, U, BinaryState_OK>
+{
+public:
+    static T Xor(T lhs, U rhs) SAFEINT_NOTHROW { return (T)(lhs ^ rhs); }
+};
+
+template<typename T, typename U>
+class BinaryXorHelper<T, U, BinaryState_Int8>
+{
+public:
+    static T Xor(T lhs, U rhs) SAFEINT_NOTHROW
+    {
+        // cast forces sign extension to be zeros
+        BinaryAssert((lhs ^ rhs) == (lhs ^ (unsigned __int8)rhs));
+        return (T)(lhs ^ (unsigned __int8)rhs);
+    }
+};
+
+template<typename T, typename U>
+class BinaryXorHelper<T, U, BinaryState_Int16>
+{
+public:
+    static T Xor(T lhs, U rhs) SAFEINT_NOTHROW
+    {
+        // cast forces sign extension to be zeros
+        BinaryAssert((lhs ^ rhs) == (lhs ^ (unsigned __int16)rhs));
+        return (T)(lhs ^ (unsigned __int16)rhs);
+    }
+};
+
+template<typename T, typename U>
+class BinaryXorHelper<T, U, BinaryState_Int32>
+{
+public:
+    static T Xor(T lhs, U rhs) SAFEINT_NOTHROW
+    {
+        // cast forces sign extension to be zeros
+        BinaryAssert((lhs ^ rhs) == (lhs ^ (unsigned __int32)rhs));
+        return (T)(lhs ^ (unsigned __int32)rhs);
+    }
+};
+
+/*****************  External functions ****************************************/
+
+// External functions that can be used where you only need to check one operation
+// non-class helper function so that you can check for a cast's validity
+// and handle errors how you like
+template<typename T, typename U>
+inline bool SafeCast(const T From, U& To) SAFEINT_NOTHROW
+{
+    return SafeCastHelper<U, T, GetCastMethod<U, T>::method>::Cast(From, To);
+}
+
+template<typename T, typename U>
+inline bool SafeEquals(const T t, const U u) SAFEINT_NOTHROW
+{
+    return EqualityTest<T, U, ValidComparison<T, U>::method>::IsEquals(t, u);
+}
+
+template<typename T, typename U>
+inline bool SafeNotEquals(const T t, const U u) SAFEINT_NOTHROW
+{
+    return !EqualityTest<T, U, ValidComparison<T, U>::method>::IsEquals(t, u);
+}
+
+template<typename T, typename U>
+inline bool SafeGreaterThan(const T t, const U u) SAFEINT_NOTHROW
+{
+    return GreaterThanTest<T, U, ValidComparison<T, U>::method>::GreaterThan(t, u);
+}
+
+template<typename T, typename U>
+inline bool SafeGreaterThanEquals(const T t, const U u) SAFEINT_NOTHROW
+{
+    return !GreaterThanTest<U, T, ValidComparison<U, T>::method>::GreaterThan(u, t);
+}
+
+template<typename T, typename U>
+inline bool SafeLessThan(const T t, const U u) SAFEINT_NOTHROW
+{
+    return GreaterThanTest<U, T, ValidComparison<U, T>::method>::GreaterThan(u, t);
+}
+
+template<typename T, typename U>
+inline bool SafeLessThanEquals(const T t, const U u) SAFEINT_NOTHROW
+{
+    return !GreaterThanTest<T, U, ValidComparison<T, U>::method>::GreaterThan(t, u);
+}
+
+template<typename T, typename U>
+inline bool SafeModulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW
+{
+    return (ModulusHelper<T, U, ValidComparison<T, U>::method>::Modulus(t, u, result) == SafeIntNoError);
+}
+
+template<typename T, typename U>
+inline bool SafeMultiply(T t, U u, T& result) SAFEINT_NOTHROW
+{
+    return MultiplicationHelper<T, U, MultiplicationMethod<T, U>::method>::Multiply(t, u, result);
+}
+
+template<typename T, typename U>
+inline bool SafeDivide(T t, U u, T& result) SAFEINT_NOTHROW
+{
+    return (DivisionHelper<T, U, DivisionMethod<T, U>::method>::Divide(t, u, result) == SafeIntNoError);
+}
+
+template<typename T, typename U>
+inline bool SafeAdd(T t, U u, T& result) SAFEINT_NOTHROW
+{
+    return AdditionHelper<T, U, AdditionMethod<T, U>::method>::Addition(t, u, result);
+}
+
+template<typename T, typename U>
+inline bool SafeSubtract(T t, U u, T& result) SAFEINT_NOTHROW
+{
+    return SubtractionHelper<T, U, SubtractionMethod<T, U>::method>::Subtract(t, u, result);
+}
+
+/*****************  end external functions ************************************/
+
+// Main SafeInt class
+// Assumes exceptions can be thrown
+template<typename T, typename E = SafeIntDefaultExceptionHandler>
+class SafeInt
+{
+public:
+    SafeInt() SAFEINT_NOTHROW
+    {
+        C_ASSERT(NumericType<T>::isInt);
+        m_int = 0;
+    }
+
+    // Having a constructor for every type of int
+    // avoids having the compiler evade our checks when doing implicit casts -
+    // e.g., SafeInt<char> s = 0x7fffffff;
+    SafeInt(const T& i) SAFEINT_NOTHROW
+    {
+        C_ASSERT(NumericType<T>::isInt);
+        // always safe
+        m_int = i;
+    }
+
+    // provide explicit boolean converter
+    SafeInt(bool b) SAFEINT_NOTHROW
+    {
+        C_ASSERT(NumericType<T>::isInt);
+        m_int = (T)(b ? 1 : 0);
+    }
+
+    constexpr SafeInt(const SafeInt<T, E>& u) SAFEINT_CPP_THROW = default;
+
+    template<typename U>
+    SafeInt(const SafeInt<U, E>& u) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(NumericType<T>::isInt);
+        *this = SafeInt<T, E>((U)u);
+    }
+
+    template<typename U>
+    SafeInt(const U& i) SAFEINT_CPP_THROW
+    {
+        C_ASSERT(NumericType<T>::isInt);
+        // SafeCast will throw exceptions if i won't fit in type T
+        SafeCastHelper<T, U, GetCastMethod<T, U>::method>::template CastThrow<E>(i, m_int);
+    }
+
+    // The destructor is intentionally commented out - no destructor
+    // vs. a do-nothing destructor makes a huge difference in
+    // inlining characteristics. It wasn't doing anything anyway.
+    // ~SafeInt(){};
+
+    // now start overloading operators
+    // assignment operator
+    // constructors exist for all int types and will ensure safety
+
+    template<typename U>
+    SafeInt<T, E>& operator=(const U& rhs) SAFEINT_CPP_THROW
+    {
+        // use constructor to test size
+        // constructor is optimized to do minimal checking based
+        // on whether T can contain U
+        // note - do not change this
+        *this = SafeInt<T, E>(rhs);
+        return *this;
+    }
+
+    SafeInt<T, E>& operator=(const T& rhs) SAFEINT_NOTHROW
+    {
+        m_int = rhs;
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator=(const SafeInt<U, E>& rhs) SAFEINT_CPP_THROW
+    {
+        SafeCastHelper<T, U, GetCastMethod<T, U>::method>::template CastThrow<E>(rhs.Ref(), m_int);
+        return *this;
+    }
+
+    SafeInt<T, E>& operator=(const SafeInt<T, E>& rhs) SAFEINT_NOTHROW
+    {
+        m_int = rhs.m_int;
+        return *this;
+    }
+
+    // Casting operators
+
+    operator bool() const SAFEINT_NOTHROW { return !!m_int; }
+
+    operator char() const SAFEINT_CPP_THROW
+    {
+        char val;
+        SafeCastHelper<char, T, GetCastMethod<char, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+
+    operator signed char() const SAFEINT_CPP_THROW
+    {
+        signed char val;
+        SafeCastHelper<signed char, T, GetCastMethod<signed char, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+
+    operator unsigned char() const SAFEINT_CPP_THROW
+    {
+        unsigned char val;
+        SafeCastHelper<unsigned char, T, GetCastMethod<unsigned char, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+
+    operator __int16() const SAFEINT_CPP_THROW
+    {
+        __int16 val;
+        SafeCastHelper<__int16, T, GetCastMethod<__int16, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+
+    operator unsigned __int16() const SAFEINT_CPP_THROW
+    {
+        unsigned __int16 val;
+        SafeCastHelper<unsigned __int16, T, GetCastMethod<unsigned __int16, T>::method>::template CastThrow<E>(m_int,
+                                                                                                               val);
+        return val;
+    }
+
+    operator __int32() const SAFEINT_CPP_THROW
+    {
+        __int32 val;
+        SafeCastHelper<__int32, T, GetCastMethod<__int32, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+
+    operator unsigned __int32() const SAFEINT_CPP_THROW
+    {
+        unsigned __int32 val;
+        SafeCastHelper<unsigned __int32, T, GetCastMethod<unsigned __int32, T>::method>::template CastThrow<E>(m_int,
+                                                                                                               val);
+        return val;
+    }
+
+    // The compiler knows that int == __int32
+    // but not that long == __int32
+    operator long() const SAFEINT_CPP_THROW
+    {
+        long val;
+        SafeCastHelper<long, T, GetCastMethod<long, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+
+    operator unsigned long() const SAFEINT_CPP_THROW
+    {
+        unsigned long val;
+        SafeCastHelper<unsigned long, T, GetCastMethod<unsigned long, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+
+    operator __int64() const SAFEINT_CPP_THROW
+    {
+        __int64 val;
+        SafeCastHelper<__int64, T, GetCastMethod<__int64, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+
+    operator unsigned __int64() const SAFEINT_CPP_THROW
+    {
+        unsigned __int64 val;
+        SafeCastHelper<unsigned __int64, T, GetCastMethod<unsigned __int64, T>::method>::template CastThrow<E>(m_int,
+                                                                                                               val);
+        return val;
+    }
+
+#if defined SAFEINT_USE_WCHAR_T || defined _NATIVE_WCHAR_T_DEFINED
+    operator wchar_t() const SAFEINT_CPP_THROW
+    {
+        wchar_t val;
+        SafeCastHelper<wchar_t, T, GetCastMethod<wchar_t, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+#endif
+
+#ifdef SIZE_T_CAST_NEEDED
+    // We also need an explicit cast to size_t, or the compiler will complain
+    // Apparently, only SOME compilers complain, and cl 14.00.50727.42 isn't one of them
+    // Leave here in case we decide to backport this to an earlier compiler
+    operator size_t() const SAFEINT_CPP_THROW
+    {
+        size_t val;
+        SafeCastHelper<size_t, T, GetCastMethod<size_t, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+#endif
+
+    // Also provide a cast operator for floating point types
+    operator float() const SAFEINT_CPP_THROW
+    {
+        float val;
+        SafeCastHelper<float, T, GetCastMethod<float, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+
+    operator double() const SAFEINT_CPP_THROW
+    {
+        double val;
+        SafeCastHelper<double, T, GetCastMethod<double, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+    operator long double() const SAFEINT_CPP_THROW
+    {
+        long double val;
+        SafeCastHelper<long double, T, GetCastMethod<long double, T>::method>::template CastThrow<E>(m_int, val);
+        return val;
+    }
+
+    // If you need a pointer to the data
+    // this could be dangerous, but allows you to correctly pass
+    // instances of this class to APIs that take a pointer to an integer
+    // also see overloaded address-of operator below
+    T* Ptr() SAFEINT_NOTHROW { return &m_int; }
+    const T* Ptr() const SAFEINT_NOTHROW { return &m_int; }
+    const T& Ref() const SAFEINT_NOTHROW { return m_int; }
+
+    // Or if SafeInt< T, E >::Ptr() is inconvenient, use the overload
+    // operator &
+    // This allows you to do unsafe things!
+    // It is meant to allow you to more easily
+    // pass a SafeInt into things like ReadFile
+    T* operator&() SAFEINT_NOTHROW { return &m_int; }
+    const T* operator&() const SAFEINT_NOTHROW { return &m_int; }
+
+    // Unary operators
+    bool operator!() const SAFEINT_NOTHROW { return (!m_int) ? true : false; }
+
+    // operator + (unary)
+    // note - normally, the '+' and '-' operators will upcast to a signed int
+    // for T < 32 bits. This class changes behavior to preserve type
+    const SafeInt<T, E>& operator+() const SAFEINT_NOTHROW { return *this; }
+
+    // unary  -
+
+    SafeInt<T, E> operator-() const SAFEINT_CPP_THROW
+    {
+        // Note - unsigned still performs the bitwise manipulation
+        // will warn at level 2 or higher if the value is 32-bit or larger
+        return SafeInt<T, E>(NegationHelper<T, IntTraits<T>::isSigned>::template NegativeThrow<E>(m_int));
+    }
+
+    // prefix increment operator
+    SafeInt<T, E>& operator++() SAFEINT_CPP_THROW
+    {
+        if (m_int != IntTraits<T>::maxInt)
+        {
+            ++m_int;
+            return *this;
+        }
+        E::SafeIntOnOverflow();
+    }
+
+    // prefix decrement operator
+    SafeInt<T, E>& operator--() SAFEINT_CPP_THROW
+    {
+        if (m_int != IntTraits<T>::minInt)
+        {
+            --m_int;
+            return *this;
+        }
+        E::SafeIntOnOverflow();
+    }
+
+    // note that postfix operators have inherently worse perf
+    // characteristics
+
+    // postfix increment operator
+    SafeInt<T, E> operator++(int) SAFEINT_CPP_THROW // dummy arg to comply with spec
+    {
+        if (m_int != IntTraits<T>::maxInt)
+        {
+            SafeInt<T, E> tmp(m_int);
+
+            m_int++;
+            return tmp;
+        }
+        E::SafeIntOnOverflow();
+    }
+
+    // postfix decrement operator
+    SafeInt<T, E> operator--(int) SAFEINT_CPP_THROW // dummy arg to comply with spec
+    {
+        if (m_int != IntTraits<T>::minInt)
+        {
+            SafeInt<T, E> tmp(m_int);
+            m_int--;
+            return tmp;
+        }
+        E::SafeIntOnOverflow();
+    }
+
+    // One's complement
+    // Note - this operator will normally change size to an int
+    // cast in return improves perf and maintains type
+    SafeInt<T, E> operator~() const SAFEINT_NOTHROW { return SafeInt<T, E>((T)~m_int); }
+
+    // Binary operators
+    //
+    // arithmetic binary operators
+    // % modulus
+    // * multiplication
+    // / division
+    // + addition
+    // - subtraction
+    //
+    // For each of the arithmetic operators, you will need to
+    // use them as follows:
+    //
+    // SafeInt<char> c = 2;
+    // SafeInt<int>  i = 3;
+    //
+    // SafeInt<int> i2 = i op (char)c;
+    // OR
+    // SafeInt<char> i2 = (int)i op c;
+    //
+    // The base problem is that if the lhs and rhs inputs are different SafeInt types
+    // it is not possible in this implementation to determine what type of SafeInt
+    // should be returned. You have to let the class know which of the two inputs
+    // need to be the return type by forcing the other value to the base integer type.
+    //
+    // Note - as per feedback from Scott Meyers, I'm exploring how to get around this.
+    // 3.0 update - I'm still thinking about this. It can be done with template metaprogramming,
+    // but it is tricky, and there's a perf vs. correctness tradeoff where the right answer
+    // is situational.
+    //
+    // The case of:
+    //
+    // SafeInt< T, E > i, j, k;
+    // i = j op k;
+    //
+    // works just fine and no unboxing is needed because the return type is not ambiguous.
+
+    // Modulus
+    // Modulus has some convenient properties -
+    // first, the magnitude of the return can never be
+    // larger than the lhs operand, and it must be the same sign
+    // as well. It does, however, suffer from the same promotion
+    // problems as comparisons, division and other operations
+    template<typename U>
+    SafeInt<T, E> operator%(U rhs) const SAFEINT_CPP_THROW
+    {
+        T result;
+        ModulusHelper<T, U, ValidComparison<T, U>::method>::template ModulusThrow<E>(m_int, rhs, result);
+        return SafeInt<T, E>(result);
+    }
+
+    SafeInt<T, E> operator%(SafeInt<T, E> rhs) const SAFEINT_CPP_THROW
+    {
+        T result;
+        ModulusHelper<T, T, ValidComparison<T, T>::method>::template ModulusThrow<E>(m_int, rhs, result);
+        return SafeInt<T, E>(result);
+    }
+
+    // Modulus assignment
+    template<typename U>
+    SafeInt<T, E>& operator%=(U rhs) SAFEINT_CPP_THROW
+    {
+        ModulusHelper<T, U, ValidComparison<T, U>::method>::template ModulusThrow<E>(m_int, rhs, m_int);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator%=(SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+    {
+        ModulusHelper<T, U, ValidComparison<T, U>::method>::template ModulusThrow<E>(m_int, (U)rhs, m_int);
+        return *this;
+    }
+
+    // Multiplication
+    template<typename U>
+    SafeInt<T, E> operator*(U rhs) const SAFEINT_CPP_THROW
+    {
+        T ret(0);
+        MultiplicationHelper<T, U, MultiplicationMethod<T, U>::method>::template MultiplyThrow<E>(m_int, rhs, ret);
+        return SafeInt<T, E>(ret);
+    }
+
+    SafeInt<T, E> operator*(SafeInt<T, E> rhs) const SAFEINT_CPP_THROW
+    {
+        T ret(0);
+        MultiplicationHelper<T, T, MultiplicationMethod<T, T>::method>::template MultiplyThrow<E>(m_int, (T)rhs, ret);
+        return SafeInt<T, E>(ret);
+    }
+
+    // Multiplication assignment
+    SafeInt<T, E>& operator*=(SafeInt<T, E> rhs) SAFEINT_CPP_THROW
+    {
+        MultiplicationHelper<T, T, MultiplicationMethod<T, T>::method>::template MultiplyThrow<E>(m_int, (T)rhs, m_int);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator*=(U rhs) SAFEINT_CPP_THROW
+    {
+        MultiplicationHelper<T, U, MultiplicationMethod<T, U>::method>::template MultiplyThrow<E>(m_int, rhs, m_int);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator*=(SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+    {
+        MultiplicationHelper<T, U, MultiplicationMethod<T, U>::method>::template MultiplyThrow<E>(
+            m_int, rhs.Ref(), m_int);
+        return *this;
+    }
+
+    // Division
+    template<typename U>
+    SafeInt<T, E> operator/(U rhs) const SAFEINT_CPP_THROW
+    {
+        T ret(0);
+        DivisionHelper<T, U, DivisionMethod<T, U>::method>::template DivideThrow<E>(m_int, rhs, ret);
+        return SafeInt<T, E>(ret);
+    }
+
+    SafeInt<T, E> operator/(SafeInt<T, E> rhs) const SAFEINT_CPP_THROW
+    {
+        T ret(0);
+        DivisionHelper<T, T, DivisionMethod<T, T>::method>::template DivideThrow<E>(m_int, (T)rhs, ret);
+        return SafeInt<T, E>(ret);
+    }
+
+    // Division assignment
+    SafeInt<T, E>& operator/=(SafeInt<T, E> i) SAFEINT_CPP_THROW
+    {
+        DivisionHelper<T, T, DivisionMethod<T, T>::method>::template DivideThrow<E>(m_int, (T)i, m_int);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator/=(U i) SAFEINT_CPP_THROW
+    {
+        DivisionHelper<T, U, DivisionMethod<T, U>::method>::template DivideThrow<E>(m_int, i, m_int);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator/=(SafeInt<U, E> i)
+    {
+        DivisionHelper<T, U, DivisionMethod<T, U>::method>::template DivideThrow<E>(m_int, (U)i, m_int);
+        return *this;
+    }
+
+    // For addition and subtraction
+
+    // Addition
+    SafeInt<T, E> operator+(SafeInt<T, E> rhs) const SAFEINT_CPP_THROW
+    {
+        T ret(0);
+        AdditionHelper<T, T, AdditionMethod<T, T>::method>::template AdditionThrow<E>(m_int, (T)rhs, ret);
+        return SafeInt<T, E>(ret);
+    }
+
+    template<typename U>
+    SafeInt<T, E> operator+(U rhs) const SAFEINT_CPP_THROW
+    {
+        T ret(0);
+        AdditionHelper<T, U, AdditionMethod<T, U>::method>::template AdditionThrow<E>(m_int, rhs, ret);
+        return SafeInt<T, E>(ret);
+    }
+
+    // addition assignment
+    SafeInt<T, E>& operator+=(SafeInt<T, E> rhs) SAFEINT_CPP_THROW
+    {
+        AdditionHelper<T, T, AdditionMethod<T, T>::method>::template AdditionThrow<E>(m_int, (T)rhs, m_int);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator+=(U rhs) SAFEINT_CPP_THROW
+    {
+        AdditionHelper<T, U, AdditionMethod<T, U>::method>::template AdditionThrow<E>(m_int, rhs, m_int);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator+=(SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+    {
+        AdditionHelper<T, U, AdditionMethod<T, U>::method>::template AdditionThrow<E>(m_int, (U)rhs, m_int);
+        return *this;
+    }
+
+    // Subtraction
+    template<typename U>
+    SafeInt<T, E> operator-(U rhs) const SAFEINT_CPP_THROW
+    {
+        T ret(0);
+        SubtractionHelper<T, U, SubtractionMethod<T, U>::method>::template SubtractThrow<E>(m_int, rhs, ret);
+        return SafeInt<T, E>(ret);
+    }
+
+    SafeInt<T, E> operator-(SafeInt<T, E> rhs) const SAFEINT_CPP_THROW
+    {
+        T ret(0);
+        SubtractionHelper<T, T, SubtractionMethod<T, T>::method>::template SubtractThrow<E>(m_int, (T)rhs, ret);
+        return SafeInt<T, E>(ret);
+    }
+
+    // Subtraction assignment
+    SafeInt<T, E>& operator-=(SafeInt<T, E> rhs) SAFEINT_CPP_THROW
+    {
+        SubtractionHelper<T, T, SubtractionMethod<T, T>::method>::template SubtractThrow<E>(m_int, (T)rhs, m_int);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator-=(U rhs) SAFEINT_CPP_THROW
+    {
+        SubtractionHelper<T, U, SubtractionMethod<T, U>::method>::template SubtractThrow<E>(m_int, rhs, m_int);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator-=(SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+    {
+        SubtractionHelper<T, U, SubtractionMethod<T, U>::method>::template SubtractThrow<E>(m_int, (U)rhs, m_int);
+        return *this;
+    }
+
+    // Shift operators
+    // Note - shift operators ALWAYS return the same type as the lhs
+    // specific version for SafeInt< T, E > not needed -
+    // code path is exactly the same as for SafeInt< U, E > as rhs
+
+    // Left shift
+    // Also, shifting > bitcount is undefined - trap in debug
+#ifdef SAFEINT_DISABLE_SHIFT_ASSERT
+#define ShiftAssert(x)
+#else
+#define ShiftAssert(x) SAFEINT_ASSERT(x)
+#endif
+
+    template<typename U>
+    SafeInt<T, E> operator<<(U bits) const SAFEINT_NOTHROW
+    {
+        ShiftAssert(!IntTraits<U>::isSigned || bits >= 0);
+        ShiftAssert(bits < (int)IntTraits<T>::bitCount);
+
+        return SafeInt<T, E>((T)(m_int << bits));
+    }
+
+    template<typename U>
+    SafeInt<T, E> operator<<(SafeInt<U, E> bits) const SAFEINT_NOTHROW
+    {
+        ShiftAssert(!IntTraits<U>::isSigned || (U)bits >= 0);
+        ShiftAssert((U)bits < (int)IntTraits<T>::bitCount);
+
+        return SafeInt<T, E>((T)(m_int << (U)bits));
+    }
+
+    // Left shift assignment
+
+    template<typename U>
+    SafeInt<T, E>& operator<<=(U bits) SAFEINT_NOTHROW
+    {
+        ShiftAssert(!IntTraits<U>::isSigned || bits >= 0);
+        ShiftAssert(bits < (int)IntTraits<T>::bitCount);
+
+        m_int <<= bits;
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator<<=(SafeInt<U, E> bits) SAFEINT_NOTHROW
+    {
+        ShiftAssert(!IntTraits<U>::isSigned || (U)bits >= 0);
+        ShiftAssert((U)bits < (int)IntTraits<T>::bitCount);
+
+        m_int <<= (U)bits;
+        return *this;
+    }
+
+    // Right shift
+    template<typename U>
+    SafeInt<T, E> operator>>(U bits) const SAFEINT_NOTHROW
+    {
+        ShiftAssert(!IntTraits<U>::isSigned || bits >= 0);
+        ShiftAssert(bits < (int)IntTraits<T>::bitCount);
+
+        return SafeInt<T, E>((T)(m_int >> bits));
+    }
+
+    template<typename U>
+    SafeInt<T, E> operator>>(SafeInt<U, E> bits) const SAFEINT_NOTHROW
+    {
+        ShiftAssert(!IntTraits<U>::isSigned || (U)bits >= 0);
+        ShiftAssert(bits < (int)IntTraits<T>::bitCount);
+
+        return SafeInt<T, E>((T)(m_int >> (U)bits));
+    }
+
+    // Right shift assignment
+    template<typename U>
+    SafeInt<T, E>& operator>>=(U bits) SAFEINT_NOTHROW
+    {
+        ShiftAssert(!IntTraits<U>::isSigned || bits >= 0);
+        ShiftAssert(bits < (int)IntTraits<T>::bitCount);
+
+        m_int >>= bits;
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator>>=(SafeInt<U, E> bits) SAFEINT_NOTHROW
+    {
+        ShiftAssert(!IntTraits<U>::isSigned || (U)bits >= 0);
+        ShiftAssert((U)bits < (int)IntTraits<T>::bitCount);
+
+        m_int >>= (U)bits;
+        return *this;
+    }
+
+    // Bitwise operators
+    // This only makes sense if we're dealing with the same type and size
+    // demand a type T, or something that fits into a type T
+
+    // Bitwise &
+    SafeInt<T, E> operator&(SafeInt<T, E> rhs) const SAFEINT_NOTHROW { return SafeInt<T, E>(m_int & (T)rhs); }
+
+    template<typename U>
+    SafeInt<T, E> operator&(U rhs) const SAFEINT_NOTHROW
+    {
+        // we want to avoid setting bits by surprise
+        // consider the case of lhs = int, value = 0xffffffff
+        //                      rhs = char, value = 0xff
+        //
+        // programmer intent is to get only the lower 8 bits
+        // normal behavior is to upcast both sides to an int
+        // which then sign extends rhs, setting all the bits
+
+        // If you land in the assert, this is because the bitwise operator
+        // was causing unexpected behavior. Fix is to properly cast your inputs
+        // so that it works like you meant, not unexpectedly
+
+        return SafeInt<T, E>(BinaryAndHelper<T, U, BinaryMethod<T, U>::method>::And(m_int, rhs));
+    }
+
+    // Bitwise & assignment
+    SafeInt<T, E>& operator&=(SafeInt<T, E> rhs) SAFEINT_NOTHROW
+    {
+        m_int &= (T)rhs;
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator&=(U rhs) SAFEINT_NOTHROW
+    {
+        m_int = BinaryAndHelper<T, U, BinaryMethod<T, U>::method>::And(m_int, rhs);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator&=(SafeInt<U, E> rhs) SAFEINT_NOTHROW
+    {
+        m_int = BinaryAndHelper<T, U, BinaryMethod<T, U>::method>::And(m_int, (U)rhs);
+        return *this;
+    }
+
+    // XOR
+    SafeInt<T, E> operator^(SafeInt<T, E> rhs) const SAFEINT_NOTHROW { return SafeInt<T, E>((T)(m_int ^ (T)rhs)); }
+
+    template<typename U>
+    SafeInt<T, E> operator^(U rhs) const SAFEINT_NOTHROW
+    {
+        // If you land in the assert, this is because the bitwise operator
+        // was causing unexpected behavior. Fix is to properly cast your inputs
+        // so that it works like you meant, not unexpectedly
+
+        return SafeInt<T, E>(BinaryXorHelper<T, U, BinaryMethod<T, U>::method>::Xor(m_int, rhs));
+    }
+
+    // XOR assignment
+    SafeInt<T, E>& operator^=(SafeInt<T, E> rhs) SAFEINT_NOTHROW
+    {
+        m_int ^= (T)rhs;
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator^=(U rhs) SAFEINT_NOTHROW
+    {
+        m_int = BinaryXorHelper<T, U, BinaryMethod<T, U>::method>::Xor(m_int, rhs);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator^=(SafeInt<U, E> rhs) SAFEINT_NOTHROW
+    {
+        m_int = BinaryXorHelper<T, U, BinaryMethod<T, U>::method>::Xor(m_int, (U)rhs);
+        return *this;
+    }
+
+    // bitwise OR
+    SafeInt<T, E> operator|(SafeInt<T, E> rhs) const SAFEINT_NOTHROW { return SafeInt<T, E>((T)(m_int | (T)rhs)); }
+
+    template<typename U>
+    SafeInt<T, E> operator|(U rhs) const SAFEINT_NOTHROW
+    {
+        return SafeInt<T, E>(BinaryOrHelper<T, U, BinaryMethod<T, U>::method>::Or(m_int, rhs));
+    }
+
+    // bitwise OR assignment
+    SafeInt<T, E>& operator|=(SafeInt<T, E> rhs) SAFEINT_NOTHROW
+    {
+        m_int |= (T)rhs;
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator|=(U rhs) SAFEINT_NOTHROW
+    {
+        m_int = BinaryOrHelper<T, U, BinaryMethod<T, U>::method>::Or(m_int, rhs);
+        return *this;
+    }
+
+    template<typename U>
+    SafeInt<T, E>& operator|=(SafeInt<U, E> rhs) SAFEINT_NOTHROW
+    {
+        m_int = BinaryOrHelper<T, U, BinaryMethod<T, U>::method>::Or(m_int, (U)rhs);
+        return *this;
+    }
+
+    // Miscellaneous helper functions
+    SafeInt<T, E> Min(SafeInt<T, E> test, const T floor = IntTraits<T>::minInt) const SAFEINT_NOTHROW
+    {
+        T tmp = test < m_int ? (T)test : m_int;
+        return tmp < floor ? floor : tmp;
+    }
+
+    SafeInt<T, E> Max(SafeInt<T, E> test, const T upper = IntTraits<T>::maxInt) const SAFEINT_NOTHROW
+    {
+        T tmp = test > m_int ? (T)test : m_int;
+        return tmp > upper ? upper : tmp;
+    }
+
+    void Swap(SafeInt<T, E>& with) SAFEINT_NOTHROW
+    {
+        T temp(m_int);
+        m_int = with.m_int;
+        with.m_int = temp;
+    }
+
+    static SafeInt<T, E> SafeAtoI(const char* input) SAFEINT_CPP_THROW { return SafeTtoI(input); }
+
+    static SafeInt<T, E> SafeWtoI(const wchar_t* input) { return SafeTtoI(input); }
+
+    enum alignBits
+    {
+        align2 = 1,
+        align4 = 2,
+        align8 = 3,
+        align16 = 4,
+        align32 = 5,
+        align64 = 6,
+        align128 = 7,
+        align256 = 8
+    };
+
+    template<alignBits bits>
+    const SafeInt<T, E>& Align() SAFEINT_CPP_THROW
+    {
+        // Zero is always aligned
+        if (m_int == 0) return *this;
+
+        // We don't support aligning negative numbers at this time
+        // Can't align unsigned numbers on bitCount (e.g., 8 bits = 256, unsigned char max = 255)
+        // or signed numbers on bitCount-1 (e.g., 7 bits = 128, signed char max = 127).
+        // Also makes no sense to try to align on negative or no bits.
+
+        ShiftAssert(((IntTraits<T>::isSigned && bits < (int)IntTraits<T>::bitCount - 1) ||
+                     (!IntTraits<T>::isSigned && bits < (int)IntTraits<T>::bitCount)) &&
+                    bits >= 0 && (!IntTraits<T>::isSigned || m_int > 0));
+
+        const T AlignValue = ((T)1 << bits) - 1;
+
+        m_int = (T)((m_int + AlignValue) & ~AlignValue);
+
+        if (m_int <= 0) E::SafeIntOnOverflow();
+
+        return *this;
+    }
+
+    // Commonly needed alignments:
+    const SafeInt<T, E>& Align2() { return Align<align2>(); }
+    const SafeInt<T, E>& Align4() { return Align<align4>(); }
+    const SafeInt<T, E>& Align8() { return Align<align8>(); }
+    const SafeInt<T, E>& Align16() { return Align<align16>(); }
+    const SafeInt<T, E>& Align32() { return Align<align32>(); }
+    const SafeInt<T, E>& Align64() { return Align<align64>(); }
+
+private:
+    // This is almost certainly not the best optimized version of atoi,
+    // but it does not display a typical bug where it isn't possible to set MinInt
+    // and it won't allow you to overflow your integer.
+    // This is here because it is useful, and it is an example of what
+    // can be done easily with SafeInt.
+    template<typename U>
+    static SafeInt<T, E> SafeTtoI(U* input) SAFEINT_CPP_THROW
+    {
+        U* tmp = input;
+        SafeInt<T, E> s;
+        bool negative = false;
+
+        // Bad input, or empty string
+        if (input == nullptr || input[0] == 0) E::SafeIntOnOverflow();
+
+        switch (*tmp)
+        {
+            case '-':
+                tmp++;
+                negative = true;
+                break;
+            case '+': tmp++; break;
+        }
+
+        while (*tmp != 0)
+        {
+            if (*tmp < '0' || *tmp > '9') break;
+
+            if ((T)s != 0) s *= (T)10;
+
+            if (!negative)
+                s += (T)(*tmp - '0');
+            else
+                s -= (T)(*tmp - '0');
+
+            tmp++;
+        }
+
+        return s;
+    }
+
+    T m_int;
+};
+
+// Helper function used to subtract pointers.
+// Used to squelch warnings
+template<typename P>
+SafeInt<ptrdiff_t, SafeIntDefaultExceptionHandler> SafePtrDiff(const P* p1, const P* p2) SAFEINT_CPP_THROW
+{
+    return SafeInt<ptrdiff_t, SafeIntDefaultExceptionHandler>(p1 - p2);
+}
+
+// Comparison operators
+
+// Less than
+template<typename T, typename U, typename E>
+bool operator<(U lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return GreaterThanTest<T, U, ValidComparison<T, U>::method>::GreaterThan((T)rhs, lhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator<(SafeInt<T, E> lhs, U rhs) SAFEINT_NOTHROW
+{
+    return GreaterThanTest<U, T, ValidComparison<U, T>::method>::GreaterThan(rhs, (T)lhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator<(SafeInt<U, E> lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return GreaterThanTest<T, U, ValidComparison<T, U>::method>::GreaterThan((T)rhs, (U)lhs);
+}
+
+// Greater than
+template<typename T, typename U, typename E>
+bool operator>(U lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return GreaterThanTest<U, T, ValidComparison<U, T>::method>::GreaterThan(lhs, (T)rhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator>(SafeInt<T, E> lhs, U rhs) SAFEINT_NOTHROW
+{
+    return GreaterThanTest<T, U, ValidComparison<T, U>::method>::GreaterThan((T)lhs, rhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator>(SafeInt<T, E> lhs, SafeInt<U, E> rhs) SAFEINT_NOTHROW
+{
+    return GreaterThanTest<T, U, ValidComparison<T, U>::method>::GreaterThan((T)lhs, (U)rhs);
+}
+
+// Greater than or equal
+template<typename T, typename U, typename E>
+bool operator>=(U lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return !GreaterThanTest<T, U, ValidComparison<T, U>::method>::GreaterThan((T)rhs, lhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator>=(SafeInt<T, E> lhs, U rhs) SAFEINT_NOTHROW
+{
+    return !GreaterThanTest<U, T, ValidComparison<U, T>::method>::GreaterThan(rhs, (T)lhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator>=(SafeInt<T, E> lhs, SafeInt<U, E> rhs) SAFEINT_NOTHROW
+{
+    return !GreaterThanTest<U, T, ValidComparison<U, T>::method>::GreaterThan((U)rhs, (T)lhs);
+}
+
+// Less than or equal
+template<typename T, typename U, typename E>
+bool operator<=(U lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return !GreaterThanTest<U, T, ValidComparison<U, T>::method>::GreaterThan(lhs, (T)rhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator<=(SafeInt<T, E> lhs, U rhs) SAFEINT_NOTHROW
+{
+    return !GreaterThanTest<T, U, ValidComparison<T, U>::method>::GreaterThan((T)lhs, rhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator<=(SafeInt<T, E> lhs, SafeInt<U, E> rhs) SAFEINT_NOTHROW
+{
+    return !GreaterThanTest<T, U, ValidComparison<T, U>::method>::GreaterThan((T)lhs, (U)rhs);
+}
+
+// equality
+// explicit overload for bool
+template<typename T, typename E>
+bool operator==(bool lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return lhs == ((T)rhs == 0 ? false : true);
+}
+
+template<typename T, typename E>
+bool operator==(SafeInt<T, E> lhs, bool rhs) SAFEINT_NOTHROW
+{
+    return rhs == ((T)lhs == 0 ? false : true);
+}
+
+template<typename T, typename U, typename E>
+bool operator==(U lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return EqualityTest<T, U, ValidComparison<T, U>::method>::IsEquals((T)rhs, lhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator==(SafeInt<T, E> lhs, U rhs) SAFEINT_NOTHROW
+{
+    return EqualityTest<T, U, ValidComparison<T, U>::method>::IsEquals((T)lhs, rhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator==(SafeInt<T, E> lhs, SafeInt<U, E> rhs) SAFEINT_NOTHROW
+{
+    return EqualityTest<T, U, ValidComparison<T, U>::method>::IsEquals((T)lhs, (U)rhs);
+}
+
+// not equals
+template<typename T, typename U, typename E>
+bool operator!=(U lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return !EqualityTest<T, U, ValidComparison<T, U>::method>::IsEquals((T)rhs, lhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator!=(SafeInt<T, E> lhs, U rhs) SAFEINT_NOTHROW
+{
+    return !EqualityTest<T, U, ValidComparison<T, U>::method>::IsEquals((T)lhs, rhs);
+}
+
+template<typename T, typename U, typename E>
+bool operator!=(SafeInt<T, E> lhs, SafeInt<U, E> rhs) SAFEINT_NOTHROW
+{
+    return !EqualityTest<T, U, ValidComparison<T, U>::method>::IsEquals(lhs, rhs);
+}
+
+template<typename T, typename E>
+bool operator!=(bool lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return ((T)rhs == 0 ? false : true) != lhs;
+}
+
+template<typename T, typename E>
+bool operator!=(SafeInt<T, E> lhs, bool rhs) SAFEINT_NOTHROW
+{
+    return ((T)lhs == 0 ? false : true) != rhs;
+}
+
+template<typename T, typename U, typename E, int method>
+class ModulusSimpleCaseHelper;
+
+template<typename T, typename E, int method>
+class ModulusSignedCaseHelper;
+
+template<typename T, typename E>
+class ModulusSignedCaseHelper<T, E, true>
+{
+public:
+    static bool SignedCase(SafeInt<T, E> rhs, SafeInt<T, E>& result) SAFEINT_NOTHROW
+    {
+        if ((T)rhs == (T)-1)
+        {
+            result = 0;
+            return true;
+        }
+        return false;
+    }
+};
+
+template<typename T, typename E>
+class ModulusSignedCaseHelper<T, E, false>
+{
+public:
+    static bool SignedCase(SafeInt<T, E> /*rhs*/, SafeInt<T, E>& /*result*/) SAFEINT_NOTHROW { return false; }
+};
+
+template<typename T, typename U, typename E>
+class ModulusSimpleCaseHelper<T, U, E, true>
+{
+public:
+    static bool ModulusSimpleCase(U lhs, SafeInt<T, E> rhs, SafeInt<T, E>& result) SAFEINT_CPP_THROW
+    {
+        if (rhs != 0)
+        {
+            if (ModulusSignedCaseHelper<T, E, IntTraits<T>::isSigned>::SignedCase(rhs, result)) return true;
+
+            result = SafeInt<T, E>((T)(lhs % (T)rhs));
+            return true;
+        }
+
+        E::SafeIntOnDivZero();
+    }
+};
+
+template<typename T, typename U, typename E>
+class ModulusSimpleCaseHelper<T, U, E, false>
+{
+public:
+    static bool ModulusSimpleCase(U /*lhs*/, SafeInt<T, E> /*rhs*/, SafeInt<T, E>& /*result*/) SAFEINT_NOTHROW
+    {
+        return false;
+    }
+};
+
+// Modulus
+template<typename T, typename U, typename E>
+SafeInt<T, E> operator%(U lhs, SafeInt<T, E> rhs) SAFEINT_CPP_THROW
+{
+    // Value of return depends on sign of lhs
+    // This one may not be safe - bounds check in constructor
+    // if lhs is negative and rhs is unsigned, this will throw an exception.
+
+    // Fast-track the simple case
+    // same size and same sign
+    SafeInt<T, E> result;
+
+    if (ModulusSimpleCaseHelper < T,
+        U,
+        E,
+        sizeof(T) == sizeof(U) &&
+            (bool)IntTraits<T>::isSigned == (bool)IntTraits<U>::isSigned > ::ModulusSimpleCase(lhs, rhs, result))
+        return result;
+
+    return SafeInt<T, E>((SafeInt<U, E>(lhs) % (T)rhs));
+}
+
+// Multiplication
+template<typename T, typename U, typename E>
+SafeInt<T, E> operator*(U lhs, SafeInt<T, E> rhs)SAFEINT_CPP_THROW
+{
+    T ret(0);
+    MultiplicationHelper<T, U, MultiplicationMethod<T, U>::method>::template MultiplyThrow<E>((T)rhs, lhs, ret);
+    return SafeInt<T, E>(ret);
+}
+
+template<typename T, typename U, typename E, int method>
+class DivisionNegativeCornerCaseHelper;
+
+template<typename T, typename U, typename E>
+class DivisionNegativeCornerCaseHelper<T, U, E, true>
+{
+public:
+    static bool NegativeCornerCase(U lhs, SafeInt<T, E> rhs, SafeInt<T, E>& result) SAFEINT_CPP_THROW
+    {
+        // Problem case - normal casting behavior changes meaning
+        // flip rhs to positive
+        // any operator casts now do the right thing
+        U tmp;
+
+        if (CompileConst<sizeof(T) == 4>::Value())
+            tmp = lhs / (U)(~(unsigned __int32)(T)rhs + 1);
+        else
+            tmp = lhs / (U)(~(unsigned __int64)(T)rhs + 1);
+
+        if (tmp <= (U)IntTraits<T>::maxInt)
+        {
+            result = SafeInt<T, E>((T)(~(unsigned __int64)tmp + 1));
+            return true;
+        }
+
+        // Corner case
+        T maxT = IntTraits<T>::maxInt;
+        if (tmp == (U)maxT + 1)
+        {
+            T minT = IntTraits<T>::minInt;
+            result = SafeInt<T, E>(minT);
+            return true;
+        }
+
+        E::SafeIntOnOverflow();
+    }
+};
+
+template<typename T, typename U, typename E>
+class DivisionNegativeCornerCaseHelper<T, U, E, false>
+{
+public:
+    static bool NegativeCornerCase(U /*lhs*/, SafeInt<T, E> /*rhs*/, SafeInt<T, E>& /*result*/) SAFEINT_NOTHROW
+    {
+        return false;
+    }
+};
+
+template<typename T, typename U, typename E, int method>
+class DivisionCornerCaseHelper;
+
+template<typename T, typename U, typename E>
+class DivisionCornerCaseHelper<T, U, E, true>
+{
+public:
+    static bool DivisionCornerCase1(U lhs, SafeInt<T, E> rhs, SafeInt<T, E>& result) SAFEINT_CPP_THROW
+    {
+        if ((T)rhs > 0)
+        {
+            result = SafeInt<T, E>(lhs / (T)rhs);
+            return true;
+        }
+
+        // Now rhs is either negative, or zero
+        if ((T)rhs != 0)
+        {
+            if (DivisionNegativeCornerCaseHelper < T,
+                U,
+                E,
+                sizeof(U) >= 4 && sizeof(T) <= sizeof(U) > ::NegativeCornerCase(lhs, rhs, result))
+                return true;
+
+            result = SafeInt<T, E>(lhs / (T)rhs);
+            return true;
+        }
+
+        E::SafeIntOnDivZero();
+    }
+};
+
+template<typename T, typename U, typename E>
+class DivisionCornerCaseHelper<T, U, E, false>
+{
+public:
+    static bool DivisionCornerCase1(U /*lhs*/, SafeInt<T, E> /*rhs*/, SafeInt<T, E>& /*result*/) SAFEINT_NOTHROW
+    {
+        return false;
+    }
+};
+
+template<typename T, typename U, typename E, int method>
+class DivisionCornerCaseHelper2;
+
+template<typename T, typename U, typename E>
+class DivisionCornerCaseHelper2<T, U, E, true>
+{
+public:
+    static bool DivisionCornerCase2(U lhs, SafeInt<T, E> rhs, SafeInt<T, E>& result) SAFEINT_CPP_THROW
+    {
+        if (lhs == IntTraits<U>::minInt && (T)rhs == -1)
+        {
+            // corner case of a corner case - lhs = min int, rhs = -1,
+            // but rhs is the return type, so in essence, we can return -lhs
+            // if rhs is a larger type than lhs
+            // If types are wrong, throws
+
+#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
+#pragma warning(push)
+// cast truncates constant value
+#pragma warning(disable : 4310)
+#endif
+
+            if (CompileConst<sizeof(U) < sizeof(T)>::Value())
+                result = SafeInt<T, E>((T)(-(T)IntTraits<U>::minInt));
+            else
+                E::SafeIntOnOverflow();
+
+#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
+#pragma warning(pop)
+#endif
+
+            return true;
+        }
+
+        return false;
+    }
+};
+
+template<typename T, typename U, typename E>
+class DivisionCornerCaseHelper2<T, U, E, false>
+{
+public:
+    static bool DivisionCornerCase2(U /*lhs*/, SafeInt<T, E> /*rhs*/, SafeInt<T, E>& /*result*/) SAFEINT_NOTHROW
+    {
+        return false;
+    }
+};
+
+// Division
+template<typename T, typename U, typename E>
+SafeInt<T, E> operator/(U lhs, SafeInt<T, E> rhs) SAFEINT_CPP_THROW
+{
+    // Corner case - has to be handled seperately
+    SafeInt<T, E> result;
+    if (DivisionCornerCaseHelper<T, U, E, (int)DivisionMethod<U, T>::method == (int)DivisionState_UnsignedSigned>::
+            DivisionCornerCase1(lhs, rhs, result))
+        return result;
+
+    if (DivisionCornerCaseHelper2<T, U, E, SafeIntCompare<T, U>::isBothSigned>::DivisionCornerCase2(lhs, rhs, result))
+        return result;
+
+    // Otherwise normal logic works with addition of bounds check when casting from U->T
+    U ret;
+    DivisionHelper<U, T, DivisionMethod<U, T>::method>::template DivideThrow<E>(lhs, (T)rhs, ret);
+    return SafeInt<T, E>(ret);
+}
+
+// Addition
+template<typename T, typename U, typename E>
+SafeInt<T, E> operator+(U lhs, SafeInt<T, E> rhs) SAFEINT_CPP_THROW
+{
+    T ret(0);
+    AdditionHelper<T, U, AdditionMethod<T, U>::method>::template AdditionThrow<E>((T)rhs, lhs, ret);
+    return SafeInt<T, E>(ret);
+}
+
+// Subtraction
+template<typename T, typename U, typename E>
+SafeInt<T, E> operator-(U lhs, SafeInt<T, E> rhs) SAFEINT_CPP_THROW
+{
+    T ret(0);
+    SubtractionHelper<U, T, SubtractionMethod2<U, T>::method>::template SubtractThrow<E>(lhs, rhs.Ref(), ret);
+
+    return SafeInt<T, E>(ret);
+}
+
+// Overrides designed to deal with cases where a SafeInt is assigned out
+// to a normal int - this at least makes the last operation safe
+// +=
+template<typename T, typename U, typename E>
+T& operator+=(T& lhs, SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+{
+    T ret(0);
+    AdditionHelper<T, U, AdditionMethod<T, U>::method>::template AdditionThrow<E>(lhs, (U)rhs, ret);
+    lhs = ret;
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T& operator-=(T& lhs, SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+{
+    T ret(0);
+    SubtractionHelper<T, U, SubtractionMethod<T, U>::method>::template SubtractThrow<E>(lhs, (U)rhs, ret);
+    lhs = ret;
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T& operator*=(T& lhs, SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+{
+    T ret(0);
+    MultiplicationHelper<T, U, MultiplicationMethod<T, U>::method>::template MultiplyThrow<E>(lhs, (U)rhs, ret);
+    lhs = ret;
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T& operator/=(T& lhs, SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+{
+    T ret(0);
+    DivisionHelper<T, U, DivisionMethod<T, U>::method>::template DivideThrow<E>(lhs, (U)rhs, ret);
+    lhs = ret;
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T& operator%=(T& lhs, SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+{
+    T ret(0);
+    ModulusHelper<T, U, ValidComparison<T, U>::method>::template ModulusThrow<E>(lhs, (U)rhs, ret);
+    lhs = ret;
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T& operator&=(T& lhs, SafeInt<U, E> rhs) SAFEINT_NOTHROW
+{
+    lhs = BinaryAndHelper<T, U, BinaryMethod<T, U>::method>::And(lhs, (U)rhs);
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T& operator^=(T& lhs, SafeInt<U, E> rhs) SAFEINT_NOTHROW
+{
+    lhs = BinaryXorHelper<T, U, BinaryMethod<T, U>::method>::Xor(lhs, (U)rhs);
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T& operator|=(T& lhs, SafeInt<U, E> rhs) SAFEINT_NOTHROW
+{
+    lhs = BinaryOrHelper<T, U, BinaryMethod<T, U>::method>::Or(lhs, (U)rhs);
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T& operator<<=(T& lhs, SafeInt<U, E> rhs) SAFEINT_NOTHROW
+{
+    lhs = (T)(SafeInt<T, E>(lhs) << (U)rhs);
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T& operator>>=(T& lhs, SafeInt<U, E> rhs) SAFEINT_NOTHROW
+{
+    lhs = (T)(SafeInt<T, E>(lhs) >> (U)rhs);
+    return lhs;
+}
+
+// Specific pointer overrides
+// Note - this function makes no attempt to ensure
+// that the resulting pointer is still in the buffer, only
+// that no int overflows happened on the way to getting the new pointer
+template<typename T, typename U, typename E>
+T*& operator+=(T*& lhs, SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+{
+    // Cast the pointer to a number so we can do arithmetic
+    SafeInt<size_t, E> ptr_val = reinterpret_cast<size_t>(lhs);
+    // Check first that rhs is valid for the type of ptrdiff_t
+    // and that multiplying by sizeof( T ) doesn't overflow a ptrdiff_t
+    // Next, we need to add 2 SafeInts of different types, so unbox the ptr_diff
+    // Finally, cast the number back to a pointer of the correct type
+    lhs = reinterpret_cast<T*>((size_t)(ptr_val + (ptrdiff_t)(SafeInt<ptrdiff_t, E>(rhs) * sizeof(T))));
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T*& operator-=(T*& lhs, SafeInt<U, E> rhs) SAFEINT_CPP_THROW
+{
+    // Cast the pointer to a number so we can do arithmetic
+    SafeInt<size_t, E> ptr_val = reinterpret_cast<size_t>(lhs);
+    // See above for comments
+    lhs = reinterpret_cast<T*>((size_t)(ptr_val - (ptrdiff_t)(SafeInt<ptrdiff_t, E>(rhs) * sizeof(T))));
+    return lhs;
+}
+
+template<typename T, typename U, typename E>
+T*& operator*=(T*& lhs, SafeInt<U, E>) SAFEINT_NOTHROW
+{
+    // This operator explicitly not supported
+    C_ASSERT(sizeof(T) == 0);
+    return (lhs = NULL);
+}
+
+template<typename T, typename U, typename E>
+T*& operator/=(T*& lhs, SafeInt<U, E>) SAFEINT_NOTHROW
+{
+    // This operator explicitly not supported
+    C_ASSERT(sizeof(T) == 0);
+    return (lhs = NULL);
+}
+
+template<typename T, typename U, typename E>
+T*& operator%=(T*& lhs, SafeInt<U, E>) SAFEINT_NOTHROW
+{
+    // This operator explicitly not supported
+    C_ASSERT(sizeof(T) == 0);
+    return (lhs = NULL);
+}
+
+template<typename T, typename U, typename E>
+T*& operator&=(T*& lhs, SafeInt<U, E>) SAFEINT_NOTHROW
+{
+    // This operator explicitly not supported
+    C_ASSERT(sizeof(T) == 0);
+    return (lhs = NULL);
+}
+
+template<typename T, typename U, typename E>
+T*& operator^=(T*& lhs, SafeInt<U, E>) SAFEINT_NOTHROW
+{
+    // This operator explicitly not supported
+    C_ASSERT(sizeof(T) == 0);
+    return (lhs = NULL);
+}
+
+template<typename T, typename U, typename E>
+T*& operator|=(T*& lhs, SafeInt<U, E>) SAFEINT_NOTHROW
+{
+    // This operator explicitly not supported
+    C_ASSERT(sizeof(T) == 0);
+    return (lhs = NULL);
+}
+
+template<typename T, typename U, typename E>
+T*& operator<<=(T*& lhs, SafeInt<U, E>) SAFEINT_NOTHROW
+{
+    // This operator explicitly not supported
+    C_ASSERT(sizeof(T) == 0);
+    return (lhs = NULL);
+}
+
+template<typename T, typename U, typename E>
+T*& operator>>=(T*& lhs, SafeInt<U, E>) SAFEINT_NOTHROW
+{
+    // This operator explicitly not supported
+    C_ASSERT(sizeof(T) == 0);
+    return (lhs = NULL);
+}
+
+// Shift operators
+// NOTE - shift operators always return the type of the lhs argument
+
+// Left shift
+template<typename T, typename U, typename E>
+SafeInt<U, E> operator<<(U lhs, SafeInt<T, E> bits) SAFEINT_NOTHROW
+{
+    ShiftAssert(!IntTraits<T>::isSigned || (T)bits >= 0);
+    ShiftAssert((T)bits < (int)IntTraits<U>::bitCount);
+
+    return SafeInt<U, E>((U)(lhs << (T)bits));
+}
+
+// Right shift
+template<typename T, typename U, typename E>
+SafeInt<U, E> operator>>(U lhs, SafeInt<T, E> bits) SAFEINT_NOTHROW
+{
+    ShiftAssert(!IntTraits<T>::isSigned || (T)bits >= 0);
+    ShiftAssert((T)bits < (int)IntTraits<U>::bitCount);
+
+    return SafeInt<U, E>((U)(lhs >> (T)bits));
+}
+
+// Bitwise operators
+// This only makes sense if we're dealing with the same type and size
+// demand a type T, or something that fits into a type T.
+
+// Bitwise &
+template<typename T, typename U, typename E>
+SafeInt<T, E> operator&(U lhs, SafeInt<T, E> rhs)SAFEINT_NOTHROW
+{
+    return SafeInt<T, E>(BinaryAndHelper<T, U, BinaryMethod<T, U>::method>::And((T)rhs, lhs));
+}
+
+// Bitwise XOR
+template<typename T, typename U, typename E>
+SafeInt<T, E> operator^(U lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return SafeInt<T, E>(BinaryXorHelper<T, U, BinaryMethod<T, U>::method>::Xor((T)rhs, lhs));
+}
+
+// Bitwise OR
+template<typename T, typename U, typename E>
+SafeInt<T, E> operator|(U lhs, SafeInt<T, E> rhs) SAFEINT_NOTHROW
+{
+    return SafeInt<T, E>(BinaryOrHelper<T, U, BinaryMethod<T, U>::method>::Or((T)rhs, lhs));
+}
+
+#if SAFEINT_COMPILER == GCC_COMPILER
+#pragma GCC diagnostic pop
+#endif
+
+#if SAFEINT_COMPILER == CLANG_COMPILER
+#pragma clang diagnostic pop
+#endif
+
+#ifdef C_ASSERT_DEFINED_SAFEINT
+#undef C_ASSERT
+#undef C_ASSERT_DEFINED_SAFEINT
+#endif // C_ASSERT_DEFINED_SAFEINT
+
+} // namespace safeint3
+} // namespace msl
diff --git a/Release/include/cpprest/details/basic_types.h b/Release/include/cpprest/details/basic_types.h
new file mode 100644 (file)
index 0000000..d2ceb87
--- /dev/null
@@ -0,0 +1,131 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Platform-dependent type definitions
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/details/cpprest_compat.h"
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#ifndef _WIN32
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdint.h>
+#else
+#include <cstdint>
+#endif
+
+#include "cpprest/details/SafeInt3.hpp"
+
+namespace utility
+{
+#ifdef _WIN32
+#define _UTF16_STRINGS
+#endif
+
+// We should be using a 64-bit size type for most situations that do
+// not involve specifying the size of a memory allocation or buffer.
+typedef uint64_t size64_t;
+
+#ifndef _WIN32
+typedef uint32_t HRESULT; // Needed for PPLX
+#endif
+
+#ifdef _UTF16_STRINGS
+//
+// On Windows, all strings are wide
+//
+typedef wchar_t char_t;
+typedef std::wstring string_t;
+#define _XPLATSTR(x) L##x
+typedef std::wostringstream ostringstream_t;
+typedef std::wofstream ofstream_t;
+typedef std::wostream ostream_t;
+typedef std::wistream istream_t;
+typedef std::wifstream ifstream_t;
+typedef std::wistringstream istringstream_t;
+typedef std::wstringstream stringstream_t;
+#define ucout std::wcout
+#define ucin std::wcin
+#define ucerr std::wcerr
+#else
+//
+// On POSIX platforms, all strings are narrow
+//
+typedef char char_t;
+typedef std::string string_t;
+#define _XPLATSTR(x) x
+typedef std::ostringstream ostringstream_t;
+typedef std::ofstream ofstream_t;
+typedef std::ostream ostream_t;
+typedef std::istream istream_t;
+typedef std::ifstream ifstream_t;
+typedef std::istringstream istringstream_t;
+typedef std::stringstream stringstream_t;
+#define ucout std::cout
+#define ucin std::cin
+#define ucerr std::cerr
+#endif // endif _UTF16_STRINGS
+
+#ifndef _TURN_OFF_PLATFORM_STRING
+// The 'U' macro can be used to create a string or character literal of the platform type, i.e. utility::char_t.
+// If you are using a library causing conflicts with 'U' macro, it can be turned off by defining the macro
+// '_TURN_OFF_PLATFORM_STRING' before including the C++ REST SDK header files, and e.g. use '_XPLATSTR' instead.
+#define U(x) _XPLATSTR(x)
+#endif // !_TURN_OFF_PLATFORM_STRING
+
+} // namespace utility
+
+typedef char utf8char;
+typedef std::string utf8string;
+typedef std::stringstream utf8stringstream;
+typedef std::ostringstream utf8ostringstream;
+typedef std::ostream utf8ostream;
+typedef std::istream utf8istream;
+typedef std::istringstream utf8istringstream;
+
+#ifdef _UTF16_STRINGS
+typedef wchar_t utf16char;
+typedef std::wstring utf16string;
+typedef std::wstringstream utf16stringstream;
+typedef std::wostringstream utf16ostringstream;
+typedef std::wostream utf16ostream;
+typedef std::wistream utf16istream;
+typedef std::wistringstream utf16istringstream;
+#else
+typedef char16_t utf16char;
+typedef std::u16string utf16string;
+typedef std::basic_stringstream<utf16char> utf16stringstream;
+typedef std::basic_ostringstream<utf16char> utf16ostringstream;
+typedef std::basic_ostream<utf16char> utf16ostream;
+typedef std::basic_istream<utf16char> utf16istream;
+typedef std::basic_istringstream<utf16char> utf16istringstream;
+#endif
+
+#if defined(_WIN32)
+// Include on everything except Windows Desktop ARM, unless explicitly excluded.
+#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
+#if defined(WINAPI_FAMILY)
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && defined(_M_ARM)
+#define CPPREST_EXCLUDE_WEBSOCKETS
+#endif
+#else
+#if defined(_M_ARM)
+#define CPPREST_EXCLUDE_WEBSOCKETS
+#endif
+#endif
+#endif
+#endif
diff --git a/Release/include/cpprest/details/cpprest_compat.h b/Release/include/cpprest/details/cpprest_compat.h
new file mode 100644 (file)
index 0000000..6dae41d
--- /dev/null
@@ -0,0 +1,86 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Standard macros and definitions.
+ * This header has minimal dependency on windows headers and is safe for use in the public API
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if defined(_WIN32)
+
+#if _MSC_VER >= 1900
+#define CPPREST_NOEXCEPT noexcept
+#define CPPREST_CONSTEXPR constexpr
+#else
+#define CPPREST_NOEXCEPT
+#define CPPREST_CONSTEXPR const
+#endif // _MSC_VER >= 1900
+
+#include <sal.h>
+
+#else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv
+
+#define __declspec(x) __attribute__((x))
+#define dllimport
+#define novtable /* no novtable equivalent */
+#define __assume(x)                                                                                                    \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        if (!(x)) __builtin_unreachable();                                                                             \
+    } while (false)
+#define CPPREST_NOEXCEPT noexcept
+#define CPPREST_CONSTEXPR constexpr
+
+#include <assert.h>
+#define _ASSERTE(x) assert(x)
+
+// No SAL on non Windows platforms
+#include "cpprest/details/nosal.h"
+
+#if !defined(__cdecl)
+#if defined(cdecl)
+#define __cdecl __attribute__((cdecl))
+#else // ^^^ defined cdecl ^^^ // vvv !defined cdecl vvv
+#define __cdecl
+#endif // defined cdecl
+#endif // not defined __cdecl
+
+#if defined(__ANDROID__)
+// This is needed to disable the use of __thread inside the boost library.
+// Android does not support thread local storage -- if boost is included
+// without this macro defined, it will create references to __tls_get_addr
+// which (while able to link) will not be available at runtime and prevent
+// the .so from loading.
+#if not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
+#define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
+#endif // not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
+#endif // defined(__ANDROID__)
+
+#ifdef __clang__
+#include <cstdio>
+#endif // __clang__
+#endif // _WIN32
+
+#ifdef _NO_ASYNCRTIMP
+#define _ASYNCRTIMP
+#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
+#endif // _NO_ASYNCRTIMP
+
+#ifdef CASABLANCA_DEPRECATION_NO_WARNINGS
+#define CASABLANCA_DEPRECATED(x)
+#else
+#define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x))
+#endif // CASABLANCA_DEPRECATION_NO_WARNINGS
diff --git a/Release/include/cpprest/details/fileio.h b/Release/include/cpprest/details/fileio.h
new file mode 100644 (file)
index 0000000..ee88c15
--- /dev/null
@@ -0,0 +1,220 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * fileio.h
+ *
+ * Asynchronous I/O: stream buffer implementation details
+ *
+ * We're going to some lengths to avoid exporting C++ class member functions and implementation details across
+ * module boundaries, and the factoring requires that we keep the implementation details away from the main header
+ * files. The supporting functions, which are in this file, use C-like signatures to avoid as many issues as
+ * possible.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifdef _WIN32
+#include <cstdint>
+#endif
+
+#include "cpprest/details/basic_types.h"
+#include "pplx/pplxtasks.h"
+
+namespace Concurrency
+{
+namespace streams
+{
+namespace details
+{
+/// <summary>
+/// A record containing the essential private data members of a file stream,
+/// in particular the parts that need to be shared between the public header
+/// file and the implementation in the implementation file.
+/// </summary>
+struct _file_info
+{
+    _ASYNCRTIMP _file_info(std::ios_base::openmode mode, size_t buffer_size)
+        : m_rdpos(0)
+        , m_wrpos(0)
+        , m_atend(false)
+        , m_buffer_size(buffer_size)
+        , m_buffer(nullptr)
+        , m_bufoff(0)
+        , m_bufsize(0)
+        , m_buffill(0)
+        , m_mode(mode)
+    {
+    }
+
+    // Positional data
+
+    size_t m_rdpos;
+    size_t m_wrpos;
+    bool m_atend;
+
+    // Input buffer
+
+    size_t m_buffer_size; // The intended size of the buffer to read into.
+    char* m_buffer;
+
+    size_t m_bufoff;                          // File position that the start of the buffer represents.
+    msl::safeint3::SafeInt<size_t> m_bufsize; // Buffer allocated size, as actually allocated.
+    size_t m_buffill;                         // Amount of file data actually in the buffer
+
+    std::ios_base::openmode m_mode;
+
+    pplx::extensibility::recursive_lock_t m_lock;
+};
+
+/// <summary>
+/// This interface provides the necessary callbacks for completion events.
+/// </summary>
+class _filestream_callback
+{
+public:
+    virtual void on_opened(_In_ details::_file_info*) {}
+    virtual void on_closed() {}
+    virtual void on_error(const std::exception_ptr&) {}
+    virtual void on_completed(size_t) {}
+
+protected:
+    virtual ~_filestream_callback() {}
+};
+
+} // namespace details
+} // namespace streams
+} // namespace Concurrency
+
+extern "C"
+{
+/// <summary>
+/// Open a file and create a streambuf instance to represent it.
+/// </summary>
+/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
+/// <param name="filename">The name of the file to open</param>
+/// <param name="mode">A creation mode for the stream buffer</param>
+/// <param name="prot">A file protection mode to use for the file stream (not supported on Linux)</param>
+/// <returns><c>true</c> if the opening operation could be initiated, <c>false</c> otherwise.</returns>
+/// <remarks>
+/// True does not signal that the file will eventually be successfully opened, just that the process was started.
+/// </remarks>
+#if !defined(__cplusplus_winrt)
+    _ASYNCRTIMP bool __cdecl _open_fsb_str(_In_ concurrency::streams::details::_filestream_callback* callback,
+                                           const utility::char_t* filename,
+                                           std::ios_base::openmode mode,
+                                           int prot);
+#endif
+
+/// <summary>
+/// Create a streambuf instance to represent a WinRT file.
+/// </summary>
+/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
+/// <param name="file">The file object</param>
+/// <param name="mode">A creation mode for the stream buffer</param>
+/// <returns><c>true</c> if the opening operation could be initiated, <c>false</c> otherwise.</returns>
+/// <remarks>
+/// True does not signal that the file will eventually be successfully opened, just that the process was started.
+/// This is only available for WinRT.
+/// </remarks>
+#if defined(__cplusplus_winrt)
+    _ASYNCRTIMP bool __cdecl _open_fsb_stf_str(_In_ concurrency::streams::details::_filestream_callback* callback,
+                                               ::Windows::Storage::StorageFile ^ file,
+                                               std::ios_base::openmode mode,
+                                               int prot);
+#endif
+
+    /// <summary>
+    /// Close a file stream buffer.
+    /// </summary>
+    /// <param name="info">The file info record of the file</param>
+    /// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
+    /// <returns><c>true</c> if the closing operation could be initiated, <c>false</c> otherwise.</returns>
+    /// <remarks>
+    /// True does not signal that the file will eventually be successfully closed, just that the process was started.
+    /// </remarks>
+    _ASYNCRTIMP bool __cdecl _close_fsb_nolock(_In_ concurrency::streams::details::_file_info** info,
+                                               _In_ concurrency::streams::details::_filestream_callback* callback);
+    _ASYNCRTIMP bool __cdecl _close_fsb(_In_ concurrency::streams::details::_file_info** info,
+                                        _In_ concurrency::streams::details::_filestream_callback* callback);
+
+    /// <summary>
+    /// Write data from a buffer into the file stream.
+    /// </summary>
+    /// <param name="info">The file info record of the file</param>
+    /// <param name="callback">A pointer to the callback interface to invoke when the write request is
+    /// completed.</param> <param name="ptr">A pointer to a buffer where the data should be placed</param> <param
+    /// name="count">The size (in characters) of the buffer</param> <returns>0 if the read request is still outstanding,
+    /// -1 if the request failed, otherwise the size of the data read into the buffer</returns>
+    _ASYNCRTIMP size_t __cdecl _putn_fsb(_In_ concurrency::streams::details::_file_info* info,
+                                         _In_ concurrency::streams::details::_filestream_callback* callback,
+                                         const void* ptr,
+                                         size_t count,
+                                         size_t char_size);
+
+    /// <summary>
+    /// Read data from a file stream into a buffer
+    /// </summary>
+    /// <param name="info">The file info record of the file</param>
+    /// <param name="callback">A pointer to the callback interface to invoke when the write request is
+    /// completed.</param> <param name="ptr">A pointer to a buffer where the data should be placed</param> <param
+    /// name="count">The size (in characters) of the buffer</param> <returns>0 if the read request is still outstanding,
+    /// -1 if the request failed, otherwise the size of the data read into the buffer</returns>
+    _ASYNCRTIMP size_t __cdecl _getn_fsb(_In_ concurrency::streams::details::_file_info* info,
+                                         _In_ concurrency::streams::details::_filestream_callback* callback,
+                                         _Out_writes_(count) void* ptr,
+                                         _In_ size_t count,
+                                         size_t char_size);
+
+    /// <summary>
+    /// Flush all buffered data to the underlying file.
+    /// </summary>
+    /// <param name="info">The file info record of the file</param>
+    /// <param name="callback">A pointer to the callback interface to invoke when the write request is
+    /// completed.</param> <returns><c>true</c> if the request was initiated</returns>
+    _ASYNCRTIMP bool __cdecl _sync_fsb(_In_ concurrency::streams::details::_file_info* info,
+                                       _In_ concurrency::streams::details::_filestream_callback* callback);
+
+    /// <summary>
+    /// Get the size of the underlying file.
+    /// </summary>
+    /// <param name="info">The file info record of the file</param>
+    /// <returns>The file size</returns>
+    _ASYNCRTIMP utility::size64_t __cdecl _get_size(_In_ concurrency::streams::details::_file_info* info,
+                                                    size_t char_size);
+
+    /// <summary>
+    /// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
+    /// </summary>
+    /// <param name="info">The file info record of the file</param>
+    /// <param name="pos">The new position (offset from the start) in the file stream</param>
+    /// <returns><c>true</c> if the request was initiated</returns>
+    _ASYNCRTIMP size_t __cdecl _seekrdpos_fsb(_In_ concurrency::streams::details::_file_info* info,
+                                              size_t pos,
+                                              size_t char_size);
+
+    /// <summary>
+    /// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
+    /// </summary>
+    /// <param name="info">The file info record of the file</param>
+    /// <param name="pos">The new position (offset from the start) in the file stream</param>
+    /// <returns><c>true</c> if the request was initiated</returns>
+    _ASYNCRTIMP size_t __cdecl _seekrdtoend_fsb(_In_ concurrency::streams::details::_file_info* info,
+                                                int64_t offset,
+                                                size_t char_size);
+
+    /// <summary>
+    /// Adjust the internal buffers and pointers when the application seeks to a new write location in the stream.
+    /// </summary>
+    /// <param name="info">The file info record of the file</param>
+    /// <param name="pos">The new position (offset from the start) in the file stream</param>
+    /// <returns><c>true</c> if the request was initiated</returns>
+    _ASYNCRTIMP size_t __cdecl _seekwrpos_fsb(_In_ concurrency::streams::details::_file_info* info,
+                                              size_t pos,
+                                              size_t char_size);
+}
diff --git a/Release/include/cpprest/details/http_constants.dat b/Release/include/cpprest/details/http_constants.dat
new file mode 100644 (file)
index 0000000..c3b1a53
--- /dev/null
@@ -0,0 +1,198 @@
+#ifdef _METHODS\r
+DAT(GET,              _XPLATSTR("GET"))\r
+DAT(POST,             _XPLATSTR("POST"))\r
+DAT(PUT,              _XPLATSTR("PUT"))\r
+DAT(DEL,              _XPLATSTR("DELETE"))\r
+DAT(HEAD,             _XPLATSTR("HEAD"))\r
+DAT(OPTIONS,          _XPLATSTR("OPTIONS"))\r
+DAT(TRCE,             _XPLATSTR("TRACE"))\r
+DAT(CONNECT,          _XPLATSTR("CONNECT"))\r
+DAT(MERGE,            _XPLATSTR("MERGE"))\r
+DAT(PATCH,            _XPLATSTR("PATCH"))\r
+#endif\r
+\r
+#ifdef _PHRASES\r
+DAT(Continue,              100, _XPLATSTR("Continue"))\r
+DAT(SwitchingProtocols,    101, _XPLATSTR("Switching Protocols"))\r
+DAT(OK,                    200, _XPLATSTR("OK"))\r
+DAT(Created,               201, _XPLATSTR("Created"))\r
+DAT(Accepted,              202, _XPLATSTR("Accepted"))\r
+DAT(NonAuthInfo,           203, _XPLATSTR("Non-Authoritative Information"))\r
+DAT(NoContent,             204, _XPLATSTR("No Content"))\r
+DAT(ResetContent,          205, _XPLATSTR("Reset Content"))\r
+DAT(PartialContent,        206, _XPLATSTR("Partial Content"))\r
+DAT(MultiStatus,           207, _XPLATSTR("Multi-Status"))\r
+DAT(AlreadyReported,       208, _XPLATSTR("Already Reported"))\r
+DAT(IMUsed,                226, _XPLATSTR("IM Used"))\r
+DAT(MultipleChoices,       300, _XPLATSTR("Multiple Choices"))\r
+DAT(MovedPermanently,      301, _XPLATSTR("Moved Permanently"))\r
+DAT(Found,                 302, _XPLATSTR("Found"))\r
+DAT(SeeOther,              303, _XPLATSTR("See Other"))\r
+DAT(NotModified,           304, _XPLATSTR("Not Modified"))\r
+DAT(UseProxy,              305, _XPLATSTR("Use Proxy"))\r
+DAT(TemporaryRedirect,     307, _XPLATSTR("Temporary Redirect"))\r
+DAT(PermanentRedirect,     308, _XPLATSTR("Permanent Redirect"))\r
+DAT(BadRequest,            400, _XPLATSTR("Bad Request"))\r
+DAT(Unauthorized,          401, _XPLATSTR("Unauthorized"))\r
+DAT(PaymentRequired,       402, _XPLATSTR("Payment Required"))\r
+DAT(Forbidden,             403, _XPLATSTR("Forbidden"))\r
+DAT(NotFound,              404, _XPLATSTR("Not Found"))\r
+DAT(MethodNotAllowed,      405, _XPLATSTR("Method Not Allowed"))\r
+DAT(NotAcceptable,         406, _XPLATSTR("Not Acceptable"))\r
+DAT(ProxyAuthRequired,     407, _XPLATSTR("Proxy Authentication Required"))\r
+DAT(RequestTimeout,        408, _XPLATSTR("Request Time-out"))\r
+DAT(Conflict,              409, _XPLATSTR("Conflict"))\r
+DAT(Gone,                  410, _XPLATSTR("Gone"))\r
+DAT(LengthRequired,        411, _XPLATSTR("Length Required"))\r
+DAT(PreconditionFailed,    412, _XPLATSTR("Precondition Failed"))\r
+DAT(RequestEntityTooLarge, 413, _XPLATSTR("Request Entity Too Large"))\r
+DAT(RequestUriTooLarge,    414, _XPLATSTR("Request Uri Too Large"))\r
+DAT(UnsupportedMediaType,  415, _XPLATSTR("Unsupported Media Type"))\r
+DAT(RangeNotSatisfiable,   416, _XPLATSTR("Requested range not satisfiable"))\r
+DAT(ExpectationFailed,     417, _XPLATSTR("Expectation Failed"))\r
+DAT(MisdirectedRequest,    421, _XPLATSTR("Misdirected Request"))\r
+DAT(UnprocessableEntity,   422, _XPLATSTR("Unprocessable Entity"))\r
+DAT(Locked,                423, _XPLATSTR("Locked"))\r
+DAT(FailedDependency,      424, _XPLATSTR("Failed Dependency"))\r
+DAT(UpgradeRequired,       426, _XPLATSTR("Upgrade Required"))\r
+DAT(PreconditionRequired,  428, _XPLATSTR("Precondition Required"))\r
+DAT(TooManyRequests,       429, _XPLATSTR("Too Many Requests"))\r
+DAT(RequestHeaderFieldsTooLarge,  431, _XPLATSTR("Request Header Fields Too Large"))\r
+DAT(UnavailableForLegalReasons,   451, _XPLATSTR("Unavailable For Legal Reasons"))\r
+DAT(InternalError,         500, _XPLATSTR("Internal Error"))\r
+DAT(NotImplemented,        501, _XPLATSTR("Not Implemented"))\r
+DAT(BadGateway,            502, _XPLATSTR("Bad Gateway"))\r
+DAT(ServiceUnavailable,    503, _XPLATSTR("Service Unavailable"))\r
+DAT(GatewayTimeout,        504, _XPLATSTR("Gateway Time-out"))\r
+DAT(HttpVersionNotSupported, 505, _XPLATSTR("HTTP Version not supported"))\r
+DAT(VariantAlsoNegotiates, 506, _XPLATSTR("Variant Also Negotiates"))\r
+DAT(InsufficientStorage,   507, _XPLATSTR("Insufficient Storage"))\r
+DAT(LoopDetected,          508, _XPLATSTR("Loop Detected"))\r
+DAT(NotExtended,           510, _XPLATSTR("Not Extended"))\r
+DAT(NetworkAuthenticationRequired, 511, _XPLATSTR("Network Authentication Required"))\r
+#endif // _PHRASES\r
+\r
+#ifdef _HEADER_NAMES\r
+DAT(accept,                 "Accept")\r
+DAT(accept_charset,         "Accept-Charset")\r
+DAT(accept_encoding,        "Accept-Encoding")\r
+DAT(accept_language,        "Accept-Language")\r
+DAT(accept_ranges,          "Accept-Ranges")\r
+DAT(access_control_allow_origin,  "Access-Control-Allow-Origin")\r
+DAT(age,                    "Age")\r
+DAT(allow,                  "Allow")\r
+DAT(authorization,          "Authorization")\r
+DAT(cache_control,          "Cache-Control")\r
+DAT(connection,             "Connection")\r
+DAT(content_encoding,       "Content-Encoding")\r
+DAT(content_language,       "Content-Language")\r
+DAT(content_length,         "Content-Length")\r
+DAT(content_location,       "Content-Location")\r
+DAT(content_md5,            "Content-MD5")\r
+DAT(content_range,          "Content-Range")\r
+DAT(content_type,           "Content-Type")\r
+DAT(content_disposition,    "Content-Disposition")\r
+DAT(date,                   "Date")\r
+DAT(etag,                   "ETag")\r
+DAT(expect,                 "Expect")\r
+DAT(expires,                "Expires")\r
+DAT(from,                   "From")\r
+DAT(host,                   "Host")\r
+DAT(if_match,               "If-Match")\r
+DAT(if_modified_since,      "If-Modified-Since")\r
+DAT(if_none_match,          "If-None-Match")\r
+DAT(if_range,               "If-Range")\r
+DAT(if_unmodified_since,    "If-Unmodified-Since")\r
+DAT(last_modified,          "Last-Modified")\r
+DAT(location,               "Location")\r
+DAT(max_forwards,           "Max-Forwards")\r
+DAT(pragma,                 "Pragma")\r
+DAT(proxy_authenticate,     "Proxy-Authenticate")\r
+DAT(proxy_authorization,    "Proxy-Authorization")\r
+DAT(range,                  "Range")\r
+DAT(referer,                "Referer")\r
+DAT(retry_after,            "Retry-After")\r
+DAT(server,                 "Server")\r
+DAT(te,                     "TE")\r
+DAT(trailer,                "Trailer")\r
+DAT(transfer_encoding,      "Transfer-Encoding")\r
+DAT(upgrade,                "Upgrade")\r
+DAT(user_agent,             "User-Agent")\r
+DAT(vary,                   "Vary")\r
+DAT(via,                    "Via")\r
+DAT(warning,                "Warning")\r
+DAT(www_authenticate,       "WWW-Authenticate")\r
+#endif // _HEADER_NAMES\r
+\r
+#ifdef _MIME_TYPES\r
+DAT(application_atom_xml,                  "application/atom+xml")\r
+DAT(application_http,                      "application/http")\r
+DAT(application_javascript,                "application/javascript")\r
+DAT(application_json,                      "application/json")\r
+DAT(application_xjson,                     "application/x-json")\r
+DAT(application_octetstream,               "application/octet-stream")\r
+DAT(application_x_www_form_urlencoded,     "application/x-www-form-urlencoded")\r
+DAT(multipart_form_data,                   "multipart/form-data")\r
+DAT(boundary,                              "boundary")\r
+DAT(form_data,                             "form-data")\r
+DAT(application_xjavascript,               "application/x-javascript")\r
+DAT(application_xml,                       "application/xml")\r
+DAT(message_http,                          "message/http")\r
+DAT(text,                                  "text")\r
+DAT(text_javascript,                       "text/javascript")\r
+DAT(text_json,                             "text/json")\r
+DAT(text_plain,                            "text/plain")\r
+DAT(text_plain_utf16,                      "text/plain; charset=utf-16")\r
+DAT(text_plain_utf16le,                    "text/plain; charset=utf-16le")\r
+DAT(text_plain_utf8,                       "text/plain; charset=utf-8")\r
+DAT(text_xjavascript,                      "text/x-javascript")\r
+DAT(text_xjson,                            "text/x-json")\r
+#endif // _MIME_TYPES\r
+\r
+#ifdef _CHARSET_TYPES\r
+DAT(ascii,                          "ascii")\r
+DAT(usascii,                        "us-ascii")\r
+DAT(latin1,                         "iso-8859-1")\r
+DAT(utf8,                           "utf-8")\r
+DAT(utf16,                          "utf-16")\r
+DAT(utf16le,                        "utf-16le")\r
+DAT(utf16be,                        "utf-16be")\r
+#endif // _CHARSET_TYPES\r
+\r
+#ifdef _OAUTH1_METHODS\r
+DAT(hmac_sha1,                      _XPLATSTR("HMAC-SHA1"))\r
+DAT(plaintext,                      _XPLATSTR("PLAINTEXT"))\r
+#endif // _OAUTH1_METHODS\r
+\r
+#ifdef _OAUTH1_STRINGS\r
+DAT(callback,                       "oauth_callback")\r
+DAT(callback_confirmed,             "oauth_callback_confirmed")\r
+DAT(consumer_key,                   "oauth_consumer_key")\r
+DAT(nonce,                          "oauth_nonce")\r
+DAT(realm,                          "realm") // NOTE: No "oauth_" prefix.\r
+DAT(signature,                      "oauth_signature")\r
+DAT(signature_method,               "oauth_signature_method")\r
+DAT(timestamp,                      "oauth_timestamp")\r
+DAT(token,                          "oauth_token")\r
+DAT(token_secret,                   "oauth_token_secret")\r
+DAT(verifier,                       "oauth_verifier")\r
+DAT(version,                        "oauth_version")\r
+#endif // _OAUTH1_STRINGS\r
+\r
+#ifdef _OAUTH2_STRINGS\r
+DAT(access_token,                   "access_token")\r
+DAT(authorization_code,             "authorization_code")\r
+DAT(bearer,                         "bearer")\r
+DAT(client_id,                      "client_id")\r
+DAT(client_secret,                  "client_secret")\r
+DAT(code,                           "code")\r
+DAT(expires_in,                     "expires_in")\r
+DAT(grant_type,                     "grant_type")\r
+DAT(redirect_uri,                   "redirect_uri")\r
+DAT(refresh_token,                  "refresh_token")\r
+DAT(response_type,                  "response_type")\r
+DAT(scope,                          "scope")\r
+DAT(state,                          "state")\r
+DAT(token,                          "token")\r
+DAT(token_type,                     "token_type")\r
+#endif // _OAUTH2_STRINGS\r
diff --git a/Release/include/cpprest/details/http_helpers.h b/Release/include/cpprest/details/http_helpers.h
new file mode 100644 (file)
index 0000000..9bed095
--- /dev/null
@@ -0,0 +1,48 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Implementation Details of the http.h layer of messaging
+ *
+ * Functions and types for interoperating with http.h from modern C++
+ *   This file includes windows definitions and should not be included in a public header
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "cpprest/details/basic_types.h"
+#include "cpprest/http_msg.h"
+
+namespace web
+{
+namespace http
+{
+namespace details
+{
+namespace chunked_encoding
+{
+// Transfer-Encoding: chunked support
+static const size_t additional_encoding_space = 12;
+static const size_t data_offset = additional_encoding_space - 2;
+
+// Add the data necessary for properly sending data with transfer-encoding: chunked.
+//
+// There are up to 12 additional bytes needed for each chunk:
+//
+// The last chunk requires 5 bytes, and is fixed.
+// All other chunks require up to 8 bytes for the length, and four for the two CRLF
+// delimiters.
+//
+_ASYNCRTIMP size_t __cdecl add_chunked_delimiters(_Out_writes_(buffer_size) uint8_t* data,
+                                                  _In_ size_t buffer_size,
+                                                  size_t bytes_read);
+} // namespace chunked_encoding
+
+} // namespace details
+} // namespace http
+} // namespace web
diff --git a/Release/include/cpprest/details/http_server.h b/Release/include/cpprest/details/http_server.h
new file mode 100644 (file)
index 0000000..37a82ff
--- /dev/null
@@ -0,0 +1,72 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: interface to implement HTTP server to service http_listeners.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+#error "Error: http server APIs are not supported in XP"
+#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA
+
+#include "cpprest/http_listener.h"
+
+namespace web
+{
+namespace http
+{
+namespace experimental
+{
+namespace details
+{
+/// <summary>
+/// Interface http listeners interact with for receiving and responding to http requests.
+/// </summary>
+class http_server
+{
+public:
+    /// <summary>
+    /// Release any held resources.
+    /// </summary>
+    virtual ~http_server() {};
+
+    /// <summary>
+    /// Start listening for incoming requests.
+    /// </summary>
+    virtual pplx::task<void> start() = 0;
+
+    /// <summary>
+    /// Registers an http listener.
+    /// </summary>
+    virtual pplx::task<void> register_listener(
+        _In_ web::http::experimental::listener::details::http_listener_impl* pListener) = 0;
+
+    /// <summary>
+    /// Unregisters an http listener.
+    /// </summary>
+    virtual pplx::task<void> unregister_listener(
+        _In_ web::http::experimental::listener::details::http_listener_impl* pListener) = 0;
+
+    /// <summary>
+    /// Stop processing and listening for incoming requests.
+    /// </summary>
+    virtual pplx::task<void> stop() = 0;
+
+    /// <summary>
+    /// Asynchronously sends the specified http response.
+    /// </summary>
+    /// <param name="response">The http_response to send.</param>
+    /// <returns>A operation which is completed once the response has been sent.</returns>
+    virtual pplx::task<void> respond(http::http_response response) = 0;
+};
+
+} // namespace details
+} // namespace experimental
+} // namespace http
+} // namespace web
diff --git a/Release/include/cpprest/details/http_server_api.h b/Release/include/cpprest/details/http_server_api.h
new file mode 100644 (file)
index 0000000..db18257
--- /dev/null
@@ -0,0 +1,93 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: exposes the entry points to the http server transport apis.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+#error "Error: http server APIs are not supported in XP"
+#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA
+
+#include "cpprest/http_listener.h"
+#include <memory>
+
+namespace web
+{
+namespace http
+{
+namespace experimental
+{
+namespace details
+{
+class http_server;
+
+/// <summary>
+/// Singleton class used to register for http requests and send responses.
+///
+/// The lifetime is tied to http listener registration. When the first listener registers an instance is created
+/// and when the last one unregisters the receiver stops and is destroyed. It can be started back up again if
+/// listeners are again registered.
+/// </summary>
+class http_server_api
+{
+public:
+    /// <summary>
+    /// Returns whether or not any listeners are registered.
+    /// </summary>
+    static bool __cdecl has_listener();
+
+    /// <summary>
+    /// Registers a HTTP server API.
+    /// </summary>
+    static void __cdecl register_server_api(std::unique_ptr<http_server> server_api);
+
+    /// <summary>
+    /// Clears the http server API.
+    /// </summary>
+    static void __cdecl unregister_server_api();
+
+    /// <summary>
+    /// Registers a listener for HTTP requests and starts receiving.
+    /// </summary>
+    static pplx::task<void> __cdecl register_listener(
+        _In_ web::http::experimental::listener::details::http_listener_impl* pListener);
+
+    /// <summary>
+    /// Unregisters the given listener and stops listening for HTTP requests.
+    /// </summary>
+    static pplx::task<void> __cdecl unregister_listener(
+        _In_ web::http::experimental::listener::details::http_listener_impl* pListener);
+
+    /// <summary>
+    /// Gets static HTTP server API. Could be null if no registered listeners.
+    /// </summary>
+    static http_server* __cdecl server_api();
+
+private:
+    /// Used to lock access to the server api registration
+    static pplx::extensibility::critical_section_t s_lock;
+
+    /// Registers a server API set -- this assumes the lock has already been taken
+    static void unsafe_register_server_api(std::unique_ptr<http_server> server_api);
+
+    // Static instance of the HTTP server API.
+    static std::unique_ptr<http_server> s_server_api;
+
+    /// Number of registered listeners;
+    static pplx::details::atomic_long s_registrations;
+
+    // Static only class. No creation.
+    http_server_api();
+};
+
+} // namespace details
+} // namespace experimental
+} // namespace http
+} // namespace web
diff --git a/Release/include/cpprest/details/nosal.h b/Release/include/cpprest/details/nosal.h
new file mode 100644 (file)
index 0000000..91a2dd8
--- /dev/null
@@ -0,0 +1,77 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ ***/
+
+#pragma once
+// selected MS SAL annotations
+
+#ifdef _In_
+#undef _In_
+#endif
+#define _In_
+
+#ifdef _Inout_
+#undef _Inout_
+#endif
+#define _Inout_
+
+#ifdef _Out_
+#undef _Out_
+#endif
+#define _Out_
+
+#ifdef _In_z_
+#undef _In_z_
+#endif
+#define _In_z_
+
+#ifdef _Out_z_
+#undef _Out_z_
+#endif
+#define _Out_z_
+
+#ifdef _Inout_z_
+#undef _Inout_z_
+#endif
+#define _Inout_z_
+
+#ifdef _In_opt_
+#undef _In_opt_
+#endif
+#define _In_opt_
+
+#ifdef _Out_opt_
+#undef _Out_opt_
+#endif
+#define _Out_opt_
+
+#ifdef _Inout_opt_
+#undef _Inout_opt_
+#endif
+#define _Inout_opt_
+
+#ifdef _Out_writes_
+#undef _Out_writes_
+#endif
+#define _Out_writes_(x)
+
+#ifdef _Out_writes_opt_
+#undef _Out_writes_opt_
+#endif
+#define _Out_writes_opt_(x)
+
+#ifdef _In_reads_
+#undef _In_reads_
+#endif
+#define _In_reads_(x)
+
+#ifdef _Inout_updates_bytes_
+#undef _Inout_updates_bytes_
+#endif
+#define _Inout_updates_bytes_(x)
diff --git a/Release/include/cpprest/details/resource.h b/Release/include/cpprest/details/resource.h
new file mode 100644 (file)
index 0000000..2d61283
--- /dev/null
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Resource.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Release/include/cpprest/details/web_utilities.h b/Release/include/cpprest/details/web_utilities.h
new file mode 100644 (file)
index 0000000..853d761
--- /dev/null
@@ -0,0 +1,225 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * utility classes used by the different web:: clients
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/uri.h"
+
+namespace web
+{
+namespace details
+{
+class zero_memory_deleter
+{
+public:
+    _ASYNCRTIMP void operator()(::utility::string_t* data) const;
+};
+typedef std::unique_ptr<::utility::string_t, zero_memory_deleter> plaintext_string;
+
+#ifdef _WIN32
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#ifdef __cplusplus_winrt
+class winrt_encryption
+{
+public:
+    winrt_encryption() = default;
+    _ASYNCRTIMP winrt_encryption(const std::wstring& data);
+    _ASYNCRTIMP plaintext_string decrypt() const;
+
+private:
+    ::pplx::task<Windows::Storage::Streams::IBuffer ^> m_buffer;
+};
+#else  // ^^^ __cplusplus_winrt ^^^ // vvv !__cplusplus_winrt vvv
+class win32_encryption
+{
+public:
+    win32_encryption() = default;
+    _ASYNCRTIMP win32_encryption(const std::wstring& data);
+    _ASYNCRTIMP ~win32_encryption();
+    _ASYNCRTIMP plaintext_string decrypt() const;
+
+private:
+    std::vector<char> m_buffer;
+    size_t m_numCharacters;
+};
+#endif // __cplusplus_winrt
+#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#endif // _WIN32
+} // namespace details
+
+/// <summary>
+/// Represents a set of user credentials (user name and password) to be used
+/// for authentication.
+/// </summary>
+class credentials
+{
+public:
+    /// <summary>
+    /// Constructs an empty set of credentials without a user name or password.
+    /// </summary>
+    credentials() {}
+
+    /// <summary>
+    /// Constructs credentials from given user name and password.
+    /// </summary>
+    /// <param name="username">User name as a string.</param>
+    /// <param name="password">Password as a string.</param>
+    credentials(utility::string_t username, const utility::string_t& password)
+        : m_username(std::move(username)), m_password(password)
+    {
+    }
+
+    /// <summary>
+    /// The user name associated with the credentials.
+    /// </summary>
+    /// <returns>A string containing the user name.</returns>
+    const utility::string_t& username() const { return m_username; }
+
+    /// <summary>
+    /// The password for the user name associated with the credentials.
+    /// </summary>
+    /// <returns>A string containing the password.</returns>
+    CASABLANCA_DEPRECATED(
+        "This API is deprecated for security reasons to avoid unnecessary password copies stored in plaintext.")
+    utility::string_t password() const
+    {
+#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+        return utility::string_t(*m_password.decrypt());
+#else
+        return m_password;
+#endif
+    }
+
+    /// <summary>
+    /// Checks if credentials have been set
+    /// </summary>
+    /// <returns><c>true</c> if user name and password is set, <c>false</c> otherwise.</returns>
+    bool is_set() const { return !m_username.empty(); }
+
+    details::plaintext_string _internal_decrypt() const
+    {
+        // Encryption APIs not supported on XP
+#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+        return m_password.decrypt();
+#else
+        return details::plaintext_string(new ::utility::string_t(m_password));
+#endif
+    }
+
+private:
+    ::utility::string_t m_username;
+
+#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#if defined(__cplusplus_winrt)
+    details::winrt_encryption m_password;
+#else
+    details::win32_encryption m_password;
+#endif
+#else
+    ::utility::string_t m_password;
+#endif
+};
+
+/// <summary>
+/// web_proxy represents the concept of the web proxy, which can be auto-discovered,
+/// disabled, or specified explicitly by the user.
+/// </summary>
+class web_proxy
+{
+    enum web_proxy_mode_internal
+    {
+        use_default_,
+        use_auto_discovery_,
+        disabled_,
+        user_provided_
+    };
+
+public:
+    enum web_proxy_mode
+    {
+        use_default = use_default_,
+        use_auto_discovery = use_auto_discovery_,
+        disabled = disabled_
+    };
+
+    /// <summary>
+    /// Constructs a proxy with the default settings.
+    /// </summary>
+    web_proxy() : m_address(), m_mode(use_default_) {}
+
+    /// <summary>
+    /// Creates a proxy with specified mode.
+    /// </summary>
+    /// <param name="mode">Mode to use.</param>
+    web_proxy(web_proxy_mode mode) : m_address(), m_mode(static_cast<web_proxy_mode_internal>(mode)) {}
+
+    /// <summary>
+    /// Creates a proxy explicitly with provided address.
+    /// </summary>
+    /// <param name="address">Proxy URI to use.</param>
+    web_proxy(uri address) : m_address(address), m_mode(user_provided_) {}
+
+    /// <summary>
+    /// Gets this proxy's URI address. Returns an empty URI if not explicitly set by user.
+    /// </summary>
+    /// <returns>A reference to this proxy's URI.</returns>
+    const uri& address() const { return m_address; }
+
+    /// <summary>
+    /// Gets the credentials used for authentication with this proxy.
+    /// </summary>
+    /// <returns>Credentials to for this proxy.</returns>
+    const web::credentials& credentials() const { return m_credentials; }
+
+    /// <summary>
+    /// Sets the credentials to use for authentication with this proxy.
+    /// </summary>
+    /// <param name="cred">Credentials to use for this proxy.</param>
+    void set_credentials(web::credentials cred)
+    {
+        if (m_mode == disabled_)
+        {
+            throw std::invalid_argument("Cannot attach credentials to a disabled proxy");
+        }
+        m_credentials = std::move(cred);
+    }
+
+    /// <summary>
+    /// Checks if this proxy was constructed with default settings.
+    /// </summary>
+    /// <returns>True if default, false otherwise.</param>
+    bool is_default() const { return m_mode == use_default_; }
+
+    /// <summary>
+    /// Checks if using a proxy is disabled.
+    /// </summary>
+    /// <returns>True if disabled, false otherwise.</returns>
+    bool is_disabled() const { return m_mode == disabled_; }
+
+    /// <summary>
+    /// Checks if the auto discovery protocol, WPAD, is to be used.
+    /// </summary>
+    /// <returns>True if auto discovery enabled, false otherwise.</returns>
+    bool is_auto_discovery() const { return m_mode == use_auto_discovery_; }
+
+    /// <summary>
+    /// Checks if a proxy address is explicitly specified by the user.
+    /// </summary>
+    /// <returns>True if a proxy address was explicitly specified, false otherwise.</returns>
+    bool is_specified() const { return m_mode == user_provided_; }
+
+private:
+    web::uri m_address;
+    web_proxy_mode_internal m_mode;
+    web::credentials m_credentials;
+};
+
+} // namespace web
diff --git a/Release/include/cpprest/filestream.h b/Release/include/cpprest/filestream.h
new file mode 100644 (file)
index 0000000..1e4a0f2
--- /dev/null
@@ -0,0 +1,1094 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Asynchronous File streams
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_FILE_STREAMS_H
+#define CASA_FILE_STREAMS_H
+
+#include "cpprest/astreambuf.h"
+#include "cpprest/details/fileio.h"
+#include "cpprest/streams.h"
+#include <assert.h>
+
+#ifndef _CONCRT_H
+#ifndef _LWRCASE_CNCRRNCY
+#define _LWRCASE_CNCRRNCY
+// Note to reader: we're using lower-case namespace names everywhere, but the 'Concurrency' namespace
+// is capitalized for historical reasons. The alias let's us pretend that style issue doesn't exist.
+namespace Concurrency
+{
+}
+namespace concurrency = Concurrency;
+#endif
+#endif
+
+namespace Concurrency
+{
+namespace streams
+{
+// Forward declarations
+template<typename _CharType>
+class file_buffer;
+
+namespace details
+{
+// This operation queue is NOT thread safe
+class async_operation_queue
+{
+    pplx::task<void> m_lastOperation;
+
+public:
+    async_operation_queue() { m_lastOperation = pplx::task_from_result(); }
+
+    // It only accepts functors that take no argument and return pplx::task<T>
+    // This function may execute op inline, thus it could throw immediately
+    template<typename Func>
+    auto enqueue_operation(Func&& op) -> decltype(op())
+    {
+        decltype(op()) res; // res is task<T> , which always has default constructor
+        if (m_lastOperation.is_done())
+        {
+            res = op(); // Exceptions are expected to be thrown directly without catching
+            if (res.is_done()) return res;
+        }
+        else
+        {
+            res = m_lastOperation.then([=] {
+                return op(); // It will cause task unwrapping
+            });
+        }
+        m_lastOperation = res.then([&](decltype(op())) {
+            // This empty task is necessary for keeping the rest of the operations on the list running
+            // even when the previous operation gets error.
+            // Don't observe exception here.
+        });
+        return res;
+    }
+
+    void wait() const { m_lastOperation.wait(); }
+};
+
+/// <summary>
+/// Private stream buffer implementation for file streams.
+/// The class itself should not be used in application code, it is used by the stream definitions farther down in the
+/// header file.
+/// </summary>
+template<typename _CharType>
+class basic_file_buffer : public details::streambuf_state_manager<_CharType>
+{
+public:
+    typedef typename basic_streambuf<_CharType>::traits traits;
+    typedef typename basic_streambuf<_CharType>::int_type int_type;
+    typedef typename basic_streambuf<_CharType>::pos_type pos_type;
+    typedef typename basic_streambuf<_CharType>::off_type off_type;
+
+    virtual ~basic_file_buffer()
+    {
+        if (this->can_read())
+        {
+            this->_close_read().wait();
+        }
+
+        if (this->can_write())
+        {
+            this->_close_write().wait();
+        }
+    }
+
+protected:
+    /// <summary>
+    /// <c>can_seek</c> is used to determine whether a stream buffer supports seeking.
+    /// </summary>
+    virtual bool can_seek() const { return this->is_open(); }
+
+    /// <summary>
+    /// <c>has_size<c/> is used to determine whether a stream buffer supports size().
+    /// </summary>
+    virtual bool has_size() const { return this->is_open(); }
+
+    virtual utility::size64_t size() const
+    {
+        if (!this->is_open()) return 0;
+        return _get_size(m_info, sizeof(_CharType));
+    }
+
+    /// <summary>
+    /// Gets the stream buffer size, if one has been set.
+    /// </summary>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
+    virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const
+    {
+        if (direction == std::ios_base::in)
+            return m_info->m_buffer_size;
+        else
+            return 0;
+    }
+
+    /// <summary>
+    /// Sets the stream buffer implementation to buffer or not buffer.
+    /// </summary>
+    /// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
+    /// will not have
+    ///          any effect on what is returned by subsequent calls to buffer_size().</remarks>
+    virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in)
+    {
+        if (direction == std::ios_base::out) return;
+
+        m_info->m_buffer_size = size;
+
+        if (size == 0 && m_info->m_buffer != nullptr)
+        {
+            delete m_info->m_buffer;
+            m_info->m_buffer = nullptr;
+        }
+    }
+
+    /// <summary>
+    /// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
+    /// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
+    /// incurring the overhead of using tasks.
+    /// </summary>
+    virtual size_t in_avail() const
+    {
+        pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
+
+        return _in_avail_unprot();
+    }
+
+    size_t _in_avail_unprot() const
+    {
+        if (!this->is_open()) return 0;
+
+        if (m_info->m_buffer == nullptr || m_info->m_buffill == 0) return 0;
+        if (m_info->m_bufoff > m_info->m_rdpos || (m_info->m_bufoff + m_info->m_buffill) < m_info->m_rdpos) return 0;
+
+        msl::safeint3::SafeInt<size_t> rdpos(m_info->m_rdpos);
+        msl::safeint3::SafeInt<size_t> buffill(m_info->m_buffill);
+        msl::safeint3::SafeInt<size_t> bufpos = rdpos - m_info->m_bufoff;
+
+        return buffill - bufpos;
+    }
+
+    _file_info* _close_stream()
+    {
+        // indicate that we are no longer open
+        auto fileInfo = m_info;
+        m_info = nullptr;
+        return fileInfo;
+    }
+
+    static pplx::task<void> _close_file(_In_ _file_info* fileInfo)
+    {
+        pplx::task_completion_event<void> result_tce;
+        auto callback = new _filestream_callback_close(result_tce);
+
+        if (!_close_fsb_nolock(&fileInfo, callback))
+        {
+            delete callback;
+            return pplx::task_from_result();
+        }
+        return pplx::create_task(result_tce);
+    }
+
+    // Workaround GCC compiler bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58972
+    void _invoke_parent_close_read() { streambuf_state_manager<_CharType>::_close_read(); }
+
+    pplx::task<void> _close_read()
+    {
+        return m_readOps.enqueue_operation([this] {
+            _invoke_parent_close_read();
+
+            if (this->can_write())
+            {
+                return pplx::task_from_result();
+            }
+            else
+            {
+                // Neither heads are open. Close the underlying device
+                // to indicate that we are no longer open
+                auto fileInfo = _close_stream();
+
+                return _close_file(fileInfo);
+            }
+        });
+    }
+
+    pplx::task<void> _close_write()
+    {
+        streambuf_state_manager<_CharType>::_close_write();
+        if (this->can_read())
+        {
+            // Read head is still open. Just flush the write data
+            return flush_internal();
+        }
+        else
+        {
+            // Neither heads are open. Close the underlying device
+
+            // We need to flush all writes if the file was opened for writing.
+            return flush_internal().then([=](pplx::task<void> flushTask) -> pplx::task<void> {
+                // swallow exception from flush
+                try
+                {
+                    flushTask.wait();
+                }
+                catch (...)
+                {
+                }
+
+                // indicate that we are no longer open
+                auto fileInfo = this->_close_stream();
+
+                return this->_close_file(fileInfo);
+            });
+        }
+    }
+
+    /// <summary>
+    /// Writes a single byte to an output stream.
+    /// </summary>
+    /// <param name="ch">The byte to write</param>
+    /// <returns>A <c>task</c> that holds the value of the byte written. This is EOF if the write operation
+    /// fails.</returns>
+    virtual pplx::task<int_type> _putc(_CharType ch)
+    {
+        auto result_tce = pplx::task_completion_event<size_t>();
+        auto callback = new _filestream_callback_write<size_t>(m_info, result_tce);
+
+        // Potentially we should consider deprecating this API, it is TERRIBLY inefficient.
+        std::shared_ptr<_CharType> sharedCh;
+        try
+        {
+            sharedCh = std::make_shared<_CharType>(ch);
+        }
+        catch (const std::bad_alloc&)
+        {
+            delete callback;
+            throw;
+        }
+
+        size_t written = _putn_fsb(m_info, callback, sharedCh.get(), 1, sizeof(_CharType));
+        if (written == sizeof(_CharType))
+        {
+            delete callback;
+            return pplx::task_from_result<int_type>(ch);
+        }
+
+        return pplx::create_task(result_tce).then([sharedCh](size_t) { return static_cast<int_type>(*sharedCh); });
+    }
+
+    /// <summary>
+    /// Allocates a contiguous memory block and returns it.
+    /// </summary>
+    /// <param name="count">The number of characters to allocate.</param>
+    /// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
+    /// alloc/commit.</returns>
+    _CharType* _alloc(size_t) { return nullptr; }
+
+    /// <summary>
+    /// Submits a block already allocated by the stream buffer.
+    /// </summary>
+    /// <param name="ptr">Count of characters to be committed.</param>
+    void _commit(size_t) {}
+
+    /// <summary>
+    /// Gets a pointer to the next already allocated contiguous block of data.
+    /// </summary>
+    /// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
+    /// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
+    /// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
+    /// <remarks>
+    /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
+    /// there is no block to return immediately or that the stream buffer does not support the operation.
+    /// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
+    /// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
+    /// a subsequent read will not succeed.
+    /// </remarks>
+    virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
+    {
+        ptr = nullptr;
+        count = 0;
+        return false;
+    }
+
+    /// <summary>
+    /// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
+    /// de-allocate the memory, if it so desires. Move the read position ahead by the count.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be released.</param>
+    /// <param name="count">The number of characters that were read.</param>
+    virtual void release(_Out_writes_(count) _CharType*, _In_ size_t count) { (void)(count); }
+
+    /// <summary>
+    /// Writes a number of characters to the stream.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be written.</param>
+    /// <param name="count">The number of characters to write.</param>
+    /// <returns>A <c>task</c> that holds the number of characters actually written, either 'count' or 0.</returns>
+    virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
+    {
+        auto result_tce = pplx::task_completion_event<size_t>();
+        auto callback = new _filestream_callback_write<size_t>(m_info, result_tce);
+
+        size_t written = _putn_fsb(m_info, callback, ptr, count, sizeof(_CharType));
+
+        if (written != 0 && written != size_t(-1))
+        {
+            delete callback;
+            written = written / sizeof(_CharType);
+            return pplx::task_from_result<size_t>(written);
+        }
+        return pplx::create_task(result_tce);
+    }
+
+    // Temporarily needed until the deprecated putn is removed.
+    virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count, bool copy)
+    {
+        if (copy)
+        {
+            auto sharedData = std::make_shared<std::vector<_CharType>>(ptr, ptr + count);
+            return _putn(ptr, count).then([sharedData](size_t size) { return size; });
+        }
+        else
+        {
+            return _putn(ptr, count);
+        }
+    }
+
+    /// <summary>
+    /// Reads a single byte from the stream and advance the read position.
+    /// </summary>
+    /// <returns>A <c>task</c> that holds the value of the byte read. This is EOF if the read fails.</returns>
+    virtual pplx::task<int_type> _bumpc()
+    {
+        return m_readOps.enqueue_operation([this]() -> pplx::task<int_type> {
+            if (_in_avail_unprot() > 0)
+            {
+                pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
+
+                // Check again once the lock is held.
+
+                if (_in_avail_unprot() > 0)
+                {
+                    auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
+                    _CharType ch = m_info->m_buffer[bufoff * sizeof(_CharType)];
+                    m_info->m_rdpos += 1;
+                    return pplx::task_from_result<int_type>(ch);
+                }
+            }
+
+            auto result_tce = pplx::task_completion_event<int_type>();
+            auto callback = new _filestream_callback_bumpc(m_info, result_tce);
+
+            size_t ch = _getn_fsb(m_info, callback, &callback->m_ch, 1, sizeof(_CharType));
+
+            if (ch == sizeof(_CharType))
+            {
+                pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
+                m_info->m_rdpos += 1;
+                _CharType ch1 = (_CharType)callback->m_ch;
+                delete callback;
+                return pplx::task_from_result<int_type>(ch1);
+            }
+            return pplx::create_task(result_tce);
+        });
+    }
+
+    /// <summary>
+    /// Reads a single byte from the stream and advance the read position.
+    /// </summary>
+    /// <returns>The value of the byte. EOF if the read fails. <see cref="::requires_async method" /> if an asynchronous
+    /// read is required</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
+    virtual int_type _sbumpc()
+    {
+        m_readOps.wait();
+        if (m_info->m_atend) return traits::eof();
+
+        if (_in_avail_unprot() == 0) return traits::requires_async();
+
+        pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
+
+        if (_in_avail_unprot() == 0) return traits::requires_async();
+
+        auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
+        _CharType ch = m_info->m_buffer[bufoff * sizeof(_CharType)];
+        m_info->m_rdpos += 1;
+        return (int_type)ch;
+    }
+
+    pplx::task<int_type> _getcImpl()
+    {
+        if (_in_avail_unprot() > 0)
+        {
+            pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
+
+            // Check again once the lock is held.
+
+            if (_in_avail_unprot() > 0)
+            {
+                auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
+                _CharType ch = m_info->m_buffer[bufoff * sizeof(_CharType)];
+                return pplx::task_from_result<int_type>(ch);
+            }
+        }
+
+        auto result_tce = pplx::task_completion_event<int_type>();
+        auto callback = new _filestream_callback_getc(m_info, result_tce);
+
+        size_t ch = _getn_fsb(m_info, callback, &callback->m_ch, 1, sizeof(_CharType));
+
+        if (ch == sizeof(_CharType))
+        {
+            pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
+            _CharType ch1 = (_CharType)callback->m_ch;
+            delete callback;
+            return pplx::task_from_result<int_type>(ch1);
+        }
+        return pplx::create_task(result_tce);
+    }
+
+    /// <summary>
+    /// Reads a single byte from the stream without advancing the read position.
+    /// </summary>
+    /// <returns>The value of the byte. EOF if the read fails.</returns>
+    pplx::task<int_type> _getc()
+    {
+        return m_readOps.enqueue_operation([this]() -> pplx::task<int_type> { return _getcImpl(); });
+    }
+
+    /// <summary>
+    /// Reads a single byte from the stream without advancing the read position.
+    /// </summary>
+    /// <returns>The value of the byte. EOF if the read fails. <see cref="::requires_async method" /> if an asynchronous
+    /// read is required</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
+    int_type _sgetc()
+    {
+        m_readOps.wait();
+        if (m_info->m_atend) return traits::eof();
+
+        if (_in_avail_unprot() == 0) return traits::requires_async();
+
+        pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
+
+        if (_in_avail_unprot() == 0) return traits::requires_async();
+
+        auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
+        _CharType ch = m_info->m_buffer[bufoff * sizeof(_CharType)];
+        return (int_type)ch;
+    }
+
+    /// <summary>
+    /// Advances the read position, then return the next character without advancing again.
+    /// </summary>
+    /// <returns>A <c>task</c> that holds the value of the byte, which is EOF if the read fails.</returns>
+    virtual pplx::task<int_type> _nextc()
+    {
+        return m_readOps.enqueue_operation([this]() -> pplx::task<int_type> {
+            _seekrdpos_fsb(m_info, m_info->m_rdpos + 1, sizeof(_CharType));
+            if (m_info->m_atend) return pplx::task_from_result(basic_file_buffer<_CharType>::traits::eof());
+            return this->_getcImpl();
+        });
+    }
+
+    /// <summary>
+    /// Retreats the read position, then return the current character without advancing.
+    /// </summary>
+    /// <returns>A <c>task</c> that holds the value of the byte. The value is EOF if the read fails,
+    /// <c>requires_async</c> if an asynchronous read is required</returns>
+    virtual pplx::task<int_type> _ungetc()
+    {
+        return m_readOps.enqueue_operation([this]() -> pplx::task<int_type> {
+            if (m_info->m_rdpos == 0)
+                return pplx::task_from_result<int_type>(basic_file_buffer<_CharType>::traits::eof());
+            _seekrdpos_fsb(m_info, m_info->m_rdpos - 1, sizeof(_CharType));
+            return this->_getcImpl();
+        });
+    }
+
+    /// <summary>
+    /// Reads up to a given number of characters from the stream.
+    /// </summary>
+    /// <param name="ptr">The address of the target memory area</param>
+    /// <param name="count">The maximum number of characters to read</param>
+    /// <returns>A <c>task</c> that holds the number of characters read. This number is O if the end of the stream is
+    /// reached, EOF if there is some error.</returns>
+    virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        return m_readOps.enqueue_operation([=]() -> pplx::task<size_t> {
+            if (m_info->m_atend || count == 0) return pplx::task_from_result<size_t>(0);
+
+            if (_in_avail_unprot() >= count)
+            {
+                pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
+
+                // Check again once the lock is held.
+
+                if (_in_avail_unprot() >= count)
+                {
+                    auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
+                    std::memcpy(
+                        (void*)ptr, this->m_info->m_buffer + bufoff * sizeof(_CharType), count * sizeof(_CharType));
+
+                    m_info->m_rdpos += count;
+                    return pplx::task_from_result<size_t>(count);
+                }
+            }
+
+            auto result_tce = pplx::task_completion_event<size_t>();
+            auto callback = new _filestream_callback_read(m_info, result_tce);
+
+            size_t read = _getn_fsb(m_info, callback, ptr, count, sizeof(_CharType));
+
+            if (read != 0 && read != size_t(-1))
+            {
+                delete callback;
+                pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
+                m_info->m_rdpos += read / sizeof(_CharType);
+                return pplx::task_from_result<size_t>(read / sizeof(_CharType));
+            }
+            return pplx::create_task(result_tce);
+        });
+    }
+
+    /// <summary>
+    /// Reads up to a given number of characters from the stream.
+    /// </summary>
+    /// <param name="ptr">The address of the target memory area</param>
+    /// <param name="count">The maximum number of characters to read</param>
+    /// <returns>The number of characters read. O if the end of the stream is reached or an asynchronous read is
+    /// required.</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
+    size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        m_readOps.wait();
+        if (m_info->m_atend) return 0;
+
+        if (count == 0 || in_avail() == 0) return 0;
+
+        pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
+
+        size_t available = _in_avail_unprot();
+        size_t copy = (count < available) ? count : available;
+
+        auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
+        std::memcpy((void*)ptr, this->m_info->m_buffer + bufoff * sizeof(_CharType), copy * sizeof(_CharType));
+
+        m_info->m_rdpos += copy;
+        m_info->m_atend = (copy < count);
+        return copy;
+    }
+
+    /// <summary>
+    /// Copies up to a given number of characters from the stream.
+    /// </summary>
+    /// <param name="ptr">The address of the target memory area</param>
+    /// <param name="count">The maximum number of characters to copy</param>
+    /// <returns>The number of characters copied. O if the end of the stream is reached or an asynchronous read is
+    /// required.</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
+    virtual size_t _scopy(_CharType*, size_t) { return 0; }
+
+    /// <summary>
+    /// Gets the current read or write position in the stream.
+    /// </summary>
+    /// <param name="direction">The I/O direction to seek (see remarks)</param>
+    /// <returns>The current position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the direction parameter defines whether to move the read or the write
+    ///          cursor.</remarks>
+    virtual pos_type getpos(std::ios_base::openmode mode) const
+    {
+        return const_cast<basic_file_buffer*>(this)->seekoff(0, std::ios_base::cur, mode);
+    }
+
+    /// <summary>
+    /// Seeks to the given position.
+    /// </summary>
+    /// <param name="pos">The offset from the beginning of the stream</param>
+    /// <param name="direction">The I/O direction to seek (see remarks)</param>
+    /// <returns>The position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the direction parameter defines whether to move the read or the write
+    ///          cursor.</remarks>
+    virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode)
+    {
+        if (mode == std::ios_base::in)
+        {
+            m_readOps.wait();
+            return (pos_type)_seekrdpos_fsb(m_info, size_t(pos), sizeof(_CharType));
+        }
+        else if ((m_info->m_mode & std::ios::ios_base::app) == 0)
+        {
+            return (pos_type)_seekwrpos_fsb(m_info, size_t(pos), sizeof(_CharType));
+        }
+        return (pos_type)Concurrency::streams::char_traits<_CharType>::eof();
+    }
+
+    /// <summary>
+    /// Seeks to a position given by a relative offset.
+    /// </summary>
+    /// <param name="offset">The relative position to seek to</param>
+    /// <param name="way">The starting point (beginning, end, current) for the seek.</param>
+    /// <param name="mode">The I/O direction to seek (see remarks)</param>
+    /// <returns>The position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
+    virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
+    {
+        if (mode == std::ios_base::in)
+        {
+            m_readOps.wait();
+            size_t current_pos = static_cast<size_t>(-1);
+            switch (way)
+            {
+                case std::ios_base::beg: return (pos_type)_seekrdpos_fsb(m_info, size_t(offset), sizeof(_CharType));
+                case std::ios_base::cur:
+                    return (pos_type)_seekrdpos_fsb(m_info, size_t(m_info->m_rdpos + offset), sizeof(_CharType));
+                case std::ios_base::end:
+                    current_pos = _seekrdtoend_fsb(m_info, int64_t(offset), sizeof(_CharType));
+                    if (current_pos == static_cast<size_t>(-1))
+                    {
+                        return -1;
+                    }
+                    return (pos_type)current_pos;
+                default:
+                    // Fail on invalid input (_S_ios_seekdir_end)
+                    assert(false);
+            }
+        }
+        else if ((m_info->m_mode & std::ios::ios_base::app) == 0)
+        {
+            switch (way)
+            {
+                case std::ios_base::beg: return (pos_type)_seekwrpos_fsb(m_info, size_t(offset), sizeof(_CharType));
+                case std::ios_base::cur:
+                    return (pos_type)_seekwrpos_fsb(m_info, size_t(m_info->m_wrpos + offset), sizeof(_CharType));
+                case std::ios_base::end: return (pos_type)_seekwrpos_fsb(m_info, size_t(-1), sizeof(_CharType));
+                default:
+                    // Fail on invalid input (_S_ios_seekdir_end)
+                    assert(false);
+            }
+        }
+        return (pos_type)traits::eof();
+    }
+
+    /// <summary>
+    /// For output streams, flush any internally buffered data to the underlying medium.
+    /// </summary>
+    virtual pplx::task<bool> _sync()
+    {
+        return flush_internal().then([]() { return true; });
+    }
+
+private:
+    template<typename _CharType1>
+    friend class ::concurrency::streams::file_buffer;
+
+    pplx::task<void> flush_internal()
+    {
+        pplx::task_completion_event<void> result_tce;
+        auto callback = utility::details::make_unique<_filestream_callback_write_b>(m_info, result_tce);
+
+        if (!_sync_fsb(m_info, callback.get()))
+        {
+            return pplx::task_from_exception<void>(std::runtime_error("failure to flush stream"));
+        }
+        callback.release();
+        return pplx::create_task(result_tce);
+    }
+
+    basic_file_buffer(_In_ _file_info* info) : streambuf_state_manager<_CharType>(info->m_mode), m_info(info) {}
+
+#if !defined(__cplusplus_winrt)
+    static pplx::task<std::shared_ptr<basic_streambuf<_CharType>>> open(
+        const utility::string_t& _Filename,
+        std::ios_base::openmode _Mode = std::ios_base::out,
+#ifdef _WIN32
+        int _Prot = (int)std::ios_base::_Openprot
+#else
+        int _Prot = 0                                         // unsupported on Linux, for now
+#endif
+    )
+    {
+        auto result_tce = pplx::task_completion_event<std::shared_ptr<basic_streambuf<_CharType>>>();
+        auto callback = new _filestream_callback_open(result_tce);
+        _open_fsb_str(callback, _Filename.c_str(), _Mode, _Prot);
+        return pplx::create_task(result_tce);
+    }
+
+#else
+    static pplx::task<std::shared_ptr<basic_streambuf<_CharType>>> open(
+        ::Windows::Storage::StorageFile ^ file, std::ios_base::openmode _Mode = std::ios_base::out)
+    {
+        auto result_tce = pplx::task_completion_event<std::shared_ptr<basic_streambuf<_CharType>>>();
+        auto callback = new _filestream_callback_open(result_tce);
+        _open_fsb_stf_str(callback, file, _Mode, 0);
+        return pplx::create_task(result_tce);
+    }
+#endif
+
+    class _filestream_callback_open : public details::_filestream_callback
+    {
+    public:
+        _filestream_callback_open(const pplx::task_completion_event<std::shared_ptr<basic_streambuf<_CharType>>>& op)
+            : m_op(op)
+        {
+        }
+
+        virtual void on_opened(_In_ _file_info* info)
+        {
+            m_op.set(std::shared_ptr<basic_file_buffer<_CharType>>(new basic_file_buffer<_CharType>(info)));
+            delete this;
+        }
+
+        virtual void on_error(const std::exception_ptr& e)
+        {
+            m_op.set_exception(e);
+            delete this;
+        }
+
+    private:
+        pplx::task_completion_event<std::shared_ptr<basic_streambuf<_CharType>>> m_op;
+    };
+
+    class _filestream_callback_close : public details::_filestream_callback
+    {
+    public:
+        _filestream_callback_close(const pplx::task_completion_event<void>& op) : m_op(op) {}
+
+        virtual void on_closed()
+        {
+            m_op.set();
+            delete this;
+        }
+
+        virtual void on_error(const std::exception_ptr& e)
+        {
+            m_op.set_exception(e);
+            delete this;
+        }
+
+    private:
+        pplx::task_completion_event<void> m_op;
+    };
+
+    template<typename ResultType>
+    class _filestream_callback_write : public details::_filestream_callback
+    {
+    public:
+        _filestream_callback_write(_In_ _file_info* info, const pplx::task_completion_event<ResultType>& op)
+            : m_info(info), m_op(op)
+        {
+        }
+
+        virtual void on_completed(size_t result)
+        {
+            m_op.set((ResultType)result / sizeof(_CharType));
+            delete this;
+        }
+
+        virtual void on_error(const std::exception_ptr& e)
+        {
+            m_op.set_exception(e);
+            delete this;
+        }
+
+    private:
+        _file_info* m_info;
+        pplx::task_completion_event<ResultType> m_op;
+    };
+
+    class _filestream_callback_write_b : public details::_filestream_callback
+    {
+    public:
+        _filestream_callback_write_b(_In_ _file_info* info, const pplx::task_completion_event<void>& op)
+            : m_info(info), m_op(op)
+        {
+        }
+
+        virtual void on_completed(size_t)
+        {
+            m_op.set();
+            delete this;
+        }
+
+        virtual void on_error(const std::exception_ptr& e)
+        {
+            m_op.set_exception(e);
+            delete this;
+        }
+
+    private:
+        _file_info* m_info;
+        pplx::task_completion_event<void> m_op;
+    };
+
+    class _filestream_callback_read : public details::_filestream_callback
+    {
+    public:
+        _filestream_callback_read(_In_ _file_info* info, const pplx::task_completion_event<size_t>& op)
+            : m_info(info), m_op(op)
+        {
+        }
+
+        virtual void on_completed(size_t result)
+        {
+            result = result / sizeof(_CharType);
+            m_info->m_rdpos += result;
+            m_op.set(result);
+            delete this;
+        }
+
+        virtual void on_error(const std::exception_ptr& e)
+        {
+            m_op.set_exception(e);
+            delete this;
+        }
+
+    private:
+        _file_info* m_info;
+        pplx::task_completion_event<size_t> m_op;
+    };
+
+    class _filestream_callback_bumpc : public details::_filestream_callback
+    {
+    public:
+        _filestream_callback_bumpc(_In_ _file_info* info, const pplx::task_completion_event<int_type>& op)
+            : m_ch(0), m_info(info), m_op(op)
+        {
+        }
+
+        virtual void on_completed(size_t result)
+        {
+            if (result == sizeof(_CharType))
+            {
+                m_info->m_rdpos += 1;
+                m_op.set(m_ch);
+            }
+            else
+            {
+                m_op.set(traits::eof());
+            }
+            delete this;
+        }
+
+        virtual void on_error(const std::exception_ptr& e)
+        {
+            m_op.set_exception(e);
+            delete this;
+        }
+
+        int_type m_ch;
+
+    private:
+        _file_info* m_info;
+        pplx::task_completion_event<int_type> m_op;
+    };
+
+    class _filestream_callback_getc : public details::_filestream_callback
+    {
+    public:
+        _filestream_callback_getc(_In_ _file_info* info, const pplx::task_completion_event<int_type>& op)
+            : m_ch(0), m_info(info), m_op(op)
+        {
+        }
+
+        virtual void on_completed(size_t result)
+        {
+            if (result == sizeof(_CharType))
+            {
+                m_op.set(m_ch);
+            }
+            else
+            {
+                m_op.set(traits::eof());
+            }
+            delete this;
+        }
+
+        int_type m_ch;
+
+        virtual void on_error(const std::exception_ptr& e)
+        {
+            m_op.set_exception(e);
+            delete this;
+        }
+
+    private:
+        _file_info* m_info;
+        pplx::task_completion_event<int_type> m_op;
+    };
+
+    _file_info* m_info;
+    async_operation_queue m_readOps;
+};
+
+} // namespace details
+
+/// <summary>
+/// Stream buffer for file streams.
+/// </summary>
+/// <typeparam name="_CharType">
+/// The data type of the basic element of the <c>file_buffer</c>.
+/// </typeparam>
+template<typename _CharType>
+class file_buffer
+{
+public:
+#if !defined(__cplusplus_winrt)
+    /// <summary>
+    /// Open a new stream buffer representing the given file.
+    /// </summary>
+    /// <param name="file_name">The name of the file</param>
+    /// <param name="mode">The opening mode of the file</param>
+    /// <param name="prot">The file protection mode</param>
+    /// <returns>A <c>task</c> that returns an opened stream buffer on completion.</returns>
+    static pplx::task<streambuf<_CharType>> open(const utility::string_t& file_name,
+                                                 std::ios_base::openmode mode = std::ios_base::out,
+#ifdef _WIN32
+                                                 int prot = _SH_DENYRD
+#else
+                                                 int prot = 0 // unsupported on Linux
+#endif
+    )
+    {
+        auto bfb = details::basic_file_buffer<_CharType>::open(file_name, mode, prot);
+        return bfb.then(
+            [](pplx::task<std::shared_ptr<details::basic_streambuf<_CharType>>> op) -> streambuf<_CharType> {
+                return streambuf<_CharType>(op.get());
+            });
+    }
+
+#else
+    /// <summary>
+    /// Open a new stream buffer representing the given file.
+    /// </summary>
+    /// <param name="file">The StorageFile instance</param>
+    /// <param name="mode">The opening mode of the file</param>
+    /// <param name="prot">The file protection mode</param>
+    /// <returns>A <c>task</c> that returns an opened stream buffer on completion.</returns>
+    static pplx::task<streambuf<_CharType>> open(::Windows::Storage::StorageFile ^ file,
+                                                 std::ios_base::openmode mode = std::ios_base::out)
+    {
+        auto bfb = details::basic_file_buffer<_CharType>::open(file, mode);
+        return bfb.then(
+            [](pplx::task<std::shared_ptr<details::basic_streambuf<_CharType>>> op) -> streambuf<_CharType> {
+                return streambuf<_CharType>(op.get());
+            });
+    }
+#endif
+};
+
+/// <summary>
+/// File stream class containing factory functions for file streams.
+/// </summary>
+/// <typeparam name="_CharType">
+/// The data type of the basic element of the <c>file_stream</c>.
+/// </typeparam>
+template<typename _CharType>
+class file_stream
+{
+public:
+#if !defined(__cplusplus_winrt)
+    /// <summary>
+    /// Open a new input stream representing the given file.
+    /// The file should already exist on disk, or an exception will be thrown.
+    /// </summary>
+    /// <param name="file_name">The name of the file</param>
+    /// <param name="mode">The opening mode of the file</param>
+    /// <param name="prot">The file protection mode</param>
+    /// <returns>A <c>task</c> that returns an opened input stream on completion.</returns>
+    static pplx::task<streams::basic_istream<_CharType>> open_istream(const utility::string_t& file_name,
+                                                                      std::ios_base::openmode mode = std::ios_base::in,
+#ifdef _WIN32
+                                                                      int prot = (int)std::ios_base::_Openprot
+#else
+                                                                      int prot = 0
+#endif
+    )
+    {
+        mode |= std::ios_base::in;
+        return streams::file_buffer<_CharType>::open(file_name, mode, prot)
+            .then([](streams::streambuf<_CharType> buf) -> basic_istream<_CharType> {
+                return basic_istream<_CharType>(buf);
+            });
+    }
+
+    /// <summary>
+    /// Open a new output stream representing the given file.
+    /// If the file does not exist, it will be create unless the folder or directory
+    /// where it is to be found also does not exist.
+    /// </summary>
+    /// <param name="file_name">The name of the file</param>
+    /// <param name="mode">The opening mode of the file</param>
+    /// <param name="prot">The file protection mode</param>
+    /// <returns>A <c>task</c> that returns an opened output stream on completion.</returns>
+    static pplx::task<streams::basic_ostream<_CharType>> open_ostream(const utility::string_t& file_name,
+                                                                      std::ios_base::openmode mode = std::ios_base::out,
+#ifdef _WIN32
+                                                                      int prot = (int)std::ios_base::_Openprot
+#else
+                                                                      int prot = 0
+#endif
+    )
+    {
+        mode |= std::ios_base::out;
+        return streams::file_buffer<_CharType>::open(file_name, mode, prot)
+            .then([](streams::streambuf<_CharType> buf) -> basic_ostream<_CharType> {
+                return basic_ostream<_CharType>(buf);
+            });
+    }
+#else
+    /// <summary>
+    /// Open a new input stream representing the given file.
+    /// The file should already exist on disk, or an exception will be thrown.
+    /// </summary>
+    /// <param name="file">The StorageFile reference representing the file</param>
+    /// <param name="mode">The opening mode of the file</param>
+    /// <returns>A <c>task</c> that returns an opened input stream on completion.</returns>
+    static pplx::task<streams::basic_istream<_CharType>> open_istream(::Windows::Storage::StorageFile ^ file,
+                                                                      std::ios_base::openmode mode = std::ios_base::in)
+    {
+        mode |= std::ios_base::in;
+        return streams::file_buffer<_CharType>::open(file, mode)
+            .then([](streams::streambuf<_CharType> buf) -> basic_istream<_CharType> {
+                return basic_istream<_CharType>(buf);
+            });
+    }
+
+    /// <summary>
+    /// Open a new output stream representing the given file.
+    /// If the file does not exist, it will be create unless the folder or directory
+    /// where it is to be found also does not exist.
+    /// </summary>
+    /// <param name="file">The StorageFile reference representing the file</param>
+    /// <param name="mode">The opening mode of the file</param>
+    /// <returns>A <c>task</c> that returns an opened output stream on completion.</returns>
+    static pplx::task<streams::basic_ostream<_CharType>> open_ostream(::Windows::Storage::StorageFile ^ file,
+                                                                      std::ios_base::openmode mode = std::ios_base::out)
+    {
+        mode |= std::ios_base::out;
+        return streams::file_buffer<_CharType>::open(file, mode)
+            .then([](streams::streambuf<_CharType> buf) -> basic_ostream<_CharType> {
+                return basic_ostream<_CharType>(buf);
+            });
+    }
+#endif
+};
+
+typedef file_stream<uint8_t> fstream;
+} // namespace streams
+} // namespace Concurrency
+
+#endif
diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h
new file mode 100644 (file)
index 0000000..fb7c606
--- /dev/null
@@ -0,0 +1,766 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Client-side APIs.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_HTTP_CLIENT_H
+#define CASA_HTTP_CLIENT_H
+
+#if defined(__cplusplus_winrt)
+#if !defined(__WRL_NO_DEFAULT_LIB__)
+#define __WRL_NO_DEFAULT_LIB__
+#endif
+#include <msxml6.h>
+#include <wrl.h>
+namespace web
+{
+namespace http
+{
+namespace client
+{
+typedef IXMLHTTPRequest2* native_handle;
+}
+} // namespace http
+} // namespace web
+#else
+namespace web
+{
+namespace http
+{
+namespace client
+{
+typedef void* native_handle;
+}
+} // namespace http
+} // namespace web
+#endif // __cplusplus_winrt
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/details/basic_types.h"
+#include "cpprest/details/web_utilities.h"
+#include "cpprest/http_msg.h"
+#include "cpprest/json.h"
+#include "cpprest/uri.h"
+#include "pplx/pplxtasks.h"
+#include <limits>
+#include <memory>
+
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#include "cpprest/oauth1.h"
+#endif
+
+#include "cpprest/oauth2.h"
+
+#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#endif
+#include "boost/asio/ssl.hpp"
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+#endif
+
+/// The web namespace contains functionality common to multiple protocols like HTTP and WebSockets.
+namespace web
+{
+/// Declarations and functionality for the HTTP protocol.
+namespace http
+{
+/// HTTP client side library.
+namespace client
+{
+// credentials and web_proxy class has been moved from web::http::client namespace to web namespace.
+// The below using declarations ensure we don't break existing code.
+// Please use the web::credentials and web::web_proxy class going forward.
+using web::credentials;
+using web::web_proxy;
+
+/// <summary>
+/// HTTP client configuration class, used to set the possible configuration options
+/// used to create an http_client instance.
+/// </summary>
+class http_client_config
+{
+public:
+    http_client_config()
+        : m_guarantee_order(false)
+        , m_timeout(std::chrono::seconds(30))
+        , m_chunksize(0)
+        , m_request_compressed(false)
+#if !defined(__cplusplus_winrt)
+        , m_validate_certificates(true)
+#endif
+#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
+        , m_tlsext_sni_enabled(true)
+#endif
+#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 _WIN32_WINNT >= _WIN32_WINNT_VISTA
+    /// <summary>
+    /// Get OAuth 1.0 configuration.
+    /// </summary>
+    /// <returns>Shared pointer to OAuth 1.0 configuration.</returns>
+    const std::shared_ptr<oauth1::experimental::oauth1_config> oauth1() const { return m_oauth1; }
+
+    /// <summary>
+    /// Set OAuth 1.0 configuration.
+    /// </summary>
+    /// <param name="config">OAuth 1.0 configuration to set.</param>
+    void set_oauth1(oauth1::experimental::oauth1_config config)
+    {
+        m_oauth1 = std::make_shared<oauth1::experimental::oauth1_config>(std::move(config));
+    }
+#endif
+
+    /// <summary>
+    /// Get OAuth 2.0 configuration.
+    /// </summary>
+    /// <returns>Shared pointer to OAuth 2.0 configuration.</returns>
+    const std::shared_ptr<oauth2::experimental::oauth2_config> oauth2() const { return m_oauth2; }
+
+    /// <summary>
+    /// Set OAuth 2.0 configuration.
+    /// </summary>
+    /// <param name="config">OAuth 2.0 configuration to set.</param>
+    void set_oauth2(oauth2::experimental::oauth2_config config)
+    {
+        m_oauth2 = std::make_shared<oauth2::experimental::oauth2_config>(std::move(config));
+    }
+
+    /// <summary>
+    /// Get the web proxy object
+    /// </summary>
+    /// <returns>A reference to the web proxy object.</returns>
+    const web_proxy& proxy() const { return m_proxy; }
+
+    /// <summary>
+    /// Set the web proxy object
+    /// </summary>
+    /// <param name="proxy">A reference to the web proxy object.</param>
+    void set_proxy(web_proxy proxy) { m_proxy = std::move(proxy); }
+
+    /// <summary>
+    /// Get the client credentials
+    /// </summary>
+    /// <returns>A reference to the client credentials.</returns>
+    const http::client::credentials& credentials() const { return m_credentials; }
+
+    /// <summary>
+    /// Set the client credentials
+    /// </summary>
+    /// <param name="cred">A reference to the client credentials.</param>
+    void set_credentials(const http::client::credentials& cred) { m_credentials = cred; }
+
+    /// <summary>
+    /// Get the 'guarantee order' property
+    /// </summary>
+    /// <returns>The value of the property.</returns>
+    bool guarantee_order() const { return m_guarantee_order; }
+
+    /// <summary>
+    /// Set the 'guarantee order' property
+    /// </summary>
+    /// <param name="guarantee_order">The value of the property.</param>
+    CASABLANCA_DEPRECATED(
+        "Confusing API will be removed in future releases. If you need to order HTTP requests use task continuations.")
+    void set_guarantee_order(bool guarantee_order) { m_guarantee_order = guarantee_order; }
+
+    /// <summary>
+    /// Get the timeout
+    /// </summary>
+    /// <returns>The timeout (in seconds) used for each send and receive operation on the client.</returns>
+    utility::seconds timeout() const { return std::chrono::duration_cast<utility::seconds>(m_timeout); }
+
+    /// <summary>
+    /// Get the timeout
+    /// </summary>
+    /// <returns>The timeout (in whatever duration) used for each send and receive operation on the client.</returns>
+    template<class T>
+    T timeout() const
+    {
+        return std::chrono::duration_cast<T>(m_timeout);
+    }
+    /// <summary>
+    /// Set the timeout
+    /// </summary>
+    /// <param name="timeout">The timeout (duration from microseconds range and up) used for each send and receive
+    /// operation on the client.</param>
+    template<class T>
+    void set_timeout(const T& timeout)
+    {
+        m_timeout = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
+    }
+
+    /// <summary>
+    /// Get the client chunk size.
+    /// </summary>
+    /// <returns>The internal buffer size used by the http client when sending and receiving data from the
+    /// network.</returns>
+    size_t chunksize() const { return m_chunksize == 0 ? 64 * 1024 : m_chunksize; }
+
+    /// <summary>
+    /// Sets the client chunk size.
+    /// </summary>
+    /// <param name="size">The internal buffer size used by the http client when sending and receiving data from the
+    /// network.</param> <remarks>This is a hint -- an implementation may disregard the setting and use some other chunk
+    /// size.</remarks>
+    void set_chunksize(size_t size) { m_chunksize = size; }
+
+    /// <summary>
+    /// Returns true if the default chunk size is in use.
+    /// <remarks>If true, implementations are allowed to choose whatever size is best.</remarks>
+    /// </summary>
+    /// <returns>True if default, false if set by user.</returns>
+    bool is_default_chunksize() const { return m_chunksize == 0; }
+
+    /// <summary>
+    /// Checks if requesting a compressed response using Content-Encoding is turned on, the default is off.
+    /// </summary>
+    /// <returns>True if a content-encoded compressed response is allowed, false otherwise</returns>
+    bool request_compressed_response() const { return m_request_compressed; }
+
+    /// <summary>
+    /// Request that the server respond with a compressed body using Content-Encoding; to use Transfer-Encoding, do not
+    /// set this, and specify a vector of <see cref="web::http::details::compression::decompress_factory" /> pointers
+    /// to the set_decompress_factories method of the <see cref="web::http::http_request" /> object for the request.
+    /// If true and the server does not support compression, this will have no effect.
+    /// The response body is internally decompressed before the consumer receives the data.
+    /// </summary>
+    /// <param name="request_compressed">True to turn on content-encoded response body compression, false
+    /// otherwise.</param> <remarks>Please note there is a performance cost due to copying the request data. Currently
+    /// only supported on Windows and OSX.</remarks>
+    void set_request_compressed_response(bool request_compressed) { m_request_compressed = request_compressed; }
+
+#if !defined(__cplusplus_winrt)
+    /// <summary>
+    /// Gets the server certificate validation property.
+    /// </summary>
+    /// <returns>True if certificates are to be verified, false otherwise.</returns>
+    bool validate_certificates() const { return m_validate_certificates; }
+
+    /// <summary>
+    /// Sets the server certificate validation property.
+    /// </summary>
+    /// <param name="validate_certs">False to turn ignore all server certificate validation errors, true
+    /// otherwise.</param> <remarks>Note ignoring certificate errors can be dangerous and should be done with
+    /// caution.</remarks>
+    void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
+#endif
+
+#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
+    /// <summary>
+    /// Checks if request data buffering is turned on, the default is off.
+    /// </summary>
+    /// <returns>True if buffering is enabled, false otherwise</returns>
+    bool buffer_request() const { return m_buffer_request; }
+
+    /// <summary>
+    /// Sets the request buffering property.
+    /// If true, in cases where the request body/stream doesn't support seeking the request data will be buffered.
+    /// This can help in situations where an authentication challenge might be expected.
+    /// </summary>
+    /// <param name="buffer_request">True to turn on buffer, false otherwise.</param>
+    /// <remarks>Please note there is a performance cost due to copying the request data.</remarks>
+    void set_buffer_request(bool buffer_request) { m_buffer_request = buffer_request; }
+#endif
+
+    /// <summary>
+    /// Get the maximum number of redirects to follow automatically.
+    /// A value of 0 indicates that no automatic redirection is performed.
+    /// </summary>
+    /// <returns>The maximum number of redirects to follow automatically.</returns>
+    /// <remarks>This is a hint -- an implementation may enforce a lower value.</remarks>
+    size_t max_redirects() const { return m_max_redirects; }
+
+    /// <summary>
+    /// Set the maximum number of redirects to follow automatically.
+    /// A value of 0 indicates that no automatic redirection is performed.
+    /// </summary>
+    /// <param name="max_redirects">The maximum number of redirects to follow automatically.</param>
+    /// <remarks>This is a hint -- an implementation may enforce a lower value.</remarks>
+    void set_max_redirects(size_t max_redirects) { m_max_redirects = max_redirects; }
+
+    /// <summary>
+    /// Checks if HTTPS to HTTP redirects are automatically followed.
+    /// </summary>
+    /// <returns>True if HTTPS to HTTP redirects are automatically followed, false otherwise.</returns>
+    bool https_to_http_redirects() const { return m_https_to_http_redirects; }
+
+    /// <summary>
+    /// Sets if HTTPS to HTTP redirects are automatically followed.
+    /// </summary>
+    /// <param name="https_to_http_redirects">True if HTTPS to HTTP redirects are to be automatically
+    /// followed, false otherwise.</param>
+    void set_https_to_http_redirects(bool https_to_http_redirects)
+    {
+        m_https_to_http_redirects = https_to_http_redirects;
+    }
+
+    /// <summary>
+    /// Sets a callback to enable custom setting of platform specific options.
+    /// </summary>
+    /// <remarks>
+    /// The native_handle is the following type depending on the underlying platform:
+    ///     Windows Desktop, WinHTTP - HINTERNET (session)
+    /// </remarks>
+    /// <param name="callback">A user callback allowing for customization of the session</param>
+    void set_nativesessionhandle_options(const std::function<void(native_handle)>& callback)
+    {
+        m_set_user_nativesessionhandle_options = callback;
+    }
+
+    /// <summary>
+    /// Invokes a user's callback to allow for customization of the session.
+    /// </summary>
+    /// <remarks>Internal Use Only</remarks>
+    /// <param name="handle">A internal implementation handle.</param>
+    void _invoke_nativesessionhandle_options(native_handle handle) const
+    {
+        if (m_set_user_nativesessionhandle_options) m_set_user_nativesessionhandle_options(handle);
+    }
+
+    /// <summary>
+    /// Sets a callback to enable custom setting of platform specific options.
+    /// </summary>
+    /// <remarks>
+    /// The native_handle is the following type depending on the underlying platform:
+    ///     Windows Desktop, WinHTTP - HINTERNET
+    ///     Windows Runtime, WinRT - IXMLHTTPRequest2 *
+    ///     All other platforms, Boost.Asio:
+    ///         https - boost::asio::ssl::stream<boost::asio::ip::tcp::socket &> *
+    ///         http - boost::asio::ip::tcp::socket *
+    /// </remarks>
+    /// <param name="callback">A user callback allowing for customization of the request</param>
+    void set_nativehandle_options(const std::function<void(native_handle)>& callback)
+    {
+        m_set_user_nativehandle_options = callback;
+    }
+
+    /// <summary>
+    /// Invokes a user's callback to allow for customization of the request.
+    /// </summary>
+    /// <param name="handle">A internal implementation handle.</param>
+    void invoke_nativehandle_options(native_handle handle) const
+    {
+        if (m_set_user_nativehandle_options) m_set_user_nativehandle_options(handle);
+    }
+
+#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
+    /// <summary>
+    /// Sets a callback to enable custom setting of the ssl context, at construction time.
+    /// </summary>
+    /// <param name="callback">A user callback allowing for customization of the ssl context at construction
+    /// time.</param>
+    void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& callback)
+    {
+        m_ssl_context_callback = callback;
+    }
+
+    /// <summary>
+    /// Gets the user's callback to allow for customization of the ssl context.
+    /// </summary>
+    const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
+    {
+        return m_ssl_context_callback;
+    }
+
+    /// <summary>
+    /// Gets the TLS extension server name indication (SNI) status.
+    /// </summary>
+    /// <returns>True if TLS server name indication is enabled, false otherwise.</returns>
+    bool is_tlsext_sni_enabled() const { return m_tlsext_sni_enabled; }
+
+    /// <summary>
+    /// Sets the TLS extension server name indication (SNI) status.
+    /// </summary>
+    /// <param name="tlsext_sni_enabled">False to disable the TLS (ClientHello) extension for server name indication,
+    /// true otherwise.</param> <remarks>Note: This setting is enabled by default as it is required in most virtual
+    /// hosting scenarios.</remarks>
+    void set_tlsext_sni_enabled(bool tlsext_sni_enabled) { m_tlsext_sni_enabled = tlsext_sni_enabled; }
+#endif
+
+private:
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+    std::shared_ptr<oauth1::experimental::oauth1_config> m_oauth1;
+#endif
+
+    std::shared_ptr<oauth2::experimental::oauth2_config> m_oauth2;
+    web_proxy m_proxy;
+    http::client::credentials m_credentials;
+    // Whether or not to guarantee ordering, i.e. only using one underlying TCP connection.
+    bool m_guarantee_order;
+
+    std::chrono::microseconds m_timeout;
+    size_t m_chunksize;
+    bool m_request_compressed;
+
+#if !defined(__cplusplus_winrt)
+    // IXmlHttpRequest2 doesn't allow configuration of certificate verification.
+    bool m_validate_certificates;
+#endif
+
+    std::function<void(native_handle)> m_set_user_nativehandle_options;
+    std::function<void(native_handle)> m_set_user_nativesessionhandle_options;
+
+#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
+    std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
+    bool m_tlsext_sni_enabled;
+#endif
+#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;
+
+/// <summary>
+/// HTTP client class, used to maintain a connection to an HTTP service for an extended session.
+/// </summary>
+class http_client
+{
+public:
+    /// <summary>
+    /// Creates a new http_client connected to specified uri.
+    /// </summary>
+    /// <param name="base_uri">A string representation of the base uri to be used for all requests. Must start with
+    /// either "http://" or "https://"</param>
+    _ASYNCRTIMP http_client(const uri& base_uri);
+
+    /// <summary>
+    /// Creates a new http_client connected to specified uri.
+    /// </summary>
+    /// <param name="base_uri">A string representation of the base uri to be used for all requests. Must start with
+    /// either "http://" or "https://"</param> <param name="client_config">The http client configuration object
+    /// containing the possible configuration options to initialize the <c>http_client</c>. </param>
+    _ASYNCRTIMP http_client(const uri& base_uri, const http_client_config& client_config);
+
+    /// <summary>
+    /// Note the destructor doesn't necessarily close the connection and release resources.
+    /// The connection is reference counted with the http_responses.
+    /// </summary>
+    _ASYNCRTIMP ~http_client() CPPREST_NOEXCEPT;
+
+    /// <summary>
+    /// Gets the base URI.
+    /// </summary>
+    /// <returns>
+    /// A base URI initialized in constructor
+    /// </returns>
+    _ASYNCRTIMP const uri& base_uri() const;
+
+    /// <summary>
+    /// Get client configuration object
+    /// </summary>
+    /// <returns>A reference to the client configuration object.</returns>
+    _ASYNCRTIMP const http_client_config& client_config() const;
+
+    /// <summary>
+    /// Adds an HTTP pipeline stage to the client.
+    /// </summary>
+    /// <param name="handler">A function object representing the pipeline stage.</param>
+    _ASYNCRTIMP void add_handler(const std::function<pplx::task<http_response> __cdecl(
+                                     http_request, std::shared_ptr<http::http_pipeline_stage>)>& handler);
+
+    /// <summary>
+    /// Adds an HTTP pipeline stage to the client.
+    /// </summary>
+    /// <param name="stage">A shared pointer to a pipeline stage.</param>
+    _ASYNCRTIMP void add_handler(const std::shared_ptr<http::http_pipeline_stage>& stage);
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request.
+    /// </summary>
+    /// <param name="request">Request to send.</param>
+    /// <param name="token">Cancellation token for cancellation of this request operation.</param>
+    /// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
+    _ASYNCRTIMP pplx::task<http_response> request(
+        http_request request, const pplx::cancellation_token& token = pplx::cancellation_token::none());
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="token">Cancellation token for cancellation of this request operation.</param>
+    /// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
+    pplx::task<http_response> request(const method& mtd,
+                                      const pplx::cancellation_token& token = pplx::cancellation_token::none())
+    {
+        http_request msg(mtd);
+        return request(msg, token);
+    }
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="token">Cancellation token for cancellation of this request operation.</param>
+    /// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utility::string_t& path_query_fragment,
+                                      const pplx::cancellation_token& token = pplx::cancellation_token::none())
+    {
+        http_request msg(mtd);
+        msg.set_request_uri(path_query_fragment);
+        return request(msg, token);
+    }
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="body_data">The data to be used as the message body, represented using the json
+    /// object library.</param> <param name="token">Cancellation token for cancellation of this request
+    /// operation.</param> <returns>An asynchronous operation that is completed once a response from the request is
+    /// received.</returns>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utility::string_t& path_query_fragment,
+                                      const json::value& body_data,
+                                      const pplx::cancellation_token& token = pplx::cancellation_token::none())
+    {
+        http_request msg(mtd);
+        msg.set_request_uri(path_query_fragment);
+        msg.set_body(body_data);
+        return request(msg, token);
+    }
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request with a string body. Assumes the
+    /// character encoding of the string is UTF-8.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
+    /// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
+    /// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
+    /// once a response from the request is received.</returns>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utf8string& path_query_fragment,
+                                      const utf8string& body_data,
+                                      const utf8string& content_type = "text/plain; charset=utf-8",
+                                      const pplx::cancellation_token& token = pplx::cancellation_token::none())
+    {
+        http_request msg(mtd);
+        msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
+        msg.set_body(body_data, content_type);
+        return request(msg, token);
+    }
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request with a string body. Assumes the
+    /// character encoding of the string is UTF-8.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
+    /// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
+    /// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
+    /// once a response from the request is received.</returns>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utf8string& path_query_fragment,
+                                      utf8string&& body_data,
+                                      const utf8string& content_type = "text/plain; charset=utf-8",
+                                      const pplx::cancellation_token& token = pplx::cancellation_token::none())
+    {
+        http_request msg(mtd);
+        msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
+        msg.set_body(std::move(body_data), content_type);
+        return request(msg, token);
+    }
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request with a string body. Assumes the
+    /// character encoding of the string is UTF-16 will perform conversion to UTF-8.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
+    /// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
+    /// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
+    /// once a response from the request is received.</returns>
+    pplx::task<http_response> request(
+        const method& mtd,
+        const utf16string& path_query_fragment,
+        const utf16string& body_data,
+        const utf16string& content_type = utility::conversions::to_utf16string("text/plain"),
+        const pplx::cancellation_token& token = pplx::cancellation_token::none())
+    {
+        http_request msg(mtd);
+        msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
+        msg.set_body(body_data, content_type);
+        return request(msg, token);
+    }
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request with a string body. Assumes the
+    /// character encoding of the string is UTF-8.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
+    /// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
+    /// operation that is completed once a response from the request is received.</returns>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utf8string& path_query_fragment,
+                                      const utf8string& body_data,
+                                      const pplx::cancellation_token& token)
+    {
+        return request(mtd, path_query_fragment, body_data, "text/plain; charset=utf-8", token);
+    }
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request with a string body. Assumes the
+    /// character encoding of the string is UTF-8.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
+    /// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
+    /// operation that is completed once a response from the request is received.</returns>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utf8string& path_query_fragment,
+                                      utf8string&& body_data,
+                                      const pplx::cancellation_token& token)
+    {
+        http_request msg(mtd);
+        msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
+        msg.set_body(std::move(body_data), "text/plain; charset=utf-8");
+        return request(msg, token);
+    }
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request with a string body. Assumes
+    /// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
+    /// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
+    /// operation that is completed once a response from the request is received.</returns>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utf16string& path_query_fragment,
+                                      const utf16string& body_data,
+                                      const pplx::cancellation_token& token)
+    {
+        return request(
+            mtd, path_query_fragment, body_data, ::utility::conversions::to_utf16string("text/plain"), token);
+    }
+
+#if !defined(__cplusplus_winrt)
+    /// <summary>
+    /// Asynchronously sends an HTTP request.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
+    /// name="content_type">A string holding the MIME type of the message body.</param> <param name="token">Cancellation
+    /// token for cancellation of this request operation.</param> <returns>A task that is completed once a response from
+    /// the request is received.</returns>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utility::string_t& path_query_fragment,
+                                      const concurrency::streams::istream& body,
+                                      const utility::string_t& content_type = _XPLATSTR("application/octet-stream"),
+                                      const pplx::cancellation_token& token = pplx::cancellation_token::none())
+    {
+        http_request msg(mtd);
+        msg.set_request_uri(path_query_fragment);
+        msg.set_body(body, content_type);
+        return request(msg, token);
+    }
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
+    /// name="token">Cancellation token for cancellation of this request operation.</param> <returns>A task that is
+    /// completed once a response from the request is received.</returns>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utility::string_t& path_query_fragment,
+                                      const concurrency::streams::istream& body,
+                                      const pplx::cancellation_token& token)
+    {
+        return request(mtd, path_query_fragment, body, _XPLATSTR("application/octet-stream"), token);
+    }
+#endif // __cplusplus_winrt
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
+    /// name="content_length">Size of the message body.</param> <param name="content_type">A string holding the MIME
+    /// type of the message body.</param> <param name="token">Cancellation token for cancellation of this request
+    /// operation.</param> <returns>A task that is completed once a response from the request is received.</returns>
+    /// <remarks>Winrt requires to provide content_length.</remarks>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utility::string_t& path_query_fragment,
+                                      const concurrency::streams::istream& body,
+                                      size_t content_length,
+                                      const utility::string_t& content_type = _XPLATSTR("application/octet-stream"),
+                                      const pplx::cancellation_token& token = pplx::cancellation_token::none())
+    {
+        http_request msg(mtd);
+        msg.set_request_uri(path_query_fragment);
+        msg.set_body(body, content_length, content_type);
+        return request(msg, token);
+    }
+
+    /// <summary>
+    /// Asynchronously sends an HTTP request.
+    /// </summary>
+    /// <param name="mtd">HTTP request method.</param>
+    /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
+    /// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
+    /// name="content_length">Size of the message body.</param> <param name="token">Cancellation token for cancellation
+    /// of this request operation.</param> <returns>A task that is completed once a response from the request is
+    /// received.</returns> <remarks>Winrt requires to provide content_length.</remarks>
+    pplx::task<http_response> request(const method& mtd,
+                                      const utility::string_t& path_query_fragment,
+                                      const concurrency::streams::istream& body,
+                                      size_t content_length,
+                                      const pplx::cancellation_token& token)
+    {
+        return request(mtd, path_query_fragment, body, content_length, _XPLATSTR("application/octet-stream"), token);
+    }
+
+private:
+    std::shared_ptr<::web::http::client::http_pipeline> m_pipeline;
+};
+
+namespace details
+{
+#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
+extern const utility::char_t* get_with_body_err_msg;
+#endif
+
+} // namespace details
+
+} // namespace client
+} // namespace http
+} // namespace web
+
+#endif
diff --git a/Release/include/cpprest/http_compression.h b/Release/include/cpprest/http_compression.h
new file mode 100644 (file)
index 0000000..b0059a6
--- /dev/null
@@ -0,0 +1,326 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Compression and decompression interfaces
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+namespace web
+{
+namespace http
+{
+namespace compression
+{
+/// <summary>
+/// Hint as to whether a compress or decompress call is meant to be the last for a particular HTTP request or reply
+/// </summary>
+enum operation_hint
+{
+    is_last, // Used for the expected last compress() call, or for an expected single decompress() call
+    has_more // Used when further compress() calls will be made, or when multiple decompress() calls may be required
+};
+
+/// <summary>
+/// Result structure for asynchronous compression and decompression operations
+/// </summary>
+struct operation_result
+{
+    size_t input_bytes_processed; // From the input buffer
+    size_t output_bytes_produced; // To the output buffer
+    bool done; // For compress, set when 'last' is true and there was enough space to complete compression;
+               // for decompress, set if the end of the decompression stream has been reached
+};
+
+/// <summary>
+/// Compression interface for use with HTTP requests
+/// </summary>
+class compress_provider
+{
+public:
+    virtual const utility::string_t& algorithm() const = 0;
+    virtual size_t compress(const uint8_t* input,
+                            size_t input_size,
+                            uint8_t* output,
+                            size_t output_size,
+                            operation_hint hint,
+                            size_t& input_bytes_processed,
+                            bool& done) = 0;
+    virtual pplx::task<operation_result> compress(
+        const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint) = 0;
+    virtual void reset() = 0;
+    virtual ~compress_provider() = default;
+};
+
+/// <summary>
+/// Decompression interface for use with HTTP requests
+/// </summary>
+class decompress_provider
+{
+public:
+    virtual const utility::string_t& algorithm() const = 0;
+    virtual size_t decompress(const uint8_t* input,
+                              size_t input_size,
+                              uint8_t* output,
+                              size_t output_size,
+                              operation_hint hint,
+                              size_t& input_bytes_processed,
+                              bool& done) = 0;
+    virtual pplx::task<operation_result> decompress(
+        const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint) = 0;
+    virtual void reset() = 0;
+    virtual ~decompress_provider() = default;
+};
+
+/// <summary>
+/// Factory interface for compressors for use with received HTTP requests
+/// </summary>
+class compress_factory
+{
+public:
+    virtual const utility::string_t& algorithm() const = 0;
+    virtual std::unique_ptr<compress_provider> make_compressor() const = 0;
+    virtual ~compress_factory() = default;
+};
+
+/// <summary>
+/// Factory interface for decompressors for use with HTTP requests
+/// </summary>
+class decompress_factory
+{
+public:
+    virtual const utility::string_t& algorithm() const = 0;
+    virtual uint16_t weight() const = 0;
+    virtual std::unique_ptr<decompress_provider> make_decompressor() const = 0;
+    virtual ~decompress_factory() = default;
+};
+
+/// <summary>
+/// Built-in compression support
+/// </summary>
+namespace builtin
+{
+/// <summary>
+/// Test whether cpprestsdk was built with built-in compression support
+/// <returns>True if cpprestsdk was built with built-in compression support, and false if not.</returns>
+/// </summary>
+_ASYNCRTIMP bool supported();
+
+/// <summary>
+// String constants for each built-in compression algorithm, for convenient use with the factory functions
+/// </summary>
+namespace algorithm
+{
+#if defined(_MSC_VER) && _MSC_VER < 1900
+const utility::char_t* const GZIP = _XPLATSTR("gzip");
+const utility::char_t* const DEFLATE = _XPLATSTR("deflate");
+const utility::char_t* const BROTLI = _XPLATSTR("br");
+#else // ^^^ VS2013 and before ^^^ // vvv VS2015+, and everything else vvv
+constexpr const utility::char_t* const GZIP = _XPLATSTR("gzip");
+constexpr const utility::char_t* const DEFLATE = _XPLATSTR("deflate");
+constexpr const utility::char_t* const BROTLI = _XPLATSTR("br");
+#endif
+
+/// <summary>
+/// Test whether cpprestsdk was built with built-in compression support and
+/// the supplied string matches a supported built-in algorithm
+/// <param name="algorithm">The name of the algorithm to test for built-in support.</param>
+/// <returns>True if cpprestsdk was built with built-in compression support and
+/// the supplied string matches a supported built-in algorithm, and false if not.</returns>
+/// <summary>
+_ASYNCRTIMP bool supported(const utility::string_t& algorithm);
+} // namespace algorithm
+
+/// <summary>
+/// Factory function to instantiate a built-in compression provider with default parameters by compression algorithm
+/// name.
+/// </summary>
+/// <param name="algorithm">The name of the algorithm for which to instantiate a provider.</param>
+/// <returns>
+/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
+/// </returns>
+_ASYNCRTIMP std::unique_ptr<compress_provider> make_compressor(const utility::string_t& algorithm);
+
+/// <summary>
+/// Factory function to instantiate a built-in decompression provider with default parameters by compression algorithm
+/// name.
+/// </summary>
+/// <param name="algorithm">The name of the algorithm for which to instantiate a provider.</param>
+/// <returns>
+/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
+/// </returns>
+_ASYNCRTIMP std::unique_ptr<decompress_provider> make_decompressor(const utility::string_t& algorithm);
+
+/// <summary>
+/// Factory function to obtain a pointer to a built-in compression provider factory by compression algorithm name.
+/// </summary>
+/// <param name="algorithm">The name of the algorithm for which to find a factory.</param>
+/// <returns>
+/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
+/// </returns>
+_ASYNCRTIMP std::shared_ptr<compress_factory> get_compress_factory(const utility::string_t& algorithm);
+
+/// <summary>
+/// Factory function to obtain a pointer to a built-in decompression provider factory by compression algorithm name.
+/// </summary>
+/// <param name="algorithm">The name of the algorithm for which to find a factory.</param>
+/// <returns>
+/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
+/// </returns>
+_ASYNCRTIMP std::shared_ptr<decompress_factory> get_decompress_factory(const utility::string_t& algorithm);
+
+/// <summary>
+// Factory function to instantiate a built-in gzip compression provider with caller-selected parameters.
+/// </summary>
+/// <returns>
+/// A caller-owned pointer to a gzip compression provider, or to nullptr if the library was built without built-in
+/// compression support.
+/// </returns>
+_ASYNCRTIMP std::unique_ptr<compress_provider> make_gzip_compressor(int compressionLevel,
+                                                                    int method,
+                                                                    int strategy,
+                                                                    int memLevel);
+
+/// <summary>
+// Factory function to instantiate a built-in deflate compression provider with caller-selected parameters.
+/// </summary>
+/// <returns>
+/// A caller-owned pointer to a deflate compression provider, or to nullptr if the library was built without built-in
+/// compression support..
+/// </returns>
+_ASYNCRTIMP std::unique_ptr<compress_provider> make_deflate_compressor(int compressionLevel,
+                                                                       int method,
+                                                                       int strategy,
+                                                                       int memLevel);
+
+/// <summary>
+// Factory function to instantiate a built-in Brotli compression provider with caller-selected parameters.
+/// </summary>
+/// <returns>
+/// A caller-owned pointer to a Brotli compression provider, or to nullptr if the library was built without built-in
+/// compression support.
+/// </returns>
+_ASYNCRTIMP std::unique_ptr<compress_provider> make_brotli_compressor(
+    uint32_t window, uint32_t quality, uint32_t mode, uint32_t block, uint32_t nomodel, uint32_t hint);
+} // namespace builtin
+
+/// <summary>
+/// Factory function to instantiate a compression provider factory by compression algorithm name.
+/// </summary>
+/// <param name="algorithm">The name of the algorithm supported by the factory.  Must match that returned by the
+/// <c>web::http::compression::compress_provider</c> type instantiated by the factory's make_compressor function.
+/// The supplied string is copied, and thus need not remain valid once the call returns.</param>
+/// <param name="make_compressor">A factory function to be used to instantiate a compressor matching the factory's
+/// reported algorithm.</param>
+/// <returns>
+/// A pointer to a generic provider factory implementation configured with the supplied parameters.
+/// </returns>
+/// <remarks>
+/// This method may be used to conveniently instantiate a factory object for a caller-selected <c>compress_provider</c>.
+/// That provider may be of the caller's own design, or it may be one of the built-in types.  As such, this method may
+/// be helpful when a caller wishes to build vectors containing a mix of custom and built-in providers.
+/// </remarks>
+_ASYNCRTIMP std::shared_ptr<compress_factory> make_compress_factory(
+    const utility::string_t& algorithm, std::function<std::unique_ptr<compress_provider>()> make_compressor);
+
+/// <summary>
+/// Factory function to instantiate a decompression provider factory by compression algorithm name.
+/// </summary>
+/// <param name="algorithm">The name of the algorithm supported by the factory.  Must match that returned by the
+/// <c>web::http::compression::decompress_provider</c> type instantiated by the factory's make_decompressor function.
+/// The supplied string is copied, and thus need not remain valid once the call returns.</param>
+/// <param name="weight">A numeric weight for the compression algorithm, times 1000,  for use as a "quality value" when
+/// requesting that the server send a compressed response.  Valid values are between 0 and 1000, inclusive, where higher
+/// values indicate more preferred algorithms, and 0 indicates that the algorithm is not allowed; values greater than
+/// 1000 are treated as 1000.</param>
+/// <param name="make_decompressor">A factory function to be used to instantiate a decompressor matching the factory's
+/// reported algorithm.</param>
+/// <returns>
+/// A pointer to a generic provider factory implementation configured with the supplied parameters.
+/// </returns>
+/// <remarks>
+/// This method may be used to conveniently instantiate a factory object for a caller-selected
+/// <c>decompress_provider</c>.  That provider may be of the caller's own design, or it may be one of the built-in
+/// types.  As such, this method may be helpful when a caller wishes to change the weights of built-in provider types,
+/// to use custom providers without explicitly implementing a <c>decompress_factory</c>, or to build vectors containing
+/// a mix of custom and built-in providers.
+/// </remarks>
+_ASYNCRTIMP std::shared_ptr<decompress_factory> make_decompress_factory(
+    const utility::string_t& algorithm,
+    uint16_t weight,
+    std::function<std::unique_ptr<decompress_provider>()> make_decompressor);
+
+namespace details
+{
+/// <summary>
+/// Header type enum for use with compressor and decompressor header parsing and building functions
+/// </summary>
+enum header_types
+{
+    transfer_encoding,
+    content_encoding,
+    te,
+    accept_encoding
+};
+
+/// <summary>
+/// Factory function to instantiate an appropriate compression provider, if any.
+/// </summary>
+/// <param name="encoding">A TE or Accept-Encoding header to interpret.</param>
+/// <param name="type">Specifies the type of header whose contents are in the encoding parameter; valid values are
+/// <c>header_type::te</c> and <c>header_type::accept_encoding</c>.</param>
+/// <param name="preferred">A compressor object of the caller's preferred (possibly custom) type, which is used if
+/// possible.</param>
+/// <param name="factories">A collection of factory objects for use in construction of an appropriate compressor, if
+/// any. If empty or not supplied, the set of supported built-in compressors is used.</param>
+/// <returns>
+/// A pointer to a compressor object that is acceptable per the supplied header, or to nullptr if no matching
+/// algorithm is found.
+/// </returns>
+_ASYNCRTIMP std::unique_ptr<compress_provider> get_compressor_from_header(
+    const utility::string_t& encoding,
+    header_types type,
+    const std::vector<std::shared_ptr<compress_factory>>& factories = std::vector<std::shared_ptr<compress_factory>>());
+
+/// <summary>
+/// Factory function to instantiate an appropriate decompression provider, if any.
+/// </summary>
+/// <param name="encoding">A Transfer-Encoding or Content-Encoding header to interpret.</param>
+/// <param name="type">Specifies the type of header whose contents are in the encoding parameter; valid values are
+/// <c>header_type::transfer_encoding</c> and <c>header_type::content_encoding</c>.</param>
+/// <param name="factories">A collection of factory objects for use in construction of an appropriate decompressor,
+/// if any. If empty or not supplied, the set of supported built-in compressors is used.</param>
+/// <returns>
+/// A pointer to a decompressor object that is acceptable per the supplied header, or to nullptr if no matching
+/// algorithm is found.
+/// </returns>
+_ASYNCRTIMP std::unique_ptr<decompress_provider> get_decompressor_from_header(
+    const utility::string_t& encoding,
+    header_types type,
+    const std::vector<std::shared_ptr<decompress_factory>>& factories =
+        std::vector<std::shared_ptr<decompress_factory>>());
+
+/// <summary>
+/// Helper function to compose a TE or Accept-Encoding header with supported, and possibly ranked, compression
+/// algorithms.
+/// </summary>
+/// <param name="type">Specifies the type of header to be built; valid values are <c>header_type::te</c> and
+/// <c>header_type::accept_encoding</c>.</param>
+/// <param name="factories">A collection of factory objects for use in header construction. If empty or not
+/// supplied, the set of supported built-in compressors is used.</param>
+/// <returns>
+/// A well-formed header, without the header name, specifying the acceptable ranked compression types.
+/// </returns>
+_ASYNCRTIMP utility::string_t build_supported_header(header_types type,
+                                                     const std::vector<std::shared_ptr<decompress_factory>>& factories =
+                                                         std::vector<std::shared_ptr<decompress_factory>>());
+} // namespace details
+} // namespace compression
+} // namespace http
+} // namespace web
diff --git a/Release/include/cpprest/http_headers.h b/Release/include/cpprest/http_headers.h
new file mode 100644 (file)
index 0000000..4b4f9ea
--- /dev/null
@@ -0,0 +1,322 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "cpprest/asyncrt_utils.h"
+#include <map>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <vector>
+
+namespace web
+{
+namespace http
+{
+/// <summary>
+/// Binds an individual reference to a string value.
+/// </summary>
+/// <typeparam name="key_type">The type of string value.</typeparam>
+/// <typeparam name="_t">The type of the value to bind to.</typeparam>
+/// <param name="text">The string value.</param>
+/// <param name="ref">The value to bind to.</param>
+/// <returns><c>true</c> if the binding succeeds, <c>false</c> otherwise.</returns>
+template<typename key_type, typename _t>
+CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, std::istringstream instead.")
+bool bind(const key_type& text, _t& ref) // const
+{
+    utility::istringstream_t iss(text);
+    iss >> ref;
+    if (iss.fail() || !iss.eof())
+    {
+        return false;
+    }
+
+    return true;
+}
+
+/// <summary>
+/// Binds an individual reference to a string value.
+/// This specialization is need because <c>istringstream::&gt;&gt;</c> delimits on whitespace.
+/// </summary>
+/// <typeparam name="key_type">The type of the string value.</typeparam>
+/// <param name="text">The string value.</param>
+/// <param name="ref">The value to bind to.</param>
+/// <returns><c>true</c> if the binding succeeds, <c>false</c> otherwise.</returns>
+template<typename key_type>
+CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release.")
+bool bind(const key_type& text, utility::string_t& ref) // const
+{
+    ref = text;
+    return true;
+}
+
+namespace details
+{
+template<typename key_type, typename _t>
+bool bind_impl(const key_type& text, _t& ref)
+{
+    utility::istringstream_t iss(text);
+    iss.imbue(std::locale::classic());
+    iss >> ref;
+    if (iss.fail() || !iss.eof())
+    {
+        return false;
+    }
+
+    return true;
+}
+
+template<typename key_type>
+bool bind_impl(const key_type& text, utf16string& ref)
+{
+    ref = utility::conversions::to_utf16string(text);
+    return true;
+}
+
+template<typename key_type>
+bool bind_impl(const key_type& text, std::string& ref)
+{
+    ref = utility::conversions::to_utf8string(text);
+    return true;
+}
+} // namespace details
+
+/// <summary>
+/// Represents HTTP headers, acts like a map.
+/// </summary>
+class http_headers
+{
+public:
+    /// Function object to perform case insensitive comparison of wstrings.
+    struct _case_insensitive_cmp
+    {
+        bool operator()(const utility::string_t& str1, const utility::string_t& str2) const
+        {
+            return utility::details::str_iless(str1, str2);
+        }
+    };
+
+private:
+    typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp> inner_container;
+
+public:
+    /// <summary>
+    /// STL-style typedefs
+    /// </summary>
+    typedef inner_container::key_type key_type;
+    typedef inner_container::key_compare key_compare;
+    typedef inner_container::allocator_type allocator_type;
+    typedef inner_container::size_type size_type;
+    typedef inner_container::difference_type difference_type;
+    typedef inner_container::pointer pointer;
+    typedef inner_container::const_pointer const_pointer;
+    typedef inner_container::reference reference;
+    typedef inner_container::const_reference const_reference;
+    typedef inner_container::iterator iterator;
+    typedef inner_container::const_iterator const_iterator;
+    typedef inner_container::reverse_iterator reverse_iterator;
+    typedef inner_container::const_reverse_iterator const_reverse_iterator;
+
+    /// <summary>
+    /// Constructs an empty set of HTTP headers.
+    /// </summary>
+    http_headers() {}
+
+    /// <summary>
+    /// Copy constructor.
+    /// </summary>
+    /// <param name="other">An <c>http_headers</c> object to copy from.</param>
+    http_headers(const http_headers& other) : m_headers(other.m_headers) {}
+
+    /// <summary>
+    /// Assignment operator.
+    /// </summary>
+    /// <param name="other">An <c>http_headers</c> object to copy from.</param>
+    http_headers& operator=(const http_headers& other)
+    {
+        if (this != &other)
+        {
+            m_headers = other.m_headers;
+        }
+        return *this;
+    }
+
+    /// <summary>
+    /// Move constructor.
+    /// </summary>
+    /// <param name="other">An <c>http_headers</c> object to move.</param>
+    http_headers(http_headers&& other) : m_headers(std::move(other.m_headers)) {}
+
+    /// <summary>
+    /// Move assignment operator.
+    /// </summary>
+    /// <param name="other">An <c>http_headers</c> object to move.</param>
+    http_headers& operator=(http_headers&& other)
+    {
+        if (this != &other)
+        {
+            m_headers = std::move(other.m_headers);
+        }
+        return *this;
+    }
+
+    /// <summary>
+    /// Adds a header field using the '&lt;&lt;' operator.
+    /// </summary>
+    /// <param name="name">The name of the header field.</param>
+    /// <param name="value">The value of the header field.</param>
+    /// <remarks>If the header field exists, the value will be combined as comma separated string.</remarks>
+    template<typename _t1>
+    void add(const key_type& name, const _t1& value)
+    {
+        auto printedValue = utility::conversions::details::print_string(value);
+        auto& mapVal = m_headers[name];
+        if (mapVal.empty())
+        {
+            mapVal = std::move(printedValue);
+        }
+        else
+        {
+            mapVal.append(_XPLATSTR(", ")).append(std::move(printedValue));
+        }
+    }
+
+    /// <summary>
+    /// Removes a header field.
+    /// </summary>
+    /// <param name="name">The name of the header field.</param>
+    void remove(const key_type& name) { m_headers.erase(name); }
+
+    /// <summary>
+    /// Removes all elements from the headers.
+    /// </summary>
+    void clear() { m_headers.clear(); }
+
+    /// <summary>
+    /// Checks if there is a header with the given key.
+    /// </summary>
+    /// <param name="name">The name of the header field.</param>
+    /// <returns><c>true</c> if there is a header with the given name, <c>false</c> otherwise.</returns>
+    bool has(const key_type& name) const { return m_headers.find(name) != m_headers.end(); }
+
+    /// <summary>
+    /// Returns the number of header fields.
+    /// </summary>
+    /// <returns>Number of header fields.</returns>
+    size_type size() const { return m_headers.size(); }
+
+    /// <summary>
+    /// Tests to see if there are any header fields.
+    /// </summary>
+    /// <returns><c>true</c> if there are no headers, <c>false</c> otherwise.</returns>
+    bool empty() const { return m_headers.empty(); }
+
+    /// <summary>
+    /// Returns a reference to header field with given name, if there is no header field one is inserted.
+    /// </summary>
+    utility::string_t& operator[](const key_type& name) { return m_headers[name]; }
+
+    /// <summary>
+    /// Checks if a header field exists with given name and returns an iterator if found. Otherwise
+    /// and iterator to end is returned.
+    /// </summary>
+    /// <param name="name">The name of the header field.</param>
+    /// <returns>An iterator to where the HTTP header is found.</returns>
+    iterator find(const key_type& name) { return m_headers.find(name); }
+    const_iterator find(const key_type& name) const { return m_headers.find(name); }
+
+    /// <summary>
+    /// Attempts to match a header field with the given name using the '>>' operator.
+    /// </summary>
+    /// <param name="name">The name of the header field.</param>
+    /// <param name="value">The value of the header field.</param>
+    /// <returns><c>true</c> if header field was found and successfully stored in value parameter.</returns>
+    template<typename _t1>
+    bool match(const key_type& name, _t1& value) const
+    {
+        auto iter = m_headers.find(name);
+        if (iter == m_headers.end())
+        {
+            return false;
+        }
+
+        return web::http::details::bind_impl(iter->second, value) || iter->second.empty();
+    }
+
+    /// <summary>
+    /// Returns an iterator referring to the first header field.
+    /// </summary>
+    /// <returns>An iterator to the beginning of the HTTP headers</returns>
+    iterator begin() { return m_headers.begin(); }
+    const_iterator begin() const { return m_headers.begin(); }
+
+    /// <summary>
+    /// Returns an iterator referring to the past-the-end header field.
+    /// </summary>
+    /// <returns>An iterator to the element past the end of the HTTP headers.</returns>
+    iterator end() { return m_headers.end(); }
+    const_iterator end() const { return m_headers.end(); }
+
+    /// <summary>
+    /// Gets the content length of the message.
+    /// </summary>
+    /// <returns>The length of the content.</returns>
+    _ASYNCRTIMP utility::size64_t content_length() const;
+
+    /// <summary>
+    /// Sets the content length of the message.
+    /// </summary>
+    /// <param name="length">The length of the content.</param>
+    _ASYNCRTIMP void set_content_length(utility::size64_t length);
+
+    /// <summary>
+    /// Gets the content type of the message.
+    /// </summary>
+    /// <returns>The content type of the body.</returns>
+    _ASYNCRTIMP utility::string_t content_type() const;
+
+    /// <summary>
+    /// Sets the content type of the message.
+    /// </summary>
+    /// <param name="type">The content type of the body.</param>
+    _ASYNCRTIMP void set_content_type(utility::string_t type);
+
+    /// <summary>
+    /// Gets the cache control header of the message.
+    /// </summary>
+    /// <returns>The cache control header value.</returns>
+    _ASYNCRTIMP utility::string_t cache_control() const;
+
+    /// <summary>
+    /// Sets the cache control header of the message.
+    /// </summary>
+    /// <param name="control">The cache control header value.</param>
+    _ASYNCRTIMP void set_cache_control(utility::string_t control);
+
+    /// <summary>
+    /// Gets the date header of the message.
+    /// </summary>
+    /// <returns>The date header value.</returns>
+    _ASYNCRTIMP utility::string_t date() const;
+
+    /// <summary>
+    /// Sets the date header of the message.
+    /// </summary>
+    /// <param name="date">The date header value.</param>
+    _ASYNCRTIMP void set_date(const utility::datetime& date);
+
+private:
+    // Headers are stored in a map with case insensitive key.
+    inner_container m_headers;
+};
+} // namespace http
+} // namespace web
diff --git a/Release/include/cpprest/http_listener.h b/Release/include/cpprest/http_listener.h
new file mode 100644 (file)
index 0000000..a5457c0
--- /dev/null
@@ -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.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: HTTP listener (server-side) APIs
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_HTTP_LISTENER_H
+#define CASA_HTTP_LISTENER_H
+
+#include "cpprest/http_msg.h"
+#include <functional>
+#include <limits>
+#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+#include <boost/asio/ssl.hpp>
+#endif
+
+#if !defined(_WIN32) || (_WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)) ||                         \
+    defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+
+namespace web
+{
+namespace http
+{
+/// HTTP listener is currently in beta.
+namespace experimental
+{
+/// HTTP server side library.
+namespace listener
+{
+/// <summary>
+/// Configuration class used to set various options when constructing and http_listener instance.
+/// </summary>
+class http_listener_config
+{
+public:
+    /// <summary>
+    /// Create an http_listener configuration with default options.
+    /// </summary>
+    http_listener_config() : m_timeout(utility::seconds(120)), m_backlog(0) {}
+
+    /// <summary>
+    /// Copy constructor.
+    /// </summary>
+    /// <param name="other">http_listener_config to copy.</param>
+    http_listener_config(const http_listener_config& other)
+        : m_timeout(other.m_timeout)
+        , m_backlog(other.m_backlog)
+#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+        , m_ssl_context_callback(other.m_ssl_context_callback)
+#endif
+    {
+    }
+
+    /// <summary>
+    /// Move constructor.
+    /// <summary>
+    /// <param name="other">http_listener_config to move from.</param>
+    http_listener_config(http_listener_config&& other)
+        : m_timeout(std::move(other.m_timeout))
+        , m_backlog(std::move(other.m_backlog))
+#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+        , m_ssl_context_callback(std::move(other.m_ssl_context_callback))
+#endif
+    {
+    }
+
+    /// <summary>
+    /// Assignment operator.
+    /// </summary>
+    /// <returns>http_listener_config instance.</returns>
+    http_listener_config& operator=(const http_listener_config& rhs)
+    {
+        if (this != &rhs)
+        {
+            m_timeout = rhs.m_timeout;
+            m_backlog = rhs.m_backlog;
+#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+            m_ssl_context_callback = rhs.m_ssl_context_callback;
+#endif
+        }
+        return *this;
+    }
+
+    /// <summary>
+    /// Assignment operator.
+    /// </summary>
+    /// <returns>http_listener_config instance.</returns>
+    http_listener_config& operator=(http_listener_config&& rhs)
+    {
+        if (this != &rhs)
+        {
+            m_timeout = std::move(rhs.m_timeout);
+            m_backlog = std::move(rhs.m_backlog);
+#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+            m_ssl_context_callback = std::move(rhs.m_ssl_context_callback);
+#endif
+        }
+        return *this;
+    }
+
+    /// <summary>
+    /// Get the timeout
+    /// </summary>
+    /// <returns>The timeout (in seconds).</returns>
+    utility::seconds timeout() const { return m_timeout; }
+
+    /// <summary>
+    /// Set the timeout
+    /// </summary>
+    /// <param name="timeout">The timeout (in seconds) used for each send and receive operation on the client.</param>
+    void set_timeout(utility::seconds timeout) { m_timeout = std::move(timeout); }
+
+    /// <summary>
+    /// Get the listen backlog
+    /// </summary>
+    /// <returns>The maximum length of the queue of pending connections, or zero for the implementation
+    /// default.</returns> <remarks>The implementation may not honour this value.</remarks>
+    int backlog() const { return m_backlog; }
+
+    /// <summary>
+    /// Set the listen backlog
+    /// </summary>
+    /// <param name="backlog">The maximum length of the queue of pending connections, or zero for the implementation
+    /// default.</param> <remarks>The implementation may not honour this value.</remarks>
+    void set_backlog(int backlog) { m_backlog = backlog; }
+
+#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+    /// <summary>
+    /// Get the callback of ssl context
+    /// </summary>
+    /// <returns>The function defined by the user of http_listener_config to configure a ssl context.</returns>
+    const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
+    {
+        return m_ssl_context_callback;
+    }
+
+    /// <summary>
+    /// Set the callback of ssl context
+    /// </summary>
+    /// <param name="ssl_context_callback">The function to configure a ssl context which will setup https
+    /// connections.</param>
+    void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& ssl_context_callback)
+    {
+        m_ssl_context_callback = ssl_context_callback;
+    }
+#endif
+
+private:
+    utility::seconds m_timeout;
+    int m_backlog;
+#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+    std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
+#endif
+};
+
+namespace details
+{
+/// <summary>
+/// Internal class for pointer to implementation design pattern.
+/// </summary>
+class http_listener_impl
+{
+public:
+    http_listener_impl() : m_closed(true), m_close_task(pplx::task_from_result()) {}
+
+    _ASYNCRTIMP http_listener_impl(http::uri address);
+    _ASYNCRTIMP http_listener_impl(http::uri address, http_listener_config config);
+
+    _ASYNCRTIMP pplx::task<void> open();
+    _ASYNCRTIMP pplx::task<void> close();
+
+    /// <summary>
+    /// Handler for all requests. The HTTP host uses this to dispatch a message to the pipeline.
+    /// </summary>
+    /// <remarks>Only HTTP server implementations should call this API.</remarks>
+    _ASYNCRTIMP void handle_request(http::http_request msg);
+
+    const http::uri& uri() const { return m_uri; }
+
+    const http_listener_config& configuration() const { return m_config; }
+
+    // Handlers
+    std::function<void(http::http_request)> m_all_requests;
+    std::map<http::method, std::function<void(http::http_request)>> m_supported_methods;
+
+private:
+    // Default implementation for TRACE and OPTIONS.
+    void handle_trace(http::http_request message);
+    void handle_options(http::http_request message);
+
+    // Gets a comma separated string containing the methods supported by this listener.
+    utility::string_t get_supported_methods() const;
+
+    http::uri m_uri;
+    http_listener_config m_config;
+
+    // Used to record that the listener is closed.
+    bool m_closed;
+    pplx::task<void> m_close_task;
+};
+
+} // namespace details
+
+/// <summary>
+/// A class for listening and processing HTTP requests at a specific URI.
+/// </summary>
+class http_listener
+{
+public:
+    /// <summary>
+    /// Create a listener from a URI.
+    /// </summary>
+    /// <remarks>The listener will not have been opened when returned.</remarks>
+    /// <param name="address">URI at which the listener should accept requests.</param>
+    http_listener(http::uri address)
+        : m_impl(utility::details::make_unique<details::http_listener_impl>(std::move(address)))
+    {
+    }
+
+    /// <summary>
+    /// Create a listener with specified URI and configuration.
+    /// </summary>
+    /// <param name="address">URI at which the listener should accept requests.</param>
+    /// <param name="config">Configuration to create listener with.</param>
+    http_listener(http::uri address, http_listener_config config)
+        : m_impl(utility::details::make_unique<details::http_listener_impl>(std::move(address), std::move(config)))
+    {
+    }
+
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    /// <remarks>The resulting listener cannot be used for anything, but is useful to initialize a variable
+    /// that will later be overwritten with a real listener instance.</remarks>
+    http_listener() : m_impl(utility::details::make_unique<details::http_listener_impl>()) {}
+
+    /// <summary>
+    /// Destructor frees any held resources.
+    /// </summary>
+    /// <remarks>Call close() before allowing a listener to be destroyed.</remarks>
+    ~http_listener()
+    {
+        if (m_impl)
+        {
+            // As a safe guard close the listener if not already done.
+            // Users are required to call close, but this is just a safeguard.
+            try
+            {
+                m_impl->close().wait();
+            }
+            catch (...)
+            {
+            }
+        }
+    }
+
+    /// <summary>
+    /// Asynchronously open the listener, i.e. start accepting requests.
+    /// </summary>
+    /// <returns>A task that will be completed once this listener is actually opened, accepting requests.</returns>
+    pplx::task<void> open() { return m_impl->open(); }
+
+    /// <summary>
+    /// Asynchronously stop accepting requests and close all connections.
+    /// </summary>
+    /// <returns>A task that will be completed once this listener is actually closed, no longer accepting
+    /// requests.</returns> <remarks> This function will stop accepting requests and wait for all outstanding handler
+    /// calls to finish before completing the task. Waiting on the task returned from close() within a handler and
+    /// blocking waiting for its result will result in a deadlock.
+    ///
+    /// Call close() before allowing a listener to be destroyed.
+    /// </remarks>
+    pplx::task<void> close() { return m_impl->close(); }
+
+    /// <summary>
+    /// Add a general handler to support all requests.
+    /// </summary>
+    /// <param name="handler">Function object to be called for all requests.</param>
+    void support(const std::function<void(http_request)>& handler) { m_impl->m_all_requests = handler; }
+
+    /// <summary>
+    /// Add support for a specific HTTP method.
+    /// </summary>
+    /// <param name="method">An HTTP method.</param>
+    /// <param name="handler">Function object to be called for all requests for the given HTTP method.</param>
+    void support(const http::method& method, const std::function<void(http_request)>& handler)
+    {
+        m_impl->m_supported_methods[method] = handler;
+    }
+
+    /// <summary>
+    /// Get the URI of the listener.
+    /// </summary>
+    /// <returns>The URI this listener is for.</returns>
+    const http::uri& uri() const { return m_impl->uri(); }
+
+    /// <summary>
+    /// Get the configuration of this listener.
+    /// </summary>
+    /// <returns>Configuration this listener was constructed with.</returns>
+    const http_listener_config& configuration() const { return m_impl->configuration(); }
+
+    /// <summary>
+    /// Move constructor.
+    /// </summary>
+    /// <param name="other">http_listener instance to construct this one from.</param>
+    http_listener(http_listener&& other) : m_impl(std::move(other.m_impl)) {}
+
+    /// <summary>
+    /// Move assignment operator.
+    /// </summary>
+    /// <param name="other">http_listener to replace this one with.</param>
+    http_listener& operator=(http_listener&& other)
+    {
+        if (this != &other)
+        {
+            m_impl = std::move(other.m_impl);
+        }
+        return *this;
+    }
+
+private:
+    // No copying of listeners.
+    http_listener(const http_listener& other);
+    http_listener& operator=(const http_listener& other);
+
+    std::unique_ptr<details::http_listener_impl> m_impl;
+};
+
+} // namespace listener
+} // namespace experimental
+} // namespace http
+} // namespace web
+
+#endif
+#endif
diff --git a/Release/include/cpprest/http_msg.h b/Release/include/cpprest/http_msg.h
new file mode 100644 (file)
index 0000000..50f05ef
--- /dev/null
@@ -0,0 +1,1632 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Request and reply message definitions.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/containerstream.h"
+#include "cpprest/details/cpprest_compat.h"
+#include "cpprest/http_compression.h"
+#include "cpprest/http_headers.h"
+#include "cpprest/json.h"
+#include "cpprest/streams.h"
+#include "cpprest/uri.h"
+#include "pplx/pplxtasks.h"
+#include <map>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <vector>
+
+namespace web
+{
+namespace http
+{
+// URI class has been moved from web::http namespace to web namespace.
+// The below using declarations ensure we don't break existing code.
+// Please use the web::uri class going forward.
+using web::uri;
+using web::uri_builder;
+
+namespace client
+{
+class http_client;
+}
+
+/// <summary>
+/// Represents the HTTP protocol version of a message, as {major, minor}.
+/// </summary>
+struct http_version
+{
+    uint8_t major;
+    uint8_t minor;
+
+    inline bool operator==(const http_version& other) const { return major == other.major && minor == other.minor; }
+    inline bool operator<(const http_version& other) const
+    {
+        return major < other.major || (major == other.major && minor < other.minor);
+    }
+
+    inline bool operator!=(const http_version& other) const { return !(*this == other); }
+    inline bool operator>=(const http_version& other) const { return !(*this < other); }
+    inline bool operator>(const http_version& other) const { return !(*this < other || *this == other); }
+    inline bool operator<=(const http_version& other) const { return *this < other || *this == other; }
+
+    /// <summary>
+    /// Creates <c>http_version</c> from an HTTP-Version string, "HTTP" "/" 1*DIGIT "." 1*DIGIT.
+    /// </summary>
+    /// <returns>Returns a <c>http_version</c> of {0, 0} if not successful.</returns>
+    static _ASYNCRTIMP http_version __cdecl from_string(const std::string& http_version_string);
+
+    /// <summary>
+    /// Returns the string representation of the <c>http_version</c>.
+    /// </summary>
+    _ASYNCRTIMP std::string to_utf8string() const;
+};
+
+/// <summary>
+/// Predefined HTTP protocol versions.
+/// </summary>
+class http_versions
+{
+public:
+    _ASYNCRTIMP static const http_version HTTP_0_9;
+    _ASYNCRTIMP static const http_version HTTP_1_0;
+    _ASYNCRTIMP static const http_version HTTP_1_1;
+};
+
+/// <summary>
+/// Predefined method strings for the standard HTTP methods mentioned in the
+/// HTTP 1.1 specification.
+/// </summary>
+typedef utility::string_t method;
+
+/// <summary>
+/// Common HTTP methods.
+/// </summary>
+class methods
+{
+public:
+#define _METHODS
+#define DAT(a, b) _ASYNCRTIMP const static method a;
+#include "cpprest/details/http_constants.dat"
+#undef _METHODS
+#undef DAT
+};
+
+typedef unsigned short status_code;
+
+/// <summary>
+/// Predefined values for all of the standard HTTP 1.1 response status codes.
+/// </summary>
+class status_codes
+{
+public:
+#define _PHRASES
+#define DAT(a, b, c) const static status_code a = b;
+#include "cpprest/details/http_constants.dat"
+#undef _PHRASES
+#undef DAT
+};
+
+namespace details
+{
+/// <summary>
+/// Constants for MIME types.
+/// </summary>
+class mime_types
+{
+public:
+#define _MIME_TYPES
+#define DAT(a, b) _ASYNCRTIMP const static utility::string_t a;
+#include "cpprest/details/http_constants.dat"
+#undef _MIME_TYPES
+#undef DAT
+};
+
+/// <summary>
+/// Constants for charset types.
+/// </summary>
+class charset_types
+{
+public:
+#define _CHARSET_TYPES
+#define DAT(a, b) _ASYNCRTIMP const static utility::string_t a;
+#include "cpprest/details/http_constants.dat"
+#undef _CHARSET_TYPES
+#undef DAT
+};
+
+} // namespace details
+
+/// Message direction
+namespace message_direction
+{
+/// <summary>
+/// Enumeration used to denote the direction of a message: a request with a body is
+/// an upload, a response with a body is a download.
+/// </summary>
+enum direction
+{
+    upload,
+    download
+};
+} // namespace message_direction
+
+typedef utility::string_t reason_phrase;
+typedef std::function<void(message_direction::direction, utility::size64_t)> progress_handler;
+
+struct http_status_to_phrase
+{
+    unsigned short id;
+    reason_phrase phrase;
+};
+
+/// <summary>
+/// Constants for the HTTP headers mentioned in RFC 2616.
+/// </summary>
+class header_names
+{
+public:
+#define _HEADER_NAMES
+#define DAT(a, b) _ASYNCRTIMP const static utility::string_t a;
+#include "cpprest/details/http_constants.dat"
+#undef _HEADER_NAMES
+#undef DAT
+};
+
+/// <summary>
+/// Represents an HTTP error. This class holds an error message and an optional error code.
+/// </summary>
+class http_exception : public std::exception
+{
+public:
+    /// <summary>
+    /// Creates an <c>http_exception</c> with just a string message and no error code.
+    /// </summary>
+    /// <param name="whatArg">Error message string.</param>
+    http_exception(const utility::string_t& whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {}
+
+#ifdef _WIN32
+    /// <summary>
+    /// Creates an <c>http_exception</c> with just a string message and no error code.
+    /// </summary>
+    /// <param name="whatArg">Error message string.</param>
+    http_exception(std::string whatArg) : m_msg(std::move(whatArg)) {}
+#endif
+
+    /// <summary>
+    /// Creates an <c>http_exception</c> with from a error code using the current platform error category.
+    /// The message of the error code will be used as the what() string message.
+    /// </summary>
+    /// <param name="errorCode">Error code value.</param>
+    http_exception(int errorCode) : m_errorCode(utility::details::create_error_code(errorCode))
+    {
+        m_msg = m_errorCode.message();
+    }
+
+    /// <summary>
+    /// Creates an <c>http_exception</c> with from a error code using the current platform error category.
+    /// </summary>
+    /// <param name="errorCode">Error code value.</param>
+    /// <param name="whatArg">Message to use in what() string.</param>
+    http_exception(int errorCode, const utility::string_t& whatArg)
+        : m_errorCode(utility::details::create_error_code(errorCode))
+        , m_msg(utility::conversions::to_utf8string(whatArg))
+    {
+    }
+
+#ifdef _WIN32
+    /// <summary>
+    /// Creates an <c>http_exception</c> with from a error code using the current platform error category.
+    /// </summary>
+    /// <param name="errorCode">Error code value.</param>
+    /// <param name="whatArg">Message to use in what() string.</param>
+    http_exception(int errorCode, std::string whatArg)
+        : m_errorCode(utility::details::create_error_code(errorCode)), m_msg(std::move(whatArg))
+    {
+    }
+#endif
+
+    /// <summary>
+    /// Creates an <c>http_exception</c> with from a error code and category. The message of the error code will be used
+    /// as the <c>what</c> string message.
+    /// </summary>
+    /// <param name="errorCode">Error code value.</param>
+    /// <param name="cat">Error category for the code.</param>
+    http_exception(int errorCode, const std::error_category& cat) : m_errorCode(std::error_code(errorCode, cat))
+    {
+        m_msg = m_errorCode.message();
+    }
+
+    /// <summary>
+    /// Creates an <c>http_exception</c> with from a error code with a category, and a string message.
+    /// </summary>
+    /// <param name="errorCode">Error code value.</param>
+    /// <param name="whatArg">Error message string.</param>
+    http_exception(std::error_code errorCode, const utility::string_t& whatArg)
+        : m_errorCode(std::move(errorCode)), m_msg(utility::conversions::to_utf8string(whatArg))
+    {
+    }
+
+#ifdef _WIN32
+    /// <summary>
+    /// Creates an <c>http_exception</c> with from a error code with a category, and a string message.
+    /// </summary>
+    /// <param name="errorCode">Error code value.</param>
+    /// <param name="whatArg">Error message string.</param>
+    http_exception(std::error_code errorCode, std::string whatArg)
+        : m_errorCode(std::move(errorCode)), m_msg(std::move(whatArg))
+    {
+    }
+#endif
+
+    /// <summary>
+    /// Gets a string identifying the cause of the exception.
+    /// </summary>
+    /// <returns>A null terminated character string.</returns>
+    const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
+
+    /// <summary>
+    /// Retrieves the underlying error code causing this exception.
+    /// </summary>
+    /// <returns>A std::error_code.</returns>
+    const std::error_code& error_code() const { return m_errorCode; }
+
+private:
+    std::error_code m_errorCode;
+    std::string m_msg;
+};
+
+namespace details
+{
+/// <summary>
+/// Base class for HTTP messages.
+/// This class is to store common functionality so it isn't duplicated on
+/// both the request and response side.
+/// </summary>
+class http_msg_base
+{
+public:
+    friend class http::client::http_client;
+
+    _ASYNCRTIMP http_msg_base();
+
+    virtual ~http_msg_base() {}
+
+    http::http_version http_version() const { return m_http_version; }
+
+    http_headers& headers() { return m_headers; }
+
+    _ASYNCRTIMP void set_body(const concurrency::streams::istream& instream, const utf8string& contentType);
+    _ASYNCRTIMP void set_body(const concurrency::streams::istream& instream, const utf16string& contentType);
+    _ASYNCRTIMP void set_body(const concurrency::streams::istream& instream,
+                              utility::size64_t contentLength,
+                              const utf8string& contentType);
+    _ASYNCRTIMP void set_body(const concurrency::streams::istream& instream,
+                              utility::size64_t contentLength,
+                              const utf16string& contentType);
+
+    /// <summary>
+    /// Helper function for extract functions. Parses the Content-Type header and check to make sure it matches,
+    /// throws an exception if not.
+    /// </summary>
+    /// <param name="ignore_content_type">If true ignores the Content-Type header value.</param>
+    /// <param name="check_content_type">Function to verify additional information on Content-Type.</param>
+    /// <returns>A string containing the charset, an empty string if no Content-Type header is empty.</returns>
+    utility::string_t parse_and_check_content_type(
+        bool ignore_content_type, const std::function<bool(const utility::string_t&)>& check_content_type);
+
+    _ASYNCRTIMP utf8string extract_utf8string(bool ignore_content_type = false);
+    _ASYNCRTIMP utf16string extract_utf16string(bool ignore_content_type = false);
+    _ASYNCRTIMP utility::string_t extract_string(bool ignore_content_type = false);
+
+    _ASYNCRTIMP json::value _extract_json(bool ignore_content_type = false);
+    _ASYNCRTIMP std::vector<unsigned char> _extract_vector();
+
+    virtual _ASYNCRTIMP utility::string_t to_string() const;
+
+    /// <summary>
+    /// Completes this message
+    /// </summary>
+    virtual _ASYNCRTIMP void _complete(utility::size64_t bodySize,
+                                       const std::exception_ptr& exceptionPtr = std::exception_ptr());
+
+    /// <summary>
+    /// Set the stream through which the message body could be read
+    /// </summary>
+    void set_instream(const concurrency::streams::istream& instream) { m_inStream = instream; }
+
+    /// <summary>
+    /// Get the stream through which the message body could be read
+    /// </summary>
+    const concurrency::streams::istream& instream() const { return m_inStream; }
+
+    /// <summary>
+    /// Set the stream through which the message body could be written
+    /// </summary>
+    void set_outstream(const concurrency::streams::ostream& outstream, bool is_default)
+    {
+        m_outStream = outstream;
+        m_default_outstream = is_default;
+    }
+
+    /// <summary>
+    /// Get the stream through which the message body could be written
+    /// </summary>
+    const concurrency::streams::ostream& outstream() const { return m_outStream; }
+
+    /// <summary>
+    /// Sets the compressor for the message body
+    /// </summary>
+    void set_compressor(std::unique_ptr<http::compression::compress_provider> compressor)
+    {
+        m_compressor = std::move(compressor);
+    }
+
+    /// <summary>
+    /// Gets the compressor for the message body, if any
+    /// </summary>
+    std::unique_ptr<http::compression::compress_provider>& compressor() { return m_compressor; }
+
+    /// <summary>
+    /// Sets the collection of factory classes for decompressors for use with the message body
+    /// </summary>
+    void set_decompress_factories(const std::vector<std::shared_ptr<http::compression::decompress_factory>>& factories)
+    {
+        m_decompressors = factories;
+    }
+
+    /// <summary>
+    /// Gets the collection of factory classes for decompressors to be used to decompress the message body, if any
+    /// </summary>
+    const std::vector<std::shared_ptr<http::compression::decompress_factory>>& decompress_factories()
+    {
+        return m_decompressors;
+    }
+
+    const pplx::task_completion_event<utility::size64_t>& _get_data_available() const { return m_data_available; }
+
+    /// <summary>
+    /// Prepare the message with an output stream to receive network data
+    /// </summary>
+    _ASYNCRTIMP void _prepare_to_receive_data();
+
+    /// <summary>
+    /// Determine the remaining input stream length
+    /// </summary>
+    /// <returns>
+    /// std::numeric_limits<size_t>::max() if the stream's remaining length cannot be determined
+    /// length      if the stream's remaining length (which may be 0) can be determined
+    /// </returns>
+    /// <remarks>
+    /// This routine should only be called after a msg (request/response) has been
+    /// completely constructed.
+    /// </remarks>
+    _ASYNCRTIMP size_t _get_stream_length();
+
+    /// <summary>
+    /// Determine the content length
+    /// </summary>
+    /// <returns>
+    /// std::numeric_limits<size_t>::max() if there is content with unknown length (transfer_encoding:chunked)
+    /// 0           if there is no content
+    /// length      if there is content with known length
+    /// </returns>
+    /// <remarks>
+    /// This routine should only be called after a msg (request/response) has been
+    /// completely constructed.
+    /// </remarks>
+    _ASYNCRTIMP size_t _get_content_length();
+
+    /// <summary>
+    /// Determine the content length, and, if necessary, manage compression in the Transfer-Encoding header
+    /// </summary>
+    /// <returns>
+    /// std::numeric_limits<size_t>::max() if there is content with unknown length (transfer_encoding:chunked)
+    /// 0           if there is no content
+    /// length      if there is content with known length
+    /// </returns>
+    /// <remarks>
+    /// This routine is like _get_content_length, except that it adds a compression algorithm to
+    /// the Trasfer-Length header if compression is configured.  It throws if a Transfer-Encoding
+    /// header exists and does not match the one it generated.
+    /// </remarks>
+    _ASYNCRTIMP size_t _get_content_length_and_set_compression();
+
+    void _set_http_version(const http::http_version& http_version) { m_http_version = http_version; }
+
+protected:
+    std::unique_ptr<http::compression::compress_provider> m_compressor;
+    std::unique_ptr<http::compression::decompress_provider> m_decompressor;
+    std::vector<std::shared_ptr<http::compression::decompress_factory>> m_decompressors;
+
+    /// <summary>
+    /// Stream to read the message body.
+    /// By default this is an invalid stream. The user could set the instream on
+    /// a request by calling set_request_stream(...). This would also be set when
+    /// set_body() is called - a stream from the body is constructed and set.
+    /// Even in the presence of msg body this stream could be invalid. An example
+    /// would be when the user sets an ostream for the response. With that API the
+    /// user does not provide the ability to read the msg body.
+    /// Thus m_instream is valid when there is a msg body and it can actually be read
+    /// </summary>
+    concurrency::streams::istream m_inStream;
+
+    /// <summary>
+    /// stream to write the msg body
+    /// By default this is an invalid stream. The user could set this on the response
+    /// (for http_client). In all the other cases we would construct one to transfer
+    /// the data from the network into the message body.
+    /// </summary>
+    concurrency::streams::ostream m_outStream;
+
+    http::http_version m_http_version;
+    http_headers m_headers;
+    bool m_default_outstream;
+
+    /// <summary> The TCE is used to signal the availability of the message body. </summary>
+    pplx::task_completion_event<utility::size64_t> m_data_available;
+
+    size_t _get_content_length(bool honor_compression);
+};
+
+/// <summary>
+/// Base structure for associating internal server information
+/// with an HTTP request/response.
+/// </summary>
+class _http_server_context
+{
+public:
+    _http_server_context() {}
+    virtual ~_http_server_context() {}
+
+private:
+};
+
+/// <summary>
+/// Internal representation of an HTTP response.
+/// </summary>
+class _http_response final : public http::details::http_msg_base
+{
+public:
+    _http_response() : m_status_code((std::numeric_limits<uint16_t>::max)()) {}
+
+    _http_response(http::status_code code) : m_status_code(code) {}
+
+    http::status_code status_code() const { return m_status_code; }
+
+    void set_status_code(http::status_code code) { m_status_code = code; }
+
+    const http::reason_phrase& reason_phrase() const { return m_reason_phrase; }
+
+    void set_reason_phrase(const http::reason_phrase& reason) { m_reason_phrase = reason; }
+
+    _ASYNCRTIMP utility::string_t to_string() const;
+
+    _http_server_context* _get_server_context() const { return m_server_context.get(); }
+
+    void _set_server_context(std::unique_ptr<details::_http_server_context> server_context)
+    {
+        m_server_context = std::move(server_context);
+    }
+
+private:
+    std::unique_ptr<_http_server_context> m_server_context;
+
+    http::status_code m_status_code;
+    http::reason_phrase m_reason_phrase;
+};
+
+} // namespace details
+
+/// <summary>
+/// Represents an HTTP response.
+/// </summary>
+class http_response
+{
+public:
+    /// <summary>
+    /// Constructs a response with an empty status code, no headers, and no body.
+    /// </summary>
+    /// <returns>A new HTTP response.</returns>
+    http_response() : _m_impl(std::make_shared<details::_http_response>()) {}
+
+    /// <summary>
+    /// Constructs a response with given status code, no headers, and no body.
+    /// </summary>
+    /// <param name="code">HTTP status code to use in response.</param>
+    /// <returns>A new HTTP response.</returns>
+    http_response(http::status_code code) : _m_impl(std::make_shared<details::_http_response>(code)) {}
+
+    /// <summary>
+    /// Gets the status code of the response message.
+    /// </summary>
+    /// <returns>status code.</returns>
+    http::status_code status_code() const { return _m_impl->status_code(); }
+
+    /// <summary>
+    /// Sets the status code of the response message.
+    /// </summary>
+    /// <param name="code">Status code to set.</param>
+    /// <remarks>
+    /// This will overwrite any previously set status code.
+    /// </remarks>
+    void set_status_code(http::status_code code) const { _m_impl->set_status_code(code); }
+
+    /// <summary>
+    /// Gets the reason phrase of the response message.
+    /// If no reason phrase is set it will default to the standard one corresponding to the status code.
+    /// </summary>
+    /// <returns>Reason phrase.</returns>
+    const http::reason_phrase& reason_phrase() const { return _m_impl->reason_phrase(); }
+
+    /// <summary>
+    /// Sets the reason phrase of the response message.
+    /// If no reason phrase is set it will default to the standard one corresponding to the status code.
+    /// </summary>
+    /// <param name="reason">The reason phrase to set.</param>
+    void set_reason_phrase(const http::reason_phrase& reason) const { _m_impl->set_reason_phrase(reason); }
+
+    /// <summary>
+    /// Gets the headers of the response message.
+    /// </summary>
+    /// <returns>HTTP headers for this response.</returns>
+    /// <remarks>
+    /// Use the <seealso cref="http_headers::add Method"/> to fill in desired headers.
+    /// </remarks>
+    http_headers& headers() { return _m_impl->headers(); }
+
+    /// <summary>
+    /// Gets a const reference to the headers of the response message.
+    /// </summary>
+    /// <returns>HTTP headers for this response.</returns>
+    const http_headers& headers() const { return _m_impl->headers(); }
+
+    /// <summary>
+    /// Generates a string representation of the message, including the body when possible.
+    /// Mainly this should be used for debugging purposes as it has to copy the
+    /// message body and doesn't have excellent performance.
+    /// </summary>
+    /// <returns>A string representation of this HTTP request.</returns>
+    /// <remarks>Note this function is synchronous and doesn't wait for the
+    /// entire message body to arrive. If the message body has arrived by the time this
+    /// function is called and it is has a textual Content-Type it will be included.
+    /// Otherwise just the headers will be present.</remarks>
+    utility::string_t to_string() const { return _m_impl->to_string(); }
+
+    /// <summary>
+    /// Extracts the body of the response message as a string value, checking that the content type is a MIME text type.
+    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
+    /// </summary>
+    /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes text.</param>
+    /// <returns>String containing body of the message.</returns>
+    pplx::task<utility::string_t> extract_string(bool ignore_content_type = false) const
+    {
+        auto impl = _m_impl;
+        return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
+            return impl->extract_string(ignore_content_type);
+        });
+    }
+
+    /// <summary>
+    /// Extracts the body of the response message as a UTF-8 string value, checking that the content type is a MIME text
+    /// type. A body can only be extracted once because in some cases an optimization is made where the data is 'moved'
+    /// out.
+    /// </summary>
+    /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes text.</param>
+    /// <returns>String containing body of the message.</returns>
+    pplx::task<utf8string> extract_utf8string(bool ignore_content_type = false) const
+    {
+        auto impl = _m_impl;
+        return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
+            return impl->extract_utf8string(ignore_content_type);
+        });
+    }
+
+    /// <summary>
+    /// Extracts the body of the response message as a UTF-16 string value, checking that the content type is a MIME
+    /// text type. A body can only be extracted once because in some cases an optimization is made where the data is
+    /// 'moved' out.
+    /// </summary>
+    /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes text.</param>
+    /// <returns>String containing body of the message.</returns>
+    pplx::task<utf16string> extract_utf16string(bool ignore_content_type = false) const
+    {
+        auto impl = _m_impl;
+        return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
+            return impl->extract_utf16string(ignore_content_type);
+        });
+    }
+
+    /// <summary>
+    /// Extracts the body of the response message into a json value, checking that the content type is application/json.
+    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
+    /// </summary>
+    /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes json.</param>
+    /// <returns>JSON value from the body of this message.</returns>
+    pplx::task<json::value> extract_json(bool ignore_content_type = false) const
+    {
+        auto impl = _m_impl;
+        return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
+            return impl->_extract_json(ignore_content_type);
+        });
+    }
+
+    /// <summary>
+    /// Extracts the body of the response message into a vector of bytes.
+    /// </summary>
+    /// <returns>The body of the message as a vector of bytes.</returns>
+    pplx::task<std::vector<unsigned char>> extract_vector() const
+    {
+        auto impl = _m_impl;
+        return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) {
+            return impl->_extract_vector();
+        });
+    }
+
+    /// <summary>
+    /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
+    /// the character encoding of the string is UTF-8.
+    /// </summary>
+    /// <param name="body_text">String containing body text.</param>
+    /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
+    /// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
+    /// </remarks>
+    void set_body(utf8string&& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
+    {
+        const auto length = body_text.size();
+        _m_impl->set_body(
+            concurrency::streams::bytestream::open_istream<std::string>(std::move(body_text)), length, content_type);
+    }
+
+    /// <summary>
+    /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
+    /// the character encoding of the string is UTF-8.
+    /// </summary>
+    /// <param name="body_text">String containing body text.</param>
+    /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
+    /// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
+    /// </remarks>
+    void set_body(const utf8string& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
+    {
+        _m_impl->set_body(
+            concurrency::streams::bytestream::open_istream<std::string>(body_text), body_text.size(), content_type);
+    }
+
+    /// <summary>
+    /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
+    /// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
+    /// </summary>
+    /// <param name="body_text">String containing body text.</param>
+    /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain".</param>
+    /// <remarks>
+    /// This will overwrite any previously set body data and "Content-Type" header.
+    /// </remarks>
+    void set_body(const utf16string& body_text,
+                  utf16string content_type = utility::conversions::to_utf16string("text/plain"))
+    {
+        if (content_type.find(::utility::conversions::to_utf16string("charset=")) != content_type.npos)
+        {
+            throw std::invalid_argument("content_type can't contain a 'charset'.");
+        }
+
+        auto utf8body = utility::conversions::utf16_to_utf8(body_text);
+        auto length = utf8body.size();
+        _m_impl->set_body(concurrency::streams::bytestream::open_istream<std::string>(std::move(utf8body)),
+                          length,
+                          std::move(content_type.append(::utility::conversions::to_utf16string("; charset=utf-8"))));
+    }
+
+    /// <summary>
+    /// Sets the body of the message to contain json value. If the 'Content-Type'
+    /// header hasn't already been set it will be set to 'application/json'.
+    /// </summary>
+    /// <param name="body_text">json value.</param>
+    /// <remarks>
+    /// This will overwrite any previously set body data.
+    /// </remarks>
+    void set_body(const json::value& body_data)
+    {
+        auto body_text = utility::conversions::to_utf8string(body_data.serialize());
+        auto length = body_text.size();
+        set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)),
+                 length,
+                 _XPLATSTR("application/json"));
+    }
+
+    /// <summary>
+    /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
+    /// header hasn't already been set it will be set to 'application/octet-stream'.
+    /// </summary>
+    /// <param name="body_data">Vector containing body data.</param>
+    /// <remarks>
+    /// This will overwrite any previously set body data.
+    /// </remarks>
+    void set_body(std::vector<unsigned char>&& body_data)
+    {
+        auto length = body_data.size();
+        set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)), length);
+    }
+
+    /// <summary>
+    /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
+    /// header hasn't already been set it will be set to 'application/octet-stream'.
+    /// </summary>
+    /// <param name="body_data">Vector containing body data.</param>
+    /// <remarks>
+    /// This will overwrite any previously set body data.
+    /// </remarks>
+    void set_body(const std::vector<unsigned char>& body_data)
+    {
+        set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size());
+    }
+
+    /// <summary>
+    /// Defines a stream that will be relied on to provide the body of the HTTP message when it is
+    /// sent.
+    /// </summary>
+    /// <param name="stream">A readable, open asynchronous stream.</param>
+    /// <param name="content_type">A string holding the MIME type of the message body.</param>
+    /// <remarks>
+    /// This cannot be used in conjunction with any external means of setting the body of the request.
+    /// The stream will not be read until the message is sent.
+    /// </remarks>
+    void set_body(const concurrency::streams::istream& stream,
+                  const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
+    {
+        _m_impl->set_body(stream, content_type);
+    }
+
+    /// <summary>
+    /// Defines a stream that will be relied on to provide the body of the HTTP message when it is
+    /// sent.
+    /// </summary>
+    /// <param name="stream">A readable, open asynchronous stream.</param>
+    /// <param name="content_length">The size of the data to be sent in the body.</param>
+    /// <param name="content_type">A string holding the MIME type of the message body.</param>
+    /// <remarks>
+    /// This cannot be used in conjunction with any external means of setting the body of the request.
+    /// The stream will not be read until the message is sent.
+    /// </remarks>
+    void set_body(const concurrency::streams::istream& stream,
+                  utility::size64_t content_length,
+                  const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
+    {
+        _m_impl->set_body(stream, content_length, content_type);
+    }
+
+    /// <summary>
+    /// Produces a stream which the caller may use to retrieve data from an incoming request.
+    /// </summary>
+    /// <returns>A readable, open asynchronous stream.</returns>
+    /// <remarks>
+    /// This cannot be used in conjunction with any other means of getting the body of the request.
+    /// It is not necessary to wait until the message has been sent before starting to write to the
+    /// stream, but it is advisable to do so, since it will allow the network I/O to start earlier
+    /// and the work of sending data can be overlapped with the production of more data.
+    /// </remarks>
+    concurrency::streams::istream body() const { return _m_impl->instream(); }
+
+    /// <summary>
+    /// Signals the user (client) when all the data for this response message has been received.
+    /// </summary>
+    /// <returns>A <c>task</c> which is completed when all of the response body has been received.</returns>
+    pplx::task<http::http_response> content_ready() const
+    {
+        http_response resp = *this;
+        return pplx::create_task(_m_impl->_get_data_available()).then([resp](utility::size64_t) mutable {
+            return resp;
+        });
+    }
+
+    std::shared_ptr<http::details::_http_response> _get_impl() const { return _m_impl; }
+
+    http::details::_http_server_context* _get_server_context() const { return _m_impl->_get_server_context(); }
+    void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)
+    {
+        _m_impl->_set_server_context(std::move(server_context));
+    }
+
+private:
+    std::shared_ptr<http::details::_http_response> _m_impl;
+};
+
+namespace details
+{
+/// <summary>
+/// Internal representation of an HTTP request message.
+/// </summary>
+class _http_request final : public http::details::http_msg_base, public std::enable_shared_from_this<_http_request>
+{
+public:
+    _ASYNCRTIMP _http_request(http::method mtd);
+
+    _ASYNCRTIMP _http_request(std::unique_ptr<http::details::_http_server_context> server_context);
+
+    virtual ~_http_request() {}
+
+    http::method& method() { return m_method; }
+
+    uri& request_uri() { return m_uri; }
+
+    _ASYNCRTIMP uri absolute_uri() const;
+
+    _ASYNCRTIMP uri relative_uri() const;
+
+    _ASYNCRTIMP void set_request_uri(const uri&);
+
+    const utility::string_t& remote_address() const { return m_remote_address; }
+
+    const pplx::cancellation_token& cancellation_token() const { return m_cancellationToken; }
+
+    void set_cancellation_token(const pplx::cancellation_token& token) { m_cancellationToken = token; }
+
+    _ASYNCRTIMP utility::string_t to_string() const;
+
+    _ASYNCRTIMP pplx::task<void> reply(const http_response& response);
+
+    pplx::task<http_response> get_response() { return pplx::task<http_response>(m_response); }
+
+    _ASYNCRTIMP pplx::task<void> _reply_if_not_already(http::status_code status);
+
+    void set_response_stream(const concurrency::streams::ostream& stream) { m_response_stream = stream; }
+
+    void set_progress_handler(const progress_handler& handler)
+    {
+        m_progress_handler = std::make_shared<progress_handler>(handler);
+    }
+
+    const concurrency::streams::ostream& _response_stream() const { return m_response_stream; }
+
+    const std::shared_ptr<progress_handler>& _progress_handler() const { return m_progress_handler; }
+
+    http::details::_http_server_context* _get_server_context() const { return m_server_context.get(); }
+
+    void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)
+    {
+        m_server_context = std::move(server_context);
+    }
+
+    void _set_listener_path(const utility::string_t& path) { m_listener_path = path; }
+
+    void _set_base_uri(const http::uri& base_uri) { m_base_uri = base_uri; }
+
+    void _set_remote_address(const utility::string_t& remote_address) { m_remote_address = remote_address; }
+
+private:
+    // Actual initiates sending the response, without checking if a response has already been sent.
+    pplx::task<void> _reply_impl(http_response response);
+
+    http::method m_method;
+
+    // Tracks whether or not a response has already been started for this message.
+    // 0 = No reply sent
+    // 1 = Usual reply sent
+    // 2 = Reply aborted by another thread; e.g. server shutdown
+    pplx::details::atomic_long m_initiated_response;
+
+    std::unique_ptr<http::details::_http_server_context> m_server_context;
+
+    pplx::cancellation_token m_cancellationToken;
+
+    http::uri m_base_uri;
+    http::uri m_uri;
+    utility::string_t m_listener_path;
+
+    concurrency::streams::ostream m_response_stream;
+
+    std::shared_ptr<progress_handler> m_progress_handler;
+
+    pplx::task_completion_event<http_response> m_response;
+
+    utility::string_t m_remote_address;
+};
+
+} // namespace details
+
+/// <summary>
+/// Represents an HTTP request.
+/// </summary>
+class http_request
+{
+public:
+    /// <summary>
+    /// Constructs a new HTTP request with the 'GET' method.
+    /// </summary>
+    http_request() : _m_impl(std::make_shared<http::details::_http_request>(methods::GET)) {}
+
+    /// <summary>
+    /// Constructs a new HTTP request with the given request method.
+    /// </summary>
+    /// <param name="mtd">Request method.</param>
+    http_request(http::method mtd) : _m_impl(std::make_shared<http::details::_http_request>(std::move(mtd))) {}
+
+    /// <summary>
+    /// Destructor frees any held resources.
+    /// </summary>
+    ~http_request() {}
+
+    /// <summary>
+    /// Get the method (GET/PUT/POST/DELETE) of the request message.
+    /// </summary>
+    /// <returns>Request method of this HTTP request.</returns>
+    const http::method& method() const { return _m_impl->method(); }
+
+    /// <summary>
+    /// Set the method (GET/PUT/POST/DELETE) of the request message.
+    /// </summary>
+    /// <param name="method">Request method of this HTTP request.</param>
+    void set_method(const http::method& method) const { _m_impl->method() = method; }
+
+    /// <summary>
+    /// Get the underling URI of the request message.
+    /// </summary>
+    /// <returns>The uri of this message.</returns>
+    const uri& request_uri() const { return _m_impl->request_uri(); }
+
+    /// <summary>
+    /// Set the underling URI of the request message.
+    /// </summary>
+    /// <param name="uri">The uri for this message.</param>
+    void set_request_uri(const uri& uri) { return _m_impl->set_request_uri(uri); }
+
+    /// <summary>
+    /// Gets a reference the URI path, query, and fragment part of this request message.
+    /// This will be appended to the base URI specified at construction of the http_client.
+    /// </summary>
+    /// <returns>A string.</returns>
+    /// <remarks>When the request is the one passed to a listener's handler, the
+    /// relative URI is the request URI less the listener's path. In all other circumstances,
+    /// request_uri() and relative_uri() will return the same value.
+    /// </remarks>
+    uri relative_uri() const { return _m_impl->relative_uri(); }
+
+    /// <summary>
+    /// Get an absolute URI with scheme, host, port, path, query, and fragment part of
+    /// the request message.
+    /// </summary>
+    /// <remarks>Absolute URI is only valid after this http_request object has been passed
+    /// to http_client::request().
+    /// </remarks>
+    uri absolute_uri() const { return _m_impl->absolute_uri(); }
+
+    /// <summary>
+    /// Gets a reference to the headers of the response message.
+    /// </summary>
+    /// <returns>HTTP headers for this response.</returns>
+    /// <remarks>
+    /// Use the http_headers::add to fill in desired headers.
+    /// </remarks>
+    http_headers& headers() { return _m_impl->headers(); }
+
+    /// <summary>
+    /// Gets a const reference to the headers of the response message.
+    /// </summary>
+    /// <returns>HTTP headers for this response.</returns>
+    /// <remarks>
+    /// Use the http_headers::add to fill in desired headers.
+    /// </remarks>
+    const http_headers& headers() const { return _m_impl->headers(); }
+
+    /// <summary>
+    /// Returns the HTTP protocol version of this request message.
+    /// </summary>
+    /// <returns>The HTTP protocol version.</returns>
+    http::http_version http_version() const { return _m_impl->http_version(); }
+
+    /// <summary>
+    /// Returns a string representation of the remote IP address.
+    /// </summary>
+    /// <returns>The remote IP address.</returns>
+    const utility::string_t& remote_address() const { return _m_impl->remote_address(); }
+
+    CASABLANCA_DEPRECATED("Use `remote_address()` instead.")
+    const utility::string_t& get_remote_address() const { return _m_impl->remote_address(); }
+
+    /// <summary>
+    /// Extract the body of the request message as a string value, checking that the content type is a MIME text type.
+    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
+    /// </summary>
+    /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-8.</param>
+    /// <returns>String containing body of the message.</returns>
+    pplx::task<utility::string_t> extract_string(bool ignore_content_type = false)
+    {
+        auto impl = _m_impl;
+        return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
+            return impl->extract_string(ignore_content_type);
+        });
+    }
+
+    /// <summary>
+    /// Extract the body of the request message as a UTF-8 string value, checking that the content type is a MIME text
+    /// type. A body can only be extracted once because in some cases an optimization is made where the data is 'moved'
+    /// out.
+    /// </summary>
+    /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-8.</param>
+    /// <returns>String containing body of the message.</returns>
+    pplx::task<utf8string> extract_utf8string(bool ignore_content_type = false)
+    {
+        auto impl = _m_impl;
+        return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
+            return impl->extract_utf8string(ignore_content_type);
+        });
+    }
+
+    /// <summary>
+    /// Extract the body of the request message as a UTF-16 string value, checking that the content type is a MIME text
+    /// type. A body can only be extracted once because in some cases an optimization is made where the data is 'moved'
+    /// out.
+    /// </summary>
+    /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-16.</param>
+    /// <returns>String containing body of the message.</returns>
+    pplx::task<utf16string> extract_utf16string(bool ignore_content_type = false)
+    {
+        auto impl = _m_impl;
+        return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
+            return impl->extract_utf16string(ignore_content_type);
+        });
+    }
+
+    /// <summary>
+    /// Extracts the body of the request message into a json value, checking that the content type is application/json.
+    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
+    /// </summary>
+    /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-8.</param>
+    /// <returns>JSON value from the body of this message.</returns>
+    pplx::task<json::value> extract_json(bool ignore_content_type = false) const
+    {
+        auto impl = _m_impl;
+        return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
+            return impl->_extract_json(ignore_content_type);
+        });
+    }
+
+    /// <summary>
+    /// Extract the body of the response message into a vector of bytes. Extracting a vector can be done on
+    /// </summary>
+    /// <returns>The body of the message as a vector of bytes.</returns>
+    pplx::task<std::vector<unsigned char>> extract_vector() const
+    {
+        auto impl = _m_impl;
+        return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) {
+            return impl->_extract_vector();
+        });
+    }
+
+    /// <summary>
+    /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
+    /// the character encoding of the string is UTF-8.
+    /// </summary>
+    /// <param name="body_text">String containing body text.</param>
+    /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
+    /// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
+    /// </remarks>
+    void set_body(utf8string&& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
+    {
+        const auto length = body_text.size();
+        _m_impl->set_body(
+            concurrency::streams::bytestream::open_istream<std::string>(std::move(body_text)), length, content_type);
+    }
+
+    /// <summary>
+    /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
+    /// the character encoding of the string is UTF-8.
+    /// </summary>
+    /// <param name="body_text">String containing body text.</param>
+    /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
+    /// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
+    /// </remarks>
+    void set_body(const utf8string& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
+    {
+        _m_impl->set_body(
+            concurrency::streams::bytestream::open_istream<std::string>(body_text), body_text.size(), content_type);
+    }
+
+    /// <summary>
+    /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
+    /// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
+    /// </summary>
+    /// </summary>
+    /// <param name="body_text">String containing body text.</param>
+    /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain".</param>
+    /// <remarks>
+    /// This will overwrite any previously set body data and "Content-Type" header.
+    /// </remarks>
+    void set_body(const utf16string& body_text,
+                  utf16string content_type = utility::conversions::to_utf16string("text/plain"))
+    {
+        if (content_type.find(::utility::conversions::to_utf16string("charset=")) != content_type.npos)
+        {
+            throw std::invalid_argument("content_type can't contain a 'charset'.");
+        }
+
+        auto utf8body = utility::conversions::utf16_to_utf8(body_text);
+        auto length = utf8body.size();
+        _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(utf8body)),
+                          length,
+                          std::move(content_type.append(::utility::conversions::to_utf16string("; charset=utf-8"))));
+    }
+
+    /// <summary>
+    /// Sets the body of the message to contain json value. If the 'Content-Type'
+    /// header hasn't already been set it will be set to 'application/json'.
+    /// </summary>
+    /// <param name="body_data">json value.</param>
+    /// <remarks>
+    /// This will overwrite any previously set body data.
+    /// </remarks>
+    void set_body(const json::value& body_data)
+    {
+        auto body_text = utility::conversions::to_utf8string(body_data.serialize());
+        auto length = body_text.size();
+        _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)),
+                          length,
+                          _XPLATSTR("application/json"));
+    }
+
+    /// <summary>
+    /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
+    /// header hasn't already been set it will be set to 'application/octet-stream'.
+    /// </summary>
+    /// <param name="body_data">Vector containing body data.</param>
+    /// <remarks>
+    /// This will overwrite any previously set body data.
+    /// </remarks>
+    void set_body(std::vector<unsigned char>&& body_data)
+    {
+        auto length = body_data.size();
+        _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)),
+                          length,
+                          _XPLATSTR("application/octet-stream"));
+    }
+
+    /// <summary>
+    /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
+    /// header hasn't already been set it will be set to 'application/octet-stream'.
+    /// </summary>
+    /// <param name="body_data">Vector containing body data.</param>
+    /// <remarks>
+    /// This will overwrite any previously set body data.
+    /// </remarks>
+    void set_body(const std::vector<unsigned char>& body_data)
+    {
+        set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size());
+    }
+
+    /// <summary>
+    /// Defines a stream that will be relied on to provide the body of the HTTP message when it is
+    /// sent.
+    /// </summary>
+    /// <param name="stream">A readable, open asynchronous stream.</param>
+    /// <param name="content_type">A string holding the MIME type of the message body.</param>
+    /// <remarks>
+    /// This cannot be used in conjunction with any other means of setting the body of the request.
+    /// The stream will not be read until the message is sent.
+    /// </remarks>
+    void set_body(const concurrency::streams::istream& stream,
+                  const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
+    {
+        _m_impl->set_body(stream, content_type);
+    }
+
+    /// <summary>
+    /// Defines a stream that will be relied on to provide the body of the HTTP message when it is
+    /// sent.
+    /// </summary>
+    /// <param name="stream">A readable, open asynchronous stream.</param>
+    /// <param name="content_length">The size of the data to be sent in the body.</param>
+    /// <param name="content_type">A string holding the MIME type of the message body.</param>
+    /// <remarks>
+    /// This cannot be used in conjunction with any other means of setting the body of the request.
+    /// The stream will not be read until the message is sent.
+    /// </remarks>
+    void set_body(const concurrency::streams::istream& stream,
+                  utility::size64_t content_length,
+                  const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
+    {
+        _m_impl->set_body(stream, content_length, content_type);
+    }
+
+    /// <summary>
+    /// Produces a stream which the caller may use to retrieve data from an incoming request.
+    /// </summary>
+    /// <returns>A readable, open asynchronous stream.</returns>
+    /// <remarks>
+    /// This cannot be used in conjunction with any other means of getting the body of the request.
+    /// It is not necessary to wait until the message has been sent before starting to write to the
+    /// stream, but it is advisable to do so, since it will allow the network I/O to start earlier
+    /// and the work of sending data can be overlapped with the production of more data.
+    /// </remarks>
+    concurrency::streams::istream body() const { return _m_impl->instream(); }
+
+    /// <summary>
+    /// Defines a stream that will be relied on to hold the body of the HTTP response message that
+    /// results from the request.
+    /// </summary>
+    /// <param name="stream">A writable, open asynchronous stream.</param>
+    /// <remarks>
+    /// If this function is called, the body of the response should not be accessed in any other
+    /// way.
+    /// </remarks>
+    void set_response_stream(const concurrency::streams::ostream& stream)
+    {
+        return _m_impl->set_response_stream(stream);
+    }
+
+    /// <summary>
+    /// Sets a compressor that will be used to compress the body of the HTTP message as it is sent.
+    /// </summary>
+    /// <param name="compressor">A pointer to an instantiated compressor of the desired type.</param>
+    /// <remarks>
+    /// This cannot be used in conjunction with any external means of compression.  The Transfer-Encoding
+    /// header will be managed internally, and must not be set by the client.
+    /// </remarks>
+    void set_compressor(std::unique_ptr<http::compression::compress_provider> compressor)
+    {
+        return _m_impl->set_compressor(std::move(compressor));
+    }
+
+    /// <summary>
+    /// Sets a compressor that will be used to compress the body of the HTTP message as it is sent.
+    /// </summary>
+    /// <param name="algorithm">The built-in compression algorithm to use.</param>
+    /// <returns>
+    /// True if a built-in compressor was instantiated, otherwise false.
+    /// </returns>
+    /// <remarks>
+    /// This cannot be used in conjunction with any external means of compression.  The Transfer-Encoding
+    /// header will be managed internally, and must not be set by the client.
+    /// </remarks>
+    bool set_compressor(utility::string_t algorithm)
+    {
+        _m_impl->set_compressor(http::compression::builtin::make_compressor(algorithm));
+        return (bool)_m_impl->compressor();
+    }
+
+    /// <summary>
+    /// Gets the compressor to be used to compress the message body, if any.
+    /// </summary>
+    /// <returns>
+    /// The compressor itself.
+    /// </returns>
+    std::unique_ptr<http::compression::compress_provider>& compressor() { return _m_impl->compressor(); }
+
+    /// <summary>
+    /// Sets the default collection of built-in factory classes for decompressors that may be used to
+    /// decompress the body of the HTTP message as it is received, effectively enabling decompression.
+    /// </summary>
+    /// <param name="factories">The collection of factory classes for allowable decompressors. The
+    /// supplied vector itself need not remain valid after the call returns.</param>
+    /// <remarks>
+    /// This default collection is implied if request_compressed_response() is set in the associated
+    /// <c>client::http_client_config</c> and neither overload of this method has been called.
+    ///
+    /// This cannot be used in conjunction with any external means of decompression.  The TE and Accept-Encoding
+    /// headers must not be set by the client, as they will be managed internally as appropriate.
+    /// </remarks>
+    _ASYNCRTIMP void set_decompress_factories();
+
+    /// <summary>
+    /// Sets a collection of factory classes for decompressors that may be used to decompress the
+    /// body of the HTTP message as it is received, effectively enabling decompression.
+    /// </summary>
+    /// <remarks>
+    /// If set, this collection takes the place of the built-in compression providers.  It may contain
+    /// custom factory classes and/or factory classes for built-in providers, and may be used to adjust
+    /// the weights of the built-in providers, which default to 500 (i.e. "q=0.500").
+    ///
+    /// This cannot be used in conjunction with any external means of decompression.  The TE and Accept-Encoding
+    /// headers must not be set by the client, as they will be managed internally as appropriate.
+    /// </remarks>
+    void set_decompress_factories(const std::vector<std::shared_ptr<http::compression::decompress_factory>>& factories)
+    {
+        return _m_impl->set_decompress_factories(factories);
+    }
+
+    /// <summary>
+    /// Gets the collection of factory classes for decompressors to be used to decompress the message body, if any.
+    /// </summary>
+    /// <returns>
+    /// The collection of factory classes itself.
+    /// </returns>
+    /// <remarks>
+    /// This cannot be used in conjunction with any external means of decompression.  The TE
+    /// header must not be set by the client, as it will be managed internally.
+    /// </remarks>
+    const std::vector<std::shared_ptr<http::compression::decompress_factory>>& decompress_factories() const
+    {
+        return _m_impl->decompress_factories();
+    }
+
+    /// <summary>
+    /// Defines a callback function that will be invoked for every chunk of data uploaded or downloaded
+    /// as part of the request.
+    /// </summary>
+    /// <param name="handler">A function representing the progress handler. It's parameters are:
+    ///    up:       a <c>message_direction::direction</c> value  indicating the direction of the message
+    ///              that is being reported.
+    ///    progress: the number of bytes that have been processed so far.
+    /// </param>
+    /// <remarks>
+    ///   This function will be called at least once for upload and at least once for
+    ///   the download body, unless there is some exception generated. An HTTP message with an error
+    ///   code is not an exception. This means, that even if there is no body, the progress handler
+    ///   will be called.
+    ///
+    ///   Setting the chunk size on the http_client does not guarantee that the client will be using
+    ///   exactly that increment for uploading and downloading data.
+    ///
+    ///   The handler will be called only once for each combination of argument values, in order. Depending
+    ///   on how a service responds, some download values may come before all upload values have been
+    ///   reported.
+    ///
+    ///   The progress handler will be called on the thread processing the request. This means that
+    ///   the implementation of the handler must take care not to block the thread or do anything
+    ///   that takes significant amounts of time. In particular, do not do any kind of I/O from within
+    ///   the handler, do not update user interfaces, and to not acquire any locks. If such activities
+    ///   are necessary, it is the handler's responsibility to execute that work on a separate thread.
+    /// </remarks>
+    void set_progress_handler(const progress_handler& handler) { return _m_impl->set_progress_handler(handler); }
+
+    /// <summary>
+    /// Asynchronously responses to this HTTP request.
+    /// </summary>
+    /// <param name="response">Response to send.</param>
+    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
+    pplx::task<void> reply(const http_response& response) const { return _m_impl->reply(response); }
+
+    /// <summary>
+    /// Asynchronously responses to this HTTP request.
+    /// </summary>
+    /// <param name="status">Response status code.</param>
+    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
+    pplx::task<void> reply(http::status_code status) const { return reply(http_response(status)); }
+
+    /// <summary>
+    /// Responds to this HTTP request.
+    /// </summary>
+    /// <param name="status">Response status code.</param>
+    /// <param name="body_data">Json value to use in the response body.</param>
+    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
+    pplx::task<void> reply(http::status_code status, const json::value& body_data) const
+    {
+        http_response response(status);
+        response.set_body(body_data);
+        return reply(response);
+    }
+
+    /// Responds to this HTTP request with a string.
+    /// Assumes the character encoding of the string is UTF-8.
+    /// </summary>
+    /// <param name="status">Response status code.</param>
+    /// <param name="body_data">UTF-8 string containing the text to use in the response body.</param>
+    /// <param name="content_type">Content type of the body.</param>
+    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
+    /// <remarks>
+    //  Callers of this function do NOT need to block waiting for the response to be
+    /// sent to before the body data is destroyed or goes out of scope.
+    /// </remarks>
+    pplx::task<void> reply(http::status_code status,
+                           utf8string&& body_data,
+                           const utf8string& content_type = "text/plain; charset=utf-8") const
+    {
+        http_response response(status);
+        response.set_body(std::move(body_data), content_type);
+        return reply(response);
+    }
+
+    /// <summary>
+    /// Responds to this HTTP request with a string.
+    /// Assumes the character encoding of the string is UTF-8.
+    /// </summary>
+    /// <param name="status">Response status code.</param>
+    /// <param name="body_data">UTF-8 string containing the text to use in the response body.</param>
+    /// <param name="content_type">Content type of the body.</param>
+    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
+    /// <remarks>
+    //  Callers of this function do NOT need to block waiting for the response to be
+    /// sent to before the body data is destroyed or goes out of scope.
+    /// </remarks>
+    pplx::task<void> reply(http::status_code status,
+                           const utf8string& body_data,
+                           const utf8string& content_type = "text/plain; charset=utf-8") const
+    {
+        http_response response(status);
+        response.set_body(body_data, content_type);
+        return reply(response);
+    }
+
+    /// <summary>
+    /// Responds to this HTTP request with a string. Assumes the character encoding
+    /// of the string is UTF-16 will perform conversion to UTF-8.
+    /// </summary>
+    /// <param name="status">Response status code.</param>
+    /// <param name="body_data">UTF-16 string containing the text to use in the response body.</param>
+    /// <param name="content_type">Content type of the body.</param>
+    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
+    /// <remarks>
+    //  Callers of this function do NOT need to block waiting for the response to be
+    /// sent to before the body data is destroyed or goes out of scope.
+    /// </remarks>
+    pplx::task<void> reply(http::status_code status,
+                           const utf16string& body_data,
+                           const utf16string& content_type = utility::conversions::to_utf16string("text/plain")) const
+    {
+        http_response response(status);
+        response.set_body(body_data, content_type);
+        return reply(response);
+    }
+
+    /// <summary>
+    /// Responds to this HTTP request.
+    /// </summary>
+    /// <param name="status">Response status code.</param>
+    /// <param name="content_type">A string holding the MIME type of the message body.</param>
+    /// <param name="body">An asynchronous stream representing the body data.</param>
+    /// <returns>A task that is completed once a response from the request is received.</returns>
+    pplx::task<void> reply(status_code status,
+                           const concurrency::streams::istream& body,
+                           const utility::string_t& content_type = _XPLATSTR("application/octet-stream")) const
+    {
+        http_response response(status);
+        response.set_body(body, content_type);
+        return reply(response);
+    }
+
+    /// <summary>
+    /// Responds to this HTTP request.
+    /// </summary>
+    /// <param name="status">Response status code.</param>
+    /// <param name="content_length">The size of the data to be sent in the body..</param>
+    /// <param name="content_type">A string holding the MIME type of the message body.</param>
+    /// <param name="body">An asynchronous stream representing the body data.</param>
+    /// <returns>A task that is completed once a response from the request is received.</returns>
+    pplx::task<void> reply(status_code status,
+                           const concurrency::streams::istream& body,
+                           utility::size64_t content_length,
+                           const utility::string_t& content_type = _XPLATSTR("application/octet-stream")) const
+    {
+        http_response response(status);
+        response.set_body(body, content_length, content_type);
+        return reply(response);
+    }
+
+    /// <summary>
+    /// Signals the user (listener) when all the data for this request message has been received.
+    /// </summary>
+    /// <returns>A <c>task</c> which is completed when all of the response body has been received</returns>
+    pplx::task<http_request> content_ready() const
+    {
+        http_request req = *this;
+        return pplx::create_task(_m_impl->_get_data_available()).then([req](utility::size64_t) mutable { return req; });
+    }
+
+    /// <summary>
+    /// Gets a task representing the response that will eventually be sent.
+    /// </summary>
+    /// <returns>A task that is completed once response is sent.</returns>
+    pplx::task<http_response> get_response() const { return _m_impl->get_response(); }
+
+    /// <summary>
+    /// Generates a string representation of the message, including the body when possible.
+    /// Mainly this should be used for debugging purposes as it has to copy the
+    /// message body and doesn't have excellent performance.
+    /// </summary>
+    /// <returns>A string representation of this HTTP request.</returns>
+    /// <remarks>Note this function is synchronous and doesn't wait for the
+    /// entire message body to arrive. If the message body has arrived by the time this
+    /// function is called and it is has a textual Content-Type it will be included.
+    /// Otherwise just the headers will be present.</remarks>
+    utility::string_t to_string() const { return _m_impl->to_string(); }
+
+    /// <summary>
+    /// Sends a response if one has not already been sent.
+    /// </summary>
+    pplx::task<void> _reply_if_not_already(status_code status) { return _m_impl->_reply_if_not_already(status); }
+
+    /// <summary>
+    /// Gets the server context associated with this HTTP message.
+    /// </summary>
+    http::details::_http_server_context* _get_server_context() const { return _m_impl->_get_server_context(); }
+
+    /// <summary>
+    /// These are used for the initial creation of the HTTP request.
+    /// </summary>
+    static http_request _create_request(std::unique_ptr<http::details::_http_server_context> server_context)
+    {
+        return http_request(std::move(server_context));
+    }
+    void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)
+    {
+        _m_impl->_set_server_context(std::move(server_context));
+    }
+
+    void _set_listener_path(const utility::string_t& path) { _m_impl->_set_listener_path(path); }
+
+    const std::shared_ptr<http::details::_http_request>& _get_impl() const { return _m_impl; }
+
+    void _set_cancellation_token(const pplx::cancellation_token& token) { _m_impl->set_cancellation_token(token); }
+
+    const pplx::cancellation_token& _cancellation_token() const { return _m_impl->cancellation_token(); }
+
+    void _set_base_uri(const http::uri& base_uri) { _m_impl->_set_base_uri(base_uri); }
+
+private:
+    friend class http::details::_http_request;
+    friend class http::client::http_client;
+
+    http_request(std::unique_ptr<http::details::_http_server_context> server_context)
+        : _m_impl(std::make_shared<details::_http_request>(std::move(server_context)))
+    {
+    }
+
+    std::shared_ptr<http::details::_http_request> _m_impl;
+};
+
+namespace client
+{
+class http_pipeline;
+}
+
+/// <summary>
+/// HTTP client handler class, used to represent an HTTP pipeline stage.
+/// </summary>
+/// <remarks>
+/// When a request goes out, it passes through a series of stages, customizable by
+/// the application and/or libraries. The default stage will interact with lower-level
+/// communication layers to actually send the message on the network. When creating a client
+/// instance, an application may add pipeline stages in front of the already existing
+/// stages. Each stage has a reference to the next stage available in the <seealso cref="http_pipeline_stage::next_stage
+/// Method"/> value.
+/// </remarks>
+class http_pipeline_stage : public std::enable_shared_from_this<http_pipeline_stage>
+{
+public:
+    http_pipeline_stage() = default;
+
+    http_pipeline_stage& operator=(const http_pipeline_stage&) = delete;
+    http_pipeline_stage(const http_pipeline_stage&) = delete;
+
+    virtual ~http_pipeline_stage() = default;
+
+    /// <summary>
+    /// Runs this stage against the given request and passes onto the next stage.
+    /// </summary>
+    /// <param name="request">The HTTP request.</param>
+    /// <returns>A task of the HTTP response.</returns>
+    virtual pplx::task<http_response> propagate(http_request request) = 0;
+
+protected:
+    /// <summary>
+    /// Gets the next stage in the pipeline.
+    /// </summary>
+    /// <returns>A shared pointer to a pipeline stage.</returns>
+    const std::shared_ptr<http_pipeline_stage>& next_stage() const { return m_next_stage; }
+
+    /// <summary>
+    /// Gets a shared pointer to this pipeline stage.
+    /// </summary>
+    /// <returns>A shared pointer to a pipeline stage.</returns>
+    CASABLANCA_DEPRECATED("This api is redundant. Use 'shared_from_this()' directly instead.")
+    std::shared_ptr<http_pipeline_stage> current_stage() { return this->shared_from_this(); }
+
+private:
+    friend class ::web::http::client::http_pipeline;
+
+    void set_next_stage(const std::shared_ptr<http_pipeline_stage>& next) { m_next_stage = next; }
+
+    std::shared_ptr<http_pipeline_stage> m_next_stage;
+};
+
+} // namespace http
+} // namespace web
diff --git a/Release/include/cpprest/interopstream.h b/Release/include/cpprest/interopstream.h
new file mode 100644 (file)
index 0000000..e3287c1
--- /dev/null
@@ -0,0 +1,554 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Adapter classes for async and STD stream buffers, used to connect std-based and async-based APIs.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "cpprest/astreambuf.h"
+#include "cpprest/streams.h"
+#include "pplx/pplxtasks.h"
+
+#if defined(_WIN32)
+#pragma warning(push)
+#pragma warning(disable : 4250)
+#endif
+
+namespace Concurrency
+{
+namespace streams
+{
+template<typename CharType>
+class stdio_ostream;
+template<typename CharType>
+class stdio_istream;
+
+namespace details
+{
+/// <summary>
+/// The basic_stdio_buffer class serves to support interoperability with STL stream buffers.
+/// Sitting atop a std::streambuf, which does all the I/O, instances of this class may read
+/// and write data to standard iostreams. The class itself should not be used in application
+/// code, it is used by the stream definitions farther down in the header file.
+/// </summary>
+template<typename _CharType>
+class basic_stdio_buffer : public streambuf_state_manager<_CharType>
+{
+    typedef concurrency::streams::char_traits<_CharType> traits;
+    typedef typename traits::int_type int_type;
+    typedef typename traits::pos_type pos_type;
+    typedef typename traits::off_type off_type;
+    /// <summary>
+    /// Private constructor
+    /// </summary>
+    basic_stdio_buffer(_In_ std::basic_streambuf<_CharType>* streambuf, std::ios_base::openmode mode)
+        : streambuf_state_manager<_CharType>(mode), m_buffer(streambuf)
+    {
+    }
+
+public:
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    virtual ~basic_stdio_buffer()
+    {
+        this->_close_read();
+        this->_close_write();
+    }
+
+private:
+    //
+    // The functions overridden below here are documented elsewhere.
+    // See astreambuf.h for further information.
+    //
+    virtual bool can_seek() const { return this->is_open(); }
+    virtual bool has_size() const { return false; }
+
+    virtual size_t in_avail() const { return (size_t)m_buffer->in_avail(); }
+
+    virtual size_t buffer_size(std::ios_base::openmode) const { return 0; }
+    virtual void set_buffer_size(size_t, std::ios_base::openmode) { return; }
+
+    virtual pplx::task<bool> _sync() { return pplx::task_from_result(m_buffer->pubsync() == 0); }
+
+    virtual pplx::task<int_type> _putc(_CharType ch) { return pplx::task_from_result(m_buffer->sputc(ch)); }
+    virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t size)
+    {
+        return pplx::task_from_result((size_t)m_buffer->sputn(ptr, size));
+    }
+
+    size_t _sgetn(_Out_writes_(size) _CharType* ptr, _In_ size_t size) const { return m_buffer->sgetn(ptr, size); }
+    virtual size_t _scopy(_Out_writes_(size) _CharType*, _In_ size_t size)
+    {
+        (void)(size);
+        return (size_t)-1;
+    }
+
+    virtual pplx::task<size_t> _getn(_Out_writes_(size) _CharType* ptr, _In_ size_t size)
+    {
+        return pplx::task_from_result((size_t)m_buffer->sgetn(ptr, size));
+    }
+
+    virtual int_type _sbumpc() { return m_buffer->sbumpc(); }
+    virtual int_type _sgetc() { return m_buffer->sgetc(); }
+
+    virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result<int_type>(m_buffer->sbumpc()); }
+    virtual pplx::task<int_type> _getc() { return pplx::task_from_result<int_type>(m_buffer->sgetc()); }
+    virtual pplx::task<int_type> _nextc() { return pplx::task_from_result<int_type>(m_buffer->snextc()); }
+    virtual pplx::task<int_type> _ungetc() { return pplx::task_from_result<int_type>(m_buffer->sungetc()); }
+
+    virtual pos_type getpos(std::ios_base::openmode mode) const
+    {
+        return m_buffer->pubseekoff(0, std::ios_base::cur, mode);
+    }
+    virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode) { return m_buffer->pubseekpos(pos, mode); }
+    virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode mode)
+    {
+        return m_buffer->pubseekoff(off, dir, mode);
+    }
+
+    virtual _CharType* _alloc(size_t) { return nullptr; }
+    virtual void _commit(size_t) {}
+
+    virtual bool acquire(_CharType*&, size_t&) { return false; }
+    virtual void release(_CharType*, size_t) {}
+
+    template<typename CharType>
+    friend class concurrency::streams::stdio_ostream;
+    template<typename CharType>
+    friend class concurrency::streams::stdio_istream;
+
+    std::basic_streambuf<_CharType>* m_buffer;
+};
+
+} // namespace details
+
+/// <summary>
+/// stdio_ostream represents an async ostream derived from a standard synchronous stream, as
+/// defined by the "std" namespace. It is constructed from a reference to a standard stream, which
+/// must be valid for the lifetime of the asynchronous stream.
+/// </summary>
+/// <typeparam name="CharType">
+/// The data type of the basic element of the <c>stdio_ostream</c>.
+/// </typeparam>
+/// <remarks>
+/// Since std streams are not reference-counted, great care must be taken by an application to make
+/// sure that the std stream does not get destroyed until all uses of the asynchronous stream are
+/// done and have been serviced.
+/// </remarks>
+template<typename CharType>
+class stdio_ostream : public basic_ostream<CharType>
+{
+public:
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <typeparam name="AlterCharType">
+    /// The data type of the basic element of the source output stream.
+    /// </typeparam>
+    /// <param name="stream">The synchronous stream that this is using for its I/O</param>
+    template<typename AlterCharType>
+    stdio_ostream(std::basic_ostream<AlterCharType>& stream)
+        : basic_ostream<CharType>(
+              streams::streambuf<AlterCharType>(std::shared_ptr<details::basic_stdio_buffer<AlterCharType>>(
+                  new details::basic_stdio_buffer<AlterCharType>(stream.rdbuf(), std::ios_base::out))))
+    {
+    }
+
+    /// <summary>
+    /// Copy constructor
+    /// </summary>
+    /// <param name="other">The source object</param>
+    stdio_ostream(const stdio_ostream& other) : basic_ostream<CharType>(other) {}
+
+    /// <summary>
+    /// Assignment operator
+    /// </summary>
+    /// <param name="other">The source object</param>
+    /// <returns>A reference to the output stream object that contains the result of the assignment.</returns>
+    stdio_ostream& operator=(const stdio_ostream& other)
+    {
+        basic_ostream<CharType>::operator=(other);
+        return *this;
+    }
+};
+
+/// <summary>
+/// stdio_istream represents an async istream derived from a standard synchronous stream, as
+/// defined by the "std" namespace. It is constructed from a reference to a standard stream, which
+/// must be valid for the lifetime of the asynchronous stream.
+/// </summary>
+/// <typeparam name="CharType">
+/// The data type of the basic element of the <c>stdio_istream</c>.
+/// </typeparam>
+/// <remarks>
+/// Since std streams are not reference-counted, great care must be taken by an application to make
+/// sure that the std stream does not get destroyed until all uses of the asynchronous stream are
+/// done and have been serviced.
+/// </remarks>
+template<typename CharType>
+class stdio_istream : public basic_istream<CharType>
+{
+public:
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <typeparam name="AlterCharType">
+    /// The data type of the basic element of the source <c>istream</c>
+    /// </typeparam>
+    /// <param name="stream">The synchronous stream that this is using for its I/O</param>
+    template<typename AlterCharType>
+    stdio_istream(std::basic_istream<AlterCharType>& stream)
+        : basic_istream<CharType>(
+              streams::streambuf<AlterCharType>(std::shared_ptr<details::basic_stdio_buffer<AlterCharType>>(
+                  new details::basic_stdio_buffer<AlterCharType>(stream.rdbuf(), std::ios_base::in))))
+    {
+    }
+
+    /// <summary>
+    /// Copy constructor
+    /// </summary>
+    /// <param name="other">The source object</param>
+    stdio_istream(const stdio_istream& other) : basic_istream<CharType>(other) {}
+
+    /// <summary>
+    /// Assignment operator
+    /// </summary>
+    /// <param name="other">The source object</param>
+    /// <returns>A reference to the input stream object that contains the result of the assignment.</returns>
+    stdio_istream& operator=(const stdio_istream& other)
+    {
+        basic_istream<CharType>::operator=(other);
+        return *this;
+    }
+};
+
+namespace details
+{
+/// <summary>
+/// IO streams stream buffer implementation used to interface with an async streambuffer underneath.
+/// Used for implementing the standard synchronous streams that provide interop between std:: and concurrency::streams::
+/// </summary>
+template<typename CharType>
+class basic_async_streambuf : public std::basic_streambuf<CharType>
+{
+public:
+    typedef concurrency::streams::char_traits<CharType> traits;
+    typedef typename traits::int_type int_type;
+    typedef typename traits::pos_type pos_type;
+    typedef typename traits::off_type off_type;
+
+    basic_async_streambuf(const streams::streambuf<CharType>& async_buf) : m_buffer(async_buf) {}
+
+protected:
+    //
+    // The following are the functions in std::basic_streambuf that we need to override.
+    //
+
+    /// <summary>
+    /// Writes one byte to the stream buffer.
+    /// </summary>
+    int_type overflow(int_type ch)
+    {
+        try
+        {
+            return m_buffer.putc(CharType(ch)).get();
+        }
+        catch (...)
+        {
+            return traits::eof();
+        }
+    }
+
+    /// <summary>
+    /// Gets one byte from the stream buffer without moving the read position.
+    /// </summary>
+    int_type underflow()
+    {
+        try
+        {
+            return m_buffer.getc().get();
+        }
+        catch (...)
+        {
+            return traits::eof();
+        }
+    }
+
+    /// <summary>
+    /// Gets one byte from the stream buffer and move the read position one character.
+    /// </summary>
+    int_type uflow()
+    {
+        try
+        {
+            return m_buffer.bumpc().get();
+        }
+        catch (...)
+        {
+            return traits::eof();
+        }
+    }
+
+    /// <summary>
+    /// Gets a number of characters from the buffer and place it into the provided memory block.
+    /// </summary>
+    std::streamsize xsgetn(_Out_writes_(count) CharType* ptr, _In_ std::streamsize count)
+    {
+        size_t cnt = size_t(count);
+        size_t read_so_far = 0;
+
+        try
+        {
+            while (read_so_far < cnt)
+            {
+                size_t rd = m_buffer.getn(ptr + read_so_far, cnt - read_so_far).get();
+                read_so_far += rd;
+                if (rd == 0) break;
+            }
+            return read_so_far;
+        }
+        catch (...)
+        {
+            return 0;
+        }
+    }
+
+    /// <summary>
+    /// Writes a given number of characters from the provided block into the stream buffer.
+    /// </summary>
+    std::streamsize xsputn(const CharType* ptr, std::streamsize count)
+    {
+        try
+        {
+            return m_buffer.putn_nocopy(ptr, static_cast<size_t>(count)).get();
+        }
+        catch (...)
+        {
+            return 0;
+        }
+    }
+
+    /// <summary>
+    /// Synchronizes with the underlying medium.
+    /// </summary>
+    int sync() // must be int as per std::basic_streambuf
+    {
+        try
+        {
+            m_buffer.sync().wait();
+        }
+        catch (...)
+        {
+        }
+        return 0;
+    }
+
+    /// <summary>
+    /// Seeks to the given offset relative to the beginning, end, or current position.
+    /// </summary>
+    pos_type seekoff(off_type offset,
+                     std::ios_base::seekdir dir,
+                     std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
+    {
+        try
+        {
+            if (dir == std::ios_base::cur && offset == 0) // Special case for getting the current position.
+                return m_buffer.getpos(mode);
+            return m_buffer.seekoff(offset, dir, mode);
+        }
+        catch (...)
+        {
+            return (pos_type(-1));
+        }
+    }
+
+    /// <summary>
+    /// Seeks to the given offset relative to the beginning of the stream.
+    /// </summary>
+    pos_type seekpos(pos_type pos, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
+    {
+        try
+        {
+            return m_buffer.seekpos(pos, mode);
+        }
+        catch (...)
+        {
+            return (pos_type(-1));
+        }
+    }
+
+private:
+    concurrency::streams::streambuf<CharType> m_buffer;
+};
+
+} // namespace details
+
+/// <summary>
+/// A concrete STL ostream which relies on an asynchronous stream for its I/O.
+/// </summary>
+/// <typeparam name="CharType">
+/// The data type of the basic element of the stream.
+/// </typeparam>
+template<typename CharType>
+class async_ostream : public std::basic_ostream<CharType>
+{
+public:
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <typeparam name="AlterCharType">
+    /// The data type of the basic element of the source ostream.
+    /// </typeparam>
+    /// <param name="astream">The asynchronous stream whose stream buffer should be used for I/O</param>
+    template<typename AlterCharType>
+    async_ostream(const streams::basic_ostream<AlterCharType>& astream)
+        : std::basic_ostream<CharType>(&m_strbuf), m_strbuf(astream.streambuf())
+    {
+    }
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <typeparam name="AlterCharType">
+    /// The data type of the basic element of the source <c>streambuf</c>.
+    /// </typeparam>
+    /// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
+    template<typename AlterCharType>
+    async_ostream(const streams::streambuf<AlterCharType>& strbuf)
+        : std::basic_ostream<CharType>(&m_strbuf), m_strbuf(strbuf)
+    {
+    }
+
+private:
+    details::basic_async_streambuf<CharType> m_strbuf;
+};
+
+/// <summary>
+/// A concrete STL istream which relies on an asynchronous stream for its I/O.
+/// </summary>
+/// <typeparam name="CharType">
+/// The data type of the basic element of the stream.
+/// </typeparam>
+template<typename CharType>
+class async_istream : public std::basic_istream<CharType>
+{
+public:
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <typeparam name="AlterCharType">
+    /// The data type of the basic element of the source istream.
+    /// </typeparam>
+    /// <param name="astream">The asynchronous stream whose stream buffer should be used for I/O</param>
+    template<typename AlterCharType>
+    async_istream(const streams::basic_istream<AlterCharType>& astream)
+        : std::basic_istream<CharType>(&m_strbuf), m_strbuf(astream.streambuf())
+    {
+    }
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <typeparam name="AlterCharType">
+    /// The data type of the basic element of the source <c>streambuf</c>.
+    /// </typeparam>
+    /// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
+    template<typename AlterCharType>
+    async_istream(const streams::streambuf<AlterCharType>& strbuf)
+        : std::basic_istream<CharType>(&m_strbuf), m_strbuf(strbuf)
+    {
+    }
+
+private:
+    details::basic_async_streambuf<CharType> m_strbuf;
+};
+
+/// <summary>
+/// A concrete STL istream which relies on an asynchronous stream buffer for its I/O.
+/// </summary>
+/// <typeparam name="CharType">
+/// The data type of the basic element of the stream.
+/// </typeparam>
+template<typename CharType>
+class async_iostream : public std::basic_iostream<CharType>
+{
+public:
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
+    async_iostream(const streams::streambuf<CharType>& strbuf)
+        : std::basic_iostream<CharType>(&m_strbuf), m_strbuf(strbuf)
+    {
+    }
+
+private:
+    details::basic_async_streambuf<CharType> m_strbuf;
+};
+
+#if defined(__cplusplus_winrt)
+
+/// <summary>
+/// Static class containing factory functions for WinRT streams implemented on top of Casablanca async streams.
+/// </summary>
+/// <remarks>WinRT streams are defined in terms of single-byte characters only.</remarks>
+class winrt_stream
+{
+public:
+    /// <summary>
+    /// Creates a WinRT <c>IInputStream</c> reference from an asynchronous stream buffer.
+    /// </summary>
+    /// <param name="buffer">A stream buffer based on a single-byte character.</param>
+    /// <returns>A reference to a WinRT <c>IInputStream</c>.</returns>
+    /// <remarks>
+    /// The stream buffer passed in must allow reading.
+    /// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
+    /// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can pass data to a WinRT component.
+    /// </remarks>
+    _ASYNCRTIMP static Windows::Storage::Streams::IInputStream ^
+        __cdecl create_input_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
+
+    /// <summary>
+    /// Creates a WinRT <c>IOutputStream</c> reference from an asynchronous stream buffer.
+    /// </summary>
+    /// <param name="buffer">A stream buffer based on a single-byte character.</param>
+    /// <returns>A reference to a WinRT <c>IOutputStream</c>.</returns>
+    /// <remarks>
+    /// The stream buffer passed in must allow writing.
+    /// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
+    /// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can retrieve data from a WinRT
+    /// component.
+    /// </remarks>
+    _ASYNCRTIMP static Windows::Storage::Streams::IOutputStream ^
+        __cdecl create_output_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
+
+    /// <summary>
+    /// Creates a WinRT <c>IRandomAccessStream reference from an asynchronous input stream.
+    /// </summary>
+    /// <param name="buffer">A stream based on a single-byte character.</param>
+    /// <returns>A reference to a WinRT <c>IRandomAccessStream</c>.</returns>
+    /// <remarks>
+    /// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
+    /// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can pass data to and retrieve data
+    /// from a WinRT component.
+    /// </remarks>
+    _ASYNCRTIMP static Windows::Storage::Streams::IRandomAccessStream ^
+        __cdecl create_random_access_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
+};
+
+#endif
+
+} // namespace streams
+} // namespace Concurrency
+
+#if defined(_WIN32)
+#pragma warning(pop)
+#endif
diff --git a/Release/include/cpprest/json.h b/Release/include/cpprest/json.h
new file mode 100644 (file)
index 0000000..8fbbf94
--- /dev/null
@@ -0,0 +1,1806 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: JSON parser and writer
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_JSON_H
+#define CASA_JSON_H
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/details/basic_types.h"
+#include <cstdint>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace web
+{
+/// Library for parsing and serializing JSON values to and from C++ types.
+namespace json
+{
+// Various forward declarations.
+namespace details
+{
+class _Value;
+class _Number;
+class _Null;
+class _Boolean;
+class _String;
+class _Object;
+class _Array;
+template<typename CharType>
+class JSON_Parser;
+} // namespace details
+
+namespace details
+{
+extern bool g_keep_json_object_unsorted;
+}
+
+/// <summary>
+/// Preserve the order of the name/value pairs when parsing a JSON object.
+/// The default is false, which can yield better performance.
+/// </summary>
+/// <param name="keep_order"><c>true</c> if ordering should be preserved when parsing, <c>false</c> otherwise.</param>
+/// <remarks>Note this is a global setting and affects all JSON parsing done.</remarks>
+void _ASYNCRTIMP __cdecl keep_object_element_order(bool keep_order);
+
+#ifdef _WIN32
+#ifdef _DEBUG
+#define ENABLE_JSON_VALUE_VISUALIZER
+#endif
+#endif
+
+class number;
+class array;
+class object;
+
+/// <summary>
+/// A JSON value represented as a C++ class.
+/// </summary>
+class value
+{
+public:
+    /// <summary>
+    /// This enumeration represents the various kinds of JSON values.
+    /// </summary>
+    enum value_type
+    {
+        /// Number value
+        Number,
+        /// Boolean value
+        Boolean,
+        /// String value
+        String,
+        /// Object value
+        Object,
+        /// Array value
+        Array,
+        /// Null value
+        Null
+    };
+
+    /// <summary>
+    /// Constructor creating a null value
+    /// </summary>
+    _ASYNCRTIMP value();
+
+    /// <summary>
+    /// Constructor creating a JSON number value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    _ASYNCRTIMP value(int32_t value);
+
+    /// <summary>
+    /// Constructor creating a JSON number value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    _ASYNCRTIMP value(uint32_t value);
+
+    /// <summary>
+    /// Constructor creating a JSON number value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    _ASYNCRTIMP value(int64_t value);
+
+    /// <summary>
+    /// Constructor creating a JSON number value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    _ASYNCRTIMP value(uint64_t value);
+
+    /// <summary>
+    /// Constructor creating a JSON number value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    _ASYNCRTIMP value(double value);
+
+    /// <summary>
+    /// Constructor creating a JSON Boolean value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    _ASYNCRTIMP explicit value(bool value);
+
+    /// <summary>
+    /// Constructor creating a JSON string value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the platform-native character
+    /// width</param> <remarks> This constructor has O(n) performance because it tries to determine if specified string
+    /// has characters that should be properly escaped in JSON. <remarks>
+    _ASYNCRTIMP explicit value(utility::string_t value);
+
+    /// <summary>
+    /// Constructor creating a JSON string value specifying if the string contains characters to escape
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the platform-native character
+    /// width</param> <param name="has_escape_chars">Whether <paramref name="value" /> contains characters that should
+    /// be escaped in JSON value</param> <remarks> This constructor has O(1) performance.
+    /// </remarks>
+    _ASYNCRTIMP explicit value(utility::string_t value, bool has_escape_chars);
+
+    /// <summary>
+    /// Constructor creating a JSON string value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the platform-native character
+    /// width</param> <remarks> <para> This constructor has O(n) performance because it tries to determine if specified
+    /// string has characters that should be properly escaped in JSON.
+    /// </para>
+    /// <para>
+    /// This constructor exists in order to avoid string literals matching another constructor,
+    /// as is very likely. For example, conversion to bool does not require a user-defined conversion,
+    /// and will therefore match first, which means that the JSON value turns up as a boolean.
+    /// </para>
+    /// </remarks>
+    _ASYNCRTIMP explicit value(const utility::char_t* value);
+
+    /// <summary>
+    /// Constructor creating a JSON string value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the platform-native character
+    /// width</param> <param name="has_escape_chars">Whether <paramref name="value" /> contains characters <remarks>
+    /// <para>
+    /// This overload has O(1) performance.
+    /// </para>
+    /// <para>
+    /// This constructor exists in order to avoid string literals matching another constructor,
+    /// as is very likely. For example, conversion to bool does not require a user-defined conversion,
+    /// and will therefore match first, which means that the JSON value turns up as a boolean.
+    /// </para>
+    /// </remarks>
+    _ASYNCRTIMP explicit value(const utility::char_t* value, bool has_escape_chars);
+
+    /// <summary>
+    /// Copy constructor
+    /// </summary>
+    _ASYNCRTIMP value(const value&);
+
+    /// <summary>
+    /// Move constructor
+    /// </summary>
+    _ASYNCRTIMP value(value&&) CPPREST_NOEXCEPT;
+
+    /// <summary>
+    /// Assignment operator.
+    /// </summary>
+    /// <returns>The JSON value object that contains the result of the assignment.</returns>
+    _ASYNCRTIMP value& operator=(const value&);
+
+    /// <summary>
+    /// Move assignment operator.
+    /// </summary>
+    /// <returns>The JSON value object that contains the result of the assignment.</returns>
+    _ASYNCRTIMP value& operator=(value&&) CPPREST_NOEXCEPT;
+
+    // Static factories
+
+    /// <summary>
+    /// Creates a null value
+    /// </summary>
+    /// <returns>A JSON null value</returns>
+    static _ASYNCRTIMP value __cdecl null();
+
+    /// <summary>
+    /// Creates a number value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    /// <returns>A JSON number value</returns>
+    static _ASYNCRTIMP value __cdecl number(double value);
+
+    /// <summary>
+    /// Creates a number value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    /// <returns>A JSON number value</returns>
+    static _ASYNCRTIMP value __cdecl number(int32_t value);
+
+    /// <summary>
+    /// Creates a number value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    /// <returns>A JSON number value</returns>
+    static _ASYNCRTIMP value __cdecl number(uint32_t value);
+
+    /// <summary>
+    /// Creates a number value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    /// <returns>A JSON number value</returns>
+    static _ASYNCRTIMP value __cdecl number(int64_t value);
+
+    /// <summary>
+    /// Creates a number value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    /// <returns>A JSON number value</returns>
+    static _ASYNCRTIMP value __cdecl number(uint64_t value);
+
+    /// <summary>
+    /// Creates a Boolean value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    /// <returns>A JSON Boolean value</returns>
+    static _ASYNCRTIMP value __cdecl boolean(bool value);
+
+    /// <summary>
+    /// Creates a string value
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    /// <returns>A JSON string value</returns>
+    /// <remarks>
+    /// This overload has O(n) performance because it tries to determine if
+    /// specified string has characters that should be properly escaped in JSON.
+    /// </remarks>
+    static _ASYNCRTIMP value __cdecl string(utility::string_t value);
+
+    /// <summary>
+    /// Creates a string value specifying if the string contains characters to escape
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from</param>
+    /// <param name="has_escape_chars">Whether <paramref name="value" /> contains characters
+    /// that should be escaped in JSON value</param>
+    /// <returns>A JSON string value</returns>
+    /// <remarks>
+    /// This overload has O(1) performance.
+    /// </remarks>
+    static _ASYNCRTIMP value __cdecl string(utility::string_t value, bool has_escape_chars);
+
+#ifdef _WIN32
+private:
+    // Only used internally by JSON parser.
+    static _ASYNCRTIMP value __cdecl string(const std::string& value);
+
+public:
+#endif
+
+    /// <summary>
+    /// Creates an object value
+    /// </summary>
+    /// <param name="keep_order">Whether to preserve the original order of the fields</param>
+    /// <returns>An empty JSON object value</returns>
+    static _ASYNCRTIMP json::value __cdecl object(bool keep_order = false);
+
+    /// <summary>
+    /// Creates an object value from a collection of field/values
+    /// </summary>
+    /// <param name="fields">Field names associated with JSON values</param>
+    /// <param name="keep_order">Whether to preserve the original order of the fields</param>
+    /// <returns>A non-empty JSON object value</returns>
+    static _ASYNCRTIMP json::value __cdecl object(std::vector<std::pair<::utility::string_t, value>> fields,
+                                                  bool keep_order = false);
+
+    /// <summary>
+    /// Creates an empty JSON array
+    /// </summary>
+    /// <returns>An empty JSON array value</returns>
+    static _ASYNCRTIMP json::value __cdecl array();
+
+    /// <summary>
+    /// Creates a JSON array
+    /// </summary>
+    /// <param name="size">The initial number of elements of the JSON value</param>
+    /// <returns>A JSON array value</returns>
+    static _ASYNCRTIMP json::value __cdecl array(size_t size);
+
+    /// <summary>
+    /// Creates a JSON array
+    /// </summary>
+    /// <param name="elements">A vector of JSON values</param>
+    /// <returns>A JSON array value</returns>
+    static _ASYNCRTIMP json::value __cdecl array(std::vector<value> elements);
+
+    /// <summary>
+    /// Accesses the type of JSON value the current value instance is
+    /// </summary>
+    /// <returns>The value's type</returns>
+    _ASYNCRTIMP json::value::value_type type() const;
+
+    /// <summary>
+    /// Is the current value a null value?
+    /// </summary>
+    /// <returns><c>true</c> if the value is a null value, <c>false</c> otherwise</returns>
+    bool is_null() const { return type() == Null; };
+
+    /// <summary>
+    /// Is the current value a number value?
+    /// </summary>
+    /// <returns><c>true</c> if the value is a number value, <c>false</c> otherwise</returns>
+    bool is_number() const { return type() == Number; }
+
+    /// <summary>
+    /// Is the current value represented as an integer number value?
+    /// </summary>
+    /// <remarks>
+    /// Note that if a json value is a number but represented as a double it can still
+    /// be retrieved as a integer using as_integer(), however the value will be truncated.
+    /// </remarks>
+    /// <returns><c>true</c> if the value is an integer value, <c>false</c> otherwise.</returns>
+    _ASYNCRTIMP bool is_integer() const;
+
+    /// <summary>
+    /// Is the current value represented as an double number value?
+    /// </summary>
+    /// <remarks>
+    /// Note that if a json value is a number but represented as a int it can still
+    /// be retrieved as a double using as_double().
+    /// </remarks>
+    /// <returns><c>true</c> if the value is an double value, <c>false</c> otherwise.</returns>
+    _ASYNCRTIMP bool is_double() const;
+
+    /// <summary>
+    /// Is the current value a Boolean value?
+    /// </summary>
+    /// <returns><c>true</c> if the value is a Boolean value, <c>false</c> otherwise</returns>
+    bool is_boolean() const { return type() == Boolean; }
+
+    /// <summary>
+    /// Is the current value a string value?
+    /// </summary>
+    /// <returns><c>true</c> if the value is a string value, <c>false</c> otherwise</returns>
+    bool is_string() const { return type() == String; }
+
+    /// <summary>
+    /// Is the current value an array?
+    /// </summary>
+    /// <returns><c>true</c> if the value is an array, <c>false</c> otherwise</returns>
+    bool is_array() const { return type() == Array; }
+
+    /// <summary>
+    /// Is the current value an object?
+    /// </summary>
+    /// <returns><c>true</c> if the value is an object, <c>false</c> otherwise</returns>
+    bool is_object() const { return type() == Object; }
+
+    /// <summary>
+    /// Gets the number of children of the value.
+    /// </summary>
+    /// <returns>The number of children. 0 for all non-composites.</returns>
+    size_t size() const;
+
+    /// <summary>
+    /// Parses a string and construct a JSON value.
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the
+    /// platform-native character width</param>
+    _ASYNCRTIMP static value __cdecl parse(const utility::string_t& value);
+
+    /// <summary>
+    /// Attempts to parse a string and construct a JSON value.
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the
+    /// platform-native character width</param>
+    /// <param name="errorCode">If parsing fails, the error code is greater than 0</param>
+    /// <returns>The parsed object. Returns web::json::value::null if failed</returns>
+    _ASYNCRTIMP static value __cdecl parse(const utility::string_t& value, std::error_code& errorCode);
+
+#ifdef _WIN32
+    /// <summary>
+    /// Parses a string and construct a JSON value.
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from, a C++ STL string in
+    /// UTF8 format</param>
+    _ASYNCRTIMP static value __cdecl parse(const std::string& value);
+
+    /// <summary>
+    /// Attempts to parse a string and construct a JSON value.
+    /// </summary>
+    /// <param name="value">The C++ value to create a JSON value from, a C++ STL string in
+    /// UTF8 format</param>
+    /// <param name="errorCode">If parsing fails, the error code is greater than 0</param>
+    /// <returns>The parsed object. Returns web::json::value::null if failed</returns>
+    _ASYNCRTIMP static value __cdecl parse(const std::string& value, std::error_code& errorCode);
+#endif
+
+    /// <summary>
+    /// Serializes the current JSON value to a C++ string.
+    /// </summary>
+    /// <returns>A string representation of the value</returns>
+    _ASYNCRTIMP utility::string_t serialize() const;
+
+    /// <summary>
+    /// Serializes the current JSON value to a C++ string.
+    /// </summary>
+    /// <returns>A string representation of the value</returns>
+    CASABLANCA_DEPRECATED("This API is deprecated and has been renamed to avoid confusion with as_string(), use "
+                          "::web::json::value::serialize() instead.")
+    _ASYNCRTIMP utility::string_t to_string() const;
+
+    /// <summary>
+    /// Parses a JSON value from the contents of an input stream using the native platform character width.
+    /// </summary>
+    /// <param name="input">The stream to read the JSON value from</param>
+    /// <returns>The JSON value object created from the input stream.</returns>
+    _ASYNCRTIMP static value __cdecl parse(utility::istream_t& input);
+
+    /// <summary>
+    /// Parses a JSON value from the contents of an input stream using the native platform character width.
+    /// </summary>
+    /// <param name="input">The stream to read the JSON value from</param>
+    /// <param name="errorCode">If parsing fails, the error code is greater than 0</param>
+    /// <returns>The parsed object. Returns web::json::value::null if failed</returns>
+    _ASYNCRTIMP static value __cdecl parse(utility::istream_t& input, std::error_code& errorCode);
+
+    /// <summary>
+    /// Writes the current JSON value to a stream with the native platform character width.
+    /// </summary>
+    /// <param name="stream">The stream that the JSON string representation should be written to.</param>
+    _ASYNCRTIMP void serialize(utility::ostream_t& stream) const;
+
+#ifdef _WIN32
+    /// <summary>
+    /// Parses a JSON value from the contents of a single-byte (UTF8) stream.
+    /// </summary>
+    /// <param name="stream">The stream to read the JSON value from</param>
+    _ASYNCRTIMP static value __cdecl parse(std::istream& stream);
+
+    /// <summary>
+    /// Parses a JSON value from the contents of a single-byte (UTF8) stream.
+    /// </summary>
+    /// <param name="stream">The stream to read the JSON value from</param>
+    /// <param name="errorCode">If parsing fails, the error code is greater than 0</param>
+    /// <returns>The parsed object. Returns web::json::value::null if failed</returns>
+    _ASYNCRTIMP static value __cdecl parse(std::istream& stream, std::error_code& error);
+
+    /// <summary>
+    /// Serializes the content of the value into a single-byte (UTF8) stream.
+    /// </summary>
+    /// <param name="stream">The stream that the JSON string representation should be written to.</param>
+    _ASYNCRTIMP void serialize(std::ostream& stream) const;
+#endif
+
+    /// <summary>
+    /// Converts the JSON value to a C++ double, if and only if it is a number value.
+    /// Throws <see cref="json_exception"/>  if the value is not a number
+    /// </summary>
+    /// <returns>A double representation of the value</returns>
+    _ASYNCRTIMP double as_double() const;
+
+    /// <summary>
+    /// Converts the JSON value to a C++ integer, if and only if it is a number value.
+    /// Throws <see cref="json_exception"/> if the value is not a number
+    /// </summary>
+    /// <returns>An integer representation of the value</returns>
+    _ASYNCRTIMP int as_integer() const;
+
+    /// <summary>
+    /// Converts the JSON value to a number class, if and only if it is a number value.
+    /// Throws <see cref="json_exception"/>  if the value is not a number
+    /// </summary>
+    /// <returns>An instance of number class</returns>
+    _ASYNCRTIMP const json::number& as_number() const;
+
+    /// <summary>
+    /// Converts the JSON value to a C++ bool, if and only if it is a Boolean value.
+    /// </summary>
+    /// <returns>A C++ bool representation of the value</returns>
+    _ASYNCRTIMP bool as_bool() const;
+
+    /// <summary>
+    /// Converts the JSON value to a json array, if and only if it is an array value.
+    /// </summary>
+    /// <remarks>The returned <c>json::array</c> should have the same or shorter lifetime as <c>this</c></remarks>
+    /// <returns>An array representation of the value</returns>
+    _ASYNCRTIMP json::array& as_array();
+
+    /// <summary>
+    /// Converts the JSON value to a json array, if and only if it is an array value.
+    /// </summary>
+    /// <remarks>The returned <c>json::array</c> should have the same or shorter lifetime as <c>this</c></remarks>
+    /// <returns>An array representation of the value</returns>
+    _ASYNCRTIMP const json::array& as_array() const;
+
+    /// <summary>
+    /// Converts the JSON value to a json object, if and only if it is an object value.
+    /// </summary>
+    /// <returns>An object representation of the value</returns>
+    _ASYNCRTIMP json::object& as_object();
+
+    /// <summary>
+    /// Converts the JSON value to a json object, if and only if it is an object value.
+    /// </summary>
+    /// <returns>An object representation of the value</returns>
+    _ASYNCRTIMP const json::object& as_object() const;
+
+    /// <summary>
+    /// Converts the JSON value to a C++ STL string, if and only if it is a string value.
+    /// </summary>
+    /// <returns>A C++ STL string representation of the value</returns>
+    _ASYNCRTIMP const utility::string_t& as_string() const;
+
+    /// <summary>
+    /// Compares two JSON values for equality.
+    /// </summary>
+    /// <param name="other">The JSON value to compare with.</param>
+    /// <returns>True if the values are equal.</returns>
+    _ASYNCRTIMP bool operator==(const value& other) const;
+
+    /// <summary>
+    /// Compares two JSON values for inequality.
+    /// </summary>
+    /// <param name="other">The JSON value to compare with.</param>
+    /// <returns>True if the values are unequal.</returns>
+    bool operator!=(const value& other) const { return !((*this) == other); }
+
+    /// <summary>
+    /// Tests for the presence of a field.
+    /// </summary>
+    /// <param name="key">The name of the field</param>
+    /// <returns>True if the field exists, false otherwise.</returns>
+    bool has_field(const utility::string_t& key) const;
+
+    /// <summary>
+    /// Tests for the presence of a number field
+    /// </summary>
+    /// <param name="key">The name of the field</param>
+    /// <returns>True if the field exists, false otherwise.</returns>
+    _ASYNCRTIMP bool has_number_field(const utility::string_t& key) const;
+
+    /// <summary>
+    /// Tests for the presence of an integer field
+    /// </summary>
+    /// <param name="key">The name of the field</param>
+    /// <returns>True if the field exists, false otherwise.</returns>
+    _ASYNCRTIMP bool has_integer_field(const utility::string_t& key) const;
+
+    /// <summary>
+    /// Tests for the presence of a double field
+    /// </summary>
+    /// <param name="key">The name of the field</param>
+    /// <returns>True if the field exists, false otherwise.</returns>
+    _ASYNCRTIMP bool has_double_field(const utility::string_t& key) const;
+
+    /// <summary>
+    /// Tests for the presence of a boolean field
+    /// </summary>
+    /// <param name="key">The name of the field</param>
+    /// <returns>True if the field exists, false otherwise.</returns>
+    _ASYNCRTIMP bool has_boolean_field(const utility::string_t& key) const;
+
+    /// <summary>
+    /// Tests for the presence of a string field
+    /// </summary>
+    /// <param name="key">The name of the field</param>
+    /// <returns>True if the field exists, false otherwise.</returns>
+    _ASYNCRTIMP bool has_string_field(const utility::string_t& key) const;
+
+    /// <summary>
+    /// Tests for the presence of an array field
+    /// </summary>
+    /// <param name="key">The name of the field</param>
+    /// <returns>True if the field exists, false otherwise.</returns>
+    _ASYNCRTIMP bool has_array_field(const utility::string_t& key) const;
+
+    /// <summary>
+    /// Tests for the presence of an object field
+    /// </summary>
+    /// <param name="key">The name of the field</param>
+    /// <returns>True if the field exists, false otherwise.</returns>
+    _ASYNCRTIMP bool has_object_field(const utility::string_t& key) const;
+
+    /// <summary>
+    /// Accesses a field of a JSON object.
+    /// </summary>
+    /// <param name="key">The name of the field</param>
+    /// <returns>The value kept in the field; null if the field does not exist</returns>
+    CASABLANCA_DEPRECATED(
+        "This API is deprecated and will be removed in a future release, use json::value::at() instead.")
+    value get(const utility::string_t& key) const;
+
+    /// <summary>
+    /// Erases an element of a JSON array. Throws if index is out of bounds.
+    /// </summary>
+    /// <param name="index">The index of the element to erase in the JSON array.</param>
+    _ASYNCRTIMP void erase(size_t index);
+
+    /// <summary>
+    /// Erases an element of a JSON object. Throws if the key doesn't exist.
+    /// </summary>
+    /// <param name="key">The key of the element to erase in the JSON object.</param>
+    _ASYNCRTIMP void erase(const utility::string_t& key);
+
+    /// <summary>
+    /// Accesses an element of a JSON array. Throws when index out of bounds.
+    /// </summary>
+    /// <param name="index">The index of an element in the JSON array.</param>
+    /// <returns>A reference to the value.</returns>
+    _ASYNCRTIMP json::value& at(size_t index);
+
+    /// <summary>
+    /// Accesses an element of a JSON array. Throws when index out of bounds.
+    /// </summary>
+    /// <param name="index">The index of an element in the JSON array.</param>
+    /// <returns>A reference to the value.</returns>
+    _ASYNCRTIMP const json::value& at(size_t index) const;
+
+    /// <summary>
+    /// Accesses an element of a JSON object. If the key doesn't exist, this method throws.
+    /// </summary>
+    /// <param name="key">The key of an element in the JSON object.</param>
+    /// <returns>If the key exists, a reference to the value.</returns>
+    _ASYNCRTIMP json::value& at(const utility::string_t& key);
+
+    /// <summary>
+    /// Accesses an element of a JSON object. If the key doesn't exist, this method throws.
+    /// </summary>
+    /// <param name="key">The key of an element in the JSON object.</param>
+    /// <returns>If the key exists, a reference to the value.</returns>
+    _ASYNCRTIMP const json::value& at(const utility::string_t& key) const;
+
+    /// <summary>
+    /// Accesses a field of a JSON object.
+    /// </summary>
+    /// <param name="key">The name of the field</param>
+    /// <returns>A reference to the value kept in the field.</returns>
+    _ASYNCRTIMP value& operator[](const utility::string_t& key);
+
+#ifdef _WIN32
+private:
+    // Only used internally by JSON parser
+    _ASYNCRTIMP value& operator[](const std::string& key)
+    {
+        // JSON object stores its field map as a unordered_map of string_t, so this conversion is hard to avoid
+        return operator[](utility::conversions::to_string_t(key));
+    }
+
+public:
+#endif
+
+    /// <summary>
+    /// Accesses an element of a JSON array.
+    /// </summary>
+    /// <param name="index">The index of an element in the JSON array</param>
+    /// <returns>The value kept at the array index; null if outside the boundaries of the array</returns>
+    CASABLANCA_DEPRECATED(
+        "This API is deprecated and will be removed in a future release, use json::value::at() instead.")
+    value get(size_t index) const;
+
+    /// <summary>
+    /// Accesses an element of a JSON array.
+    /// </summary>
+    /// <param name="index">The index of an element in the JSON array.</param>
+    /// <returns>A reference to the value kept in the field.</returns>
+    _ASYNCRTIMP value& operator[](size_t index);
+
+private:
+    friend class web::json::details::_Object;
+    friend class web::json::details::_Array;
+    template<typename CharType>
+    friend class web::json::details::JSON_Parser;
+
+#ifdef _WIN32
+    /// <summary>
+    /// Writes the current JSON value as a double-byte string to a string instance.
+    /// </summary>
+    /// <param name="string">The string that the JSON representation should be written to.</param>
+    _ASYNCRTIMP void format(std::basic_string<utf16char>& string) const;
+#endif
+    /// <summary>
+    /// Serializes the content of the value into a string instance in UTF8 format
+    /// </summary>
+    /// <param name="string">The string that the JSON representation should be written to</param>
+    _ASYNCRTIMP void format(std::basic_string<char>& string) const;
+
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    explicit value(std::unique_ptr<details::_Value> v, value_type kind) : m_value(std::move(v)), m_kind(kind)
+#else
+    explicit value(std::unique_ptr<details::_Value> v) : m_value(std::move(v))
+#endif
+    {
+    }
+
+    std::unique_ptr<details::_Value> m_value;
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    value_type m_kind;
+#endif
+};
+
+/// <summary>
+/// A single exception type to represent errors in parsing, converting, and accessing
+/// elements of JSON values.
+/// </summary>
+class json_exception : public std::exception
+{
+private:
+    std::string _message;
+
+public:
+    json_exception(const char* const message) : _message(message) {}
+#ifdef _UTF16_STRINGS
+    json_exception(const wchar_t* const message) : _message(utility::conversions::utf16_to_utf8(message)) {}
+#endif // _UTF16_STRINGS
+    json_exception(std::string&& message) : _message(std::move(message)) {}
+
+    // Must be narrow string because it derives from std::exception
+    const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); }
+};
+
+namespace details
+{
+enum json_error
+{
+    left_over_character_in_stream = 1,
+    malformed_array_literal,
+    malformed_comment,
+    malformed_literal,
+    malformed_object_literal,
+    malformed_numeric_literal,
+    malformed_string_literal,
+    malformed_token,
+    mismatched_brances,
+    nesting,
+    unexpected_token
+};
+
+class json_error_category_impl : public std::error_category
+{
+public:
+    virtual const char* name() const CPPREST_NOEXCEPT override { return "json"; }
+
+    virtual std::string message(int ev) const override
+    {
+        switch (ev)
+        {
+            case json_error::left_over_character_in_stream:
+                return "Left-over characters in stream after parsing a JSON value";
+            case json_error::malformed_array_literal: return "Malformed array literal";
+            case json_error::malformed_comment: return "Malformed comment";
+            case json_error::malformed_literal: return "Malformed literal";
+            case json_error::malformed_object_literal: return "Malformed object literal";
+            case json_error::malformed_numeric_literal: return "Malformed numeric literal";
+            case json_error::malformed_string_literal: return "Malformed string literal";
+            case json_error::malformed_token: return "Malformed token";
+            case json_error::mismatched_brances: return "Mismatched braces";
+            case json_error::nesting: return "Nesting too deep";
+            case json_error::unexpected_token: return "Unexpected token";
+            default: return "Unknown json error";
+        }
+    }
+};
+
+const json_error_category_impl& json_error_category();
+} // namespace details
+
+/// <summary>
+/// A JSON array represented as a C++ class.
+/// </summary>
+class array
+{
+    typedef std::vector<json::value> storage_type;
+
+public:
+    typedef storage_type::iterator iterator;
+    typedef storage_type::const_iterator const_iterator;
+    typedef storage_type::reverse_iterator reverse_iterator;
+    typedef storage_type::const_reverse_iterator const_reverse_iterator;
+    typedef storage_type::size_type size_type;
+
+private:
+    array() : m_elements() {}
+    array(size_type size) : m_elements(size) {}
+    array(storage_type elements) : m_elements(std::move(elements)) {}
+
+public:
+    /// <summary>
+    /// Gets the beginning iterator element of the array
+    /// </summary>
+    /// <returns>An <c>iterator</c> to the beginning of the JSON array.</returns>
+    iterator begin() { return m_elements.begin(); }
+
+    /// <summary>
+    /// Gets the beginning const iterator element of the array.
+    /// </summary>
+    /// <returns>A <c>const_iterator</c> to the beginning of the JSON array.</returns>
+    const_iterator begin() const { return m_elements.cbegin(); }
+
+    /// <summary>
+    /// Gets the end iterator element of the array
+    /// </summary>
+    /// <returns>An <c>iterator</c> to the end of the JSON array.</returns>
+    iterator end() { return m_elements.end(); }
+
+    /// <summary>
+    /// Gets the end const iterator element of the array.
+    /// </summary>
+    /// <returns>A <c>const_iterator</c> to the end of the JSON array.</returns>
+    const_iterator end() const { return m_elements.cend(); }
+
+    /// <summary>
+    /// Gets the beginning reverse iterator element of the array
+    /// </summary>
+    /// <returns>An <c>reverse_iterator</c> to the beginning of the JSON array.</returns>
+    reverse_iterator rbegin() { return m_elements.rbegin(); }
+
+    /// <summary>
+    /// Gets the beginning const reverse iterator element of the array
+    /// </summary>
+    /// <returns>An <c>const_reverse_iterator</c> to the beginning of the JSON array.</returns>
+    const_reverse_iterator rbegin() const { return m_elements.rbegin(); }
+
+    /// <summary>
+    /// Gets the end reverse iterator element of the array
+    /// </summary>
+    /// <returns>An <c>reverse_iterator</c> to the end of the JSON array.</returns>
+    reverse_iterator rend() { return m_elements.rend(); }
+
+    /// <summary>
+    /// Gets the end const reverse iterator element of the array
+    /// </summary>
+    /// <returns>An <c>const_reverse_iterator</c> to the end of the JSON array.</returns>
+    const_reverse_iterator rend() const { return m_elements.crend(); }
+
+    /// <summary>
+    /// Gets the beginning const iterator element of the array.
+    /// </summary>
+    /// <returns>A <c>const_iterator</c> to the beginning of the JSON array.</returns>
+    const_iterator cbegin() const { return m_elements.cbegin(); }
+
+    /// <summary>
+    /// Gets the end const iterator element of the array.
+    /// </summary>
+    /// <returns>A <c>const_iterator</c> to the end of the JSON array.</returns>
+    const_iterator cend() const { return m_elements.cend(); }
+
+    /// <summary>
+    /// Gets the beginning const reverse iterator element of the array.
+    /// </summary>
+    /// <returns>A <c>const_reverse_iterator</c> to the beginning of the JSON array.</returns>
+    const_reverse_iterator crbegin() const { return m_elements.crbegin(); }
+
+    /// <summary>
+    /// Gets the end const reverse iterator element of the array.
+    /// </summary>
+    /// <returns>A <c>const_reverse_iterator</c> to the end of the JSON array.</returns>
+    const_reverse_iterator crend() const { return m_elements.crend(); }
+
+    /// <summary>
+    /// Deletes an element of the JSON array.
+    /// </summary>
+    /// <param name="position">A const_iterator to the element to delete.</param>
+    /// <returns>Iterator to the new location of the element following the erased element.</returns>
+    /// <remarks>GCC doesn't support erase with const_iterator on vector yet. In the future this should be
+    /// changed.</remarks>
+    iterator erase(iterator position) { return m_elements.erase(position); }
+
+    /// <summary>
+    /// Deletes the element at an index of the JSON array.
+    /// </summary>
+    /// <param name="index">The index of the element to delete.</param>
+    void erase(size_type index)
+    {
+        if (index >= m_elements.size())
+        {
+            throw json_exception("index out of bounds");
+        }
+        m_elements.erase(m_elements.begin() + index);
+    }
+
+    /// <summary>
+    /// Accesses an element of a JSON array. Throws when index out of bounds.
+    /// </summary>
+    /// <param name="index">The index of an element in the JSON array.</param>
+    /// <returns>A reference to the value kept in the field.</returns>
+    json::value& at(size_type index)
+    {
+        if (index >= m_elements.size()) throw json_exception("index out of bounds");
+
+        return m_elements[index];
+    }
+
+    /// <summary>
+    /// Accesses an element of a JSON array. Throws when index out of bounds.
+    /// </summary>
+    /// <param name="index">The index of an element in the JSON array.</param>
+    /// <returns>A reference to the value kept in the field.</returns>
+    const json::value& at(size_type index) const
+    {
+        if (index >= m_elements.size()) throw json_exception("index out of bounds");
+
+        return m_elements[index];
+    }
+
+    /// <summary>
+    /// Accesses an element of a JSON array.
+    /// </summary>
+    /// <param name="index">The index of an element in the JSON array.</param>
+    /// <returns>A reference to the value kept in the field.</returns>
+    json::value& operator[](size_type index)
+    {
+        msl::safeint3::SafeInt<size_type> nMinSize(index);
+        nMinSize += 1;
+        msl::safeint3::SafeInt<size_type> nlastSize(m_elements.size());
+        if (nlastSize < nMinSize) m_elements.resize((size_type)nMinSize);
+
+        return m_elements[index];
+    }
+
+    /// <summary>
+    /// Gets the number of elements of the array.
+    /// </summary>
+    /// <returns>The number of elements.</returns>
+    size_type size() const { return m_elements.size(); }
+
+private:
+    storage_type m_elements;
+
+    friend class details::_Array;
+    template<typename CharType>
+    friend class json::details::JSON_Parser;
+};
+
+/// <summary>
+/// A JSON object represented as a C++ class.
+/// </summary>
+class object
+{
+    typedef std::vector<std::pair<utility::string_t, json::value>> storage_type;
+
+public:
+    typedef storage_type::iterator iterator;
+    typedef storage_type::const_iterator const_iterator;
+    typedef storage_type::reverse_iterator reverse_iterator;
+    typedef storage_type::const_reverse_iterator const_reverse_iterator;
+    typedef storage_type::size_type size_type;
+
+private:
+    object(bool keep_order = false) : m_elements(), m_keep_order(keep_order) {}
+    object(storage_type elements, bool keep_order = false) : m_elements(std::move(elements)), m_keep_order(keep_order)
+    {
+        if (!keep_order)
+        {
+            sort(m_elements.begin(), m_elements.end(), compare_pairs);
+        }
+    }
+
+public:
+    /// <summary>
+    /// Gets the beginning iterator element of the object
+    /// </summary>
+    /// <returns>An <c>iterator</c> to the beginning of the JSON object.</returns>
+    iterator begin() { return m_elements.begin(); }
+
+    /// <summary>
+    /// Gets the beginning const iterator element of the object.
+    /// </summary>
+    /// <returns>A <c>const_iterator</c> to the beginning of the JSON object.</returns>
+    const_iterator begin() const { return m_elements.cbegin(); }
+
+    /// <summary>
+    /// Gets the end iterator element of the object
+    /// </summary>
+    /// <returns>An <c>iterator</c> to the end of the JSON object.</returns>
+    iterator end() { return m_elements.end(); }
+
+    /// <summary>
+    /// Gets the end const iterator element of the object.
+    /// </summary>
+    /// <returns>A <c>const_iterator</c> to the end of the JSON object.</returns>
+    const_iterator end() const { return m_elements.cend(); }
+
+    /// <summary>
+    /// Gets the beginning reverse iterator element of the object
+    /// </summary>
+    /// <returns>An <c>reverse_iterator</c> to the beginning of the JSON object.</returns>
+    reverse_iterator rbegin() { return m_elements.rbegin(); }
+
+    /// <summary>
+    /// Gets the beginning const reverse iterator element of the object
+    /// </summary>
+    /// <returns>An <c>const_reverse_iterator</c> to the beginning of the JSON object.</returns>
+    const_reverse_iterator rbegin() const { return m_elements.rbegin(); }
+
+    /// <summary>
+    /// Gets the end reverse iterator element of the object
+    /// </summary>
+    /// <returns>An <c>reverse_iterator</c> to the end of the JSON object.</returns>
+    reverse_iterator rend() { return m_elements.rend(); }
+
+    /// <summary>
+    /// Gets the end const reverse iterator element of the object
+    /// </summary>
+    /// <returns>An <c>const_reverse_iterator</c> to the end of the JSON object.</returns>
+    const_reverse_iterator rend() const { return m_elements.crend(); }
+
+    /// <summary>
+    /// Gets the beginning const iterator element of the object.
+    /// </summary>
+    /// <returns>A <c>const_iterator</c> to the beginning of the JSON object.</returns>
+    const_iterator cbegin() const { return m_elements.cbegin(); }
+
+    /// <summary>
+    /// Gets the end const iterator element of the object.
+    /// </summary>
+    /// <returns>A <c>const_iterator</c> to the end of the JSON object.</returns>
+    const_iterator cend() const { return m_elements.cend(); }
+
+    /// <summary>
+    /// Gets the beginning const reverse iterator element of the object.
+    /// </summary>
+    /// <returns>A <c>const_reverse_iterator</c> to the beginning of the JSON object.</returns>
+    const_reverse_iterator crbegin() const { return m_elements.crbegin(); }
+
+    /// <summary>
+    /// Gets the end const reverse iterator element of the object.
+    /// </summary>
+    /// <returns>A <c>const_reverse_iterator</c> to the end of the JSON object.</returns>
+    const_reverse_iterator crend() const { return m_elements.crend(); }
+
+    /// <summary>
+    /// Deletes an element of the JSON object.
+    /// </summary>
+    /// <param name="position">A const_iterator to the element to delete.</param>
+    /// <returns>Iterator to the new location of the element following the erased element.</returns>
+    /// <remarks>GCC doesn't support erase with const_iterator on vector yet. In the future this should be
+    /// changed.</remarks>
+    iterator erase(iterator position) { return m_elements.erase(position); }
+
+    /// <summary>
+    /// Deletes an element of the JSON object. If the key doesn't exist, this method throws.
+    /// </summary>
+    /// <param name="key">The key of an element in the JSON object.</param>
+    void erase(const utility::string_t& key)
+    {
+        auto iter = find_by_key(key);
+        if (iter == m_elements.end())
+        {
+            throw web::json::json_exception("Key not found");
+        }
+
+        m_elements.erase(iter);
+    }
+
+    /// <summary>
+    /// Accesses an element of a JSON object. If the key doesn't exist, this method throws.
+    /// </summary>
+    /// <param name="key">The key of an element in the JSON object.</param>
+    /// <returns>If the key exists, a reference to the value kept in the field.</returns>
+    json::value& at(const utility::string_t& key)
+    {
+        auto iter = find_by_key(key);
+        if (iter == m_elements.end())
+        {
+            throw web::json::json_exception("Key not found");
+        }
+
+        return iter->second;
+    }
+
+    /// <summary>
+    /// Accesses an element of a JSON object. If the key doesn't exist, this method throws.
+    /// </summary>
+    /// <param name="key">The key of an element in the JSON object.</param>
+    /// <returns>If the key exists, a reference to the value kept in the field.</returns>
+    const json::value& at(const utility::string_t& key) const
+    {
+        auto iter = find_by_key(key);
+        if (iter == m_elements.end())
+        {
+            throw web::json::json_exception("Key not found");
+        }
+
+        return iter->second;
+    }
+
+    /// <summary>
+    /// Accesses an element of a JSON object.
+    /// </summary>
+    /// <param name="key">The key of an element in the JSON object.</param>
+    /// <returns>If the key exists, a reference to the value kept in the field, otherwise a newly created null value
+    /// that will be stored for the given key.</returns>
+    json::value& operator[](const utility::string_t& key)
+    {
+        auto iter = find_insert_location(key);
+
+        if (iter == m_elements.end() || key != iter->first)
+        {
+            return m_elements.insert(iter, std::pair<utility::string_t, value>(key, value()))->second;
+        }
+
+        return iter->second;
+    }
+
+    /// <summary>
+    /// Gets an iterator to an element of a JSON object.
+    /// </summary>
+    /// <param name="key">The key of an element in the JSON object.</param>
+    /// <returns>A const iterator to the value kept in the field.</returns>
+    const_iterator find(const utility::string_t& key) const { return find_by_key(key); }
+
+    /// <summary>
+    /// Gets the number of elements of the object.
+    /// </summary>
+    /// <returns>The number of elements.</returns>
+    size_type size() const { return m_elements.size(); }
+
+    /// <summary>
+    /// Checks if there are any elements in the JSON object.
+    /// </summary>
+    /// <returns>True if empty.</returns>
+    bool empty() const { return m_elements.empty(); }
+
+private:
+    static bool compare_pairs(const std::pair<utility::string_t, value>& p1,
+                              const std::pair<utility::string_t, value>& p2)
+    {
+        return p1.first < p2.first;
+    }
+    static bool compare_with_key(const std::pair<utility::string_t, value>& p1, const utility::string_t& key)
+    {
+        return p1.first < key;
+    }
+
+    storage_type::iterator find_insert_location(const utility::string_t& key)
+    {
+        if (m_keep_order)
+        {
+            return std::find_if(m_elements.begin(),
+                                m_elements.end(),
+                                [&key](const std::pair<utility::string_t, value>& p) { return p.first == key; });
+        }
+        else
+        {
+            return std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key);
+        }
+    }
+
+    storage_type::const_iterator find_by_key(const utility::string_t& key) const
+    {
+        if (m_keep_order)
+        {
+            return std::find_if(m_elements.begin(),
+                                m_elements.end(),
+                                [&key](const std::pair<utility::string_t, value>& p) { return p.first == key; });
+        }
+        else
+        {
+            auto iter = std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key);
+            if (iter != m_elements.end() && key != iter->first)
+            {
+                return m_elements.end();
+            }
+            return iter;
+        }
+    }
+
+    storage_type::iterator find_by_key(const utility::string_t& key)
+    {
+        auto iter = find_insert_location(key);
+        if (iter != m_elements.end() && key != iter->first)
+        {
+            return m_elements.end();
+        }
+        return iter;
+    }
+
+    storage_type m_elements;
+    bool m_keep_order;
+    friend class details::_Object;
+
+    template<typename CharType>
+    friend class json::details::JSON_Parser;
+};
+
+/// <summary>
+/// A JSON number represented as a C++ class.
+/// </summary>
+class number
+{
+    // Note that these constructors make sure that only negative integers are stored as signed int64 (while others
+    // 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) {}
+
+public:
+    /// <summary>
+    /// Does the number fit into int32?
+    /// </summary>
+    /// <returns><c>true</c> if the number fits into int32, <c>false</c> otherwise</returns>
+    _ASYNCRTIMP bool is_int32() const;
+
+    /// <summary>
+    /// Does the number fit into unsigned int32?
+    /// </summary>
+    /// <returns><c>true</c> if the number fits into unsigned int32, <c>false</c> otherwise</returns>
+    _ASYNCRTIMP bool is_uint32() const;
+
+    /// <summary>
+    /// Does the number fit into int64?
+    /// </summary>
+    /// <returns><c>true</c> if the number fits into int64, <c>false</c> otherwise</returns>
+    _ASYNCRTIMP bool is_int64() const;
+
+    /// <summary>
+    /// Does the number fit into unsigned int64?
+    /// </summary>
+    /// <returns><c>true</c> if the number fits into unsigned int64, <c>false</c> otherwise</returns>
+    bool is_uint64() const
+    {
+        switch (m_type)
+        {
+            case signed_type: return m_intval >= 0;
+            case unsigned_type: return true;
+            case double_type:
+            default: return false;
+        }
+    }
+
+    /// <summary>
+    /// Converts the JSON number to a C++ double.
+    /// </summary>
+    /// <returns>A double representation of the number</returns>
+    double to_double() const
+    {
+        switch (m_type)
+        {
+            case double_type: return m_value;
+            case signed_type: return static_cast<double>(m_intval);
+            case unsigned_type: return static_cast<double>(m_uintval);
+            default: return false;
+        }
+    }
+
+    /// <summary>
+    /// Converts the JSON number to int32.
+    /// </summary>
+    /// <returns>An int32 representation of the number</returns>
+    int32_t to_int32() const
+    {
+        if (m_type == double_type)
+            return static_cast<int32_t>(m_value);
+        else
+            return static_cast<int32_t>(m_intval);
+    }
+
+    /// <summary>
+    /// Converts the JSON number to unsigned int32.
+    /// </summary>
+    /// <returns>An unsigned int32 representation of the number</returns>
+    uint32_t to_uint32() const
+    {
+        if (m_type == double_type)
+            return static_cast<uint32_t>(m_value);
+        else
+            return static_cast<uint32_t>(m_intval);
+    }
+
+    /// <summary>
+    /// Converts the JSON number to int64.
+    /// </summary>
+    /// <returns>An int64 representation of the number</returns>
+    int64_t to_int64() const
+    {
+        if (m_type == double_type)
+            return static_cast<int64_t>(m_value);
+        else
+            return static_cast<int64_t>(m_intval);
+    }
+
+    /// <summary>
+    /// Converts the JSON number to unsigned int64.
+    /// </summary>
+    /// <returns>An unsigned int64 representation of the number</returns>
+    uint64_t to_uint64() const
+    {
+        if (m_type == double_type)
+            return static_cast<uint64_t>(m_value);
+        else
+            return static_cast<uint64_t>(m_intval);
+    }
+
+    /// <summary>
+    /// Is the number represented internally as an integral type?
+    /// </summary>
+    /// <returns><c>true</c> if the number is represented as an integral type, <c>false</c> otherwise</returns>
+    bool is_integral() const { return m_type != double_type; }
+
+    /// <summary>
+    /// Compares two JSON numbers for equality.
+    /// </summary>
+    /// <param name="other">The JSON number to compare with.</param>
+    /// <returns>True if the numbers are equal.</returns>
+    bool operator==(const number& other) const
+    {
+        if (m_type != other.m_type) return false;
+
+        switch (m_type)
+        {
+            case json::number::type::signed_type: return m_intval == other.m_intval;
+            case json::number::type::unsigned_type: return m_uintval == other.m_uintval;
+            case json::number::type::double_type: return m_value == other.m_value;
+        }
+        __assume(0);
+        // Absence of this return statement provokes a warning from Intel
+        // compiler, but its presence results in a warning from MSVC, so
+        // we have to resort to conditional compilation to keep both happy.
+#ifdef __INTEL_COMPILER
+        return false;
+#endif
+    }
+
+private:
+    union {
+        int64_t m_intval;
+        uint64_t m_uintval;
+        double m_value;
+    };
+
+    enum type
+    {
+        signed_type = 0,
+        unsigned_type,
+        double_type
+    } m_type;
+
+    friend class details::_Number;
+};
+
+namespace details
+{
+class _Value
+{
+public:
+    virtual std::unique_ptr<_Value> _copy_value() = 0;
+
+    virtual bool has_field(const utility::string_t&) const { return false; }
+    virtual value get_field(const utility::string_t&) const { throw json_exception("not an object"); }
+    virtual value get_element(array::size_type) const { throw json_exception("not an array"); }
+
+    virtual value& index(const utility::string_t&) { throw json_exception("not an object"); }
+    virtual value& index(array::size_type) { throw json_exception("not an array"); }
+
+    virtual const value& cnst_index(const utility::string_t&) const { throw json_exception("not an object"); }
+    virtual const value& cnst_index(array::size_type) const { throw json_exception("not an array"); }
+
+    // Common function used for serialization to strings and streams.
+    virtual void serialize_impl(std::string& str) const { format(str); }
+#ifdef _WIN32
+    virtual void serialize_impl(std::wstring& str) const { format(str); }
+#endif
+
+    virtual utility::string_t to_string() const
+    {
+        utility::string_t str;
+        serialize_impl(str);
+        return str;
+    }
+
+    virtual json::value::value_type type() const { return json::value::Null; }
+
+    virtual bool is_integer() const { throw json_exception("not a number"); }
+    virtual bool is_double() const { throw json_exception("not a number"); }
+
+    virtual const json::number& as_number() { throw json_exception("not a number"); }
+    virtual double as_double() const { throw json_exception("not a number"); }
+    virtual int as_integer() const { throw json_exception("not a number"); }
+    virtual bool as_bool() const { throw json_exception("not a boolean"); }
+    virtual json::array& as_array() { throw json_exception("not an array"); }
+    virtual const json::array& as_array() const { throw json_exception("not an array"); }
+    virtual json::object& as_object() { throw json_exception("not an object"); }
+    virtual const json::object& as_object() const { throw json_exception("not an object"); }
+    virtual const utility::string_t& as_string() const { throw json_exception("not a string"); }
+
+    virtual size_t size() const { return 0; }
+
+    virtual ~_Value() {}
+
+protected:
+    _Value() {}
+
+    virtual void format(std::basic_string<char>& stream) const { stream.append("null"); }
+#ifdef _WIN32
+    virtual void format(std::basic_string<wchar_t>& stream) const { stream.append(L"null"); }
+#endif
+private:
+    friend class web::json::value;
+};
+
+class _Null : public _Value
+{
+public:
+    virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Null>(); }
+    virtual json::value::value_type type() const { return json::value::Null; }
+};
+
+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) {}
+
+    virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Number>(*this); }
+
+    virtual json::value::value_type type() const { return json::value::Number; }
+
+    virtual bool is_integer() const { return m_number.is_integral(); }
+    virtual bool is_double() const { return !m_number.is_integral(); }
+
+    virtual double as_double() const { return m_number.to_double(); }
+
+    virtual int as_integer() const { return m_number.to_int32(); }
+
+    virtual const number& as_number() { return m_number; }
+
+protected:
+    virtual void format(std::basic_string<char>& stream) const;
+#ifdef _WIN32
+    virtual void format(std::basic_string<wchar_t>& stream) const;
+#endif
+private:
+    template<typename CharType>
+    friend class json::details::JSON_Parser;
+
+    json::number m_number;
+};
+
+class _Boolean : public _Value
+{
+public:
+    _Boolean(bool value) : m_value(value) {}
+
+    virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Boolean>(*this); }
+
+    virtual json::value::value_type type() const { return json::value::Boolean; }
+
+    virtual bool as_bool() const { return m_value; }
+
+protected:
+    virtual void format(std::basic_string<char>& stream) const { stream.append(m_value ? "true" : "false"); }
+
+#ifdef _WIN32
+    virtual void format(std::basic_string<wchar_t>& stream) const { stream.append(m_value ? L"true" : L"false"); }
+#endif
+private:
+    template<typename CharType>
+    friend class json::details::JSON_Parser;
+    bool m_value;
+};
+
+class _String : public _Value
+{
+public:
+    _String(utility::string_t value) : m_string(std::move(value)) { m_has_escape_char = has_escape_chars(*this); }
+    _String(utility::string_t value, bool escaped_chars) : m_string(std::move(value)), m_has_escape_char(escaped_chars)
+    {
+    }
+
+#ifdef _WIN32
+    _String(std::string&& value) : m_string(utility::conversions::to_utf16string(std::move(value)))
+    {
+        m_has_escape_char = has_escape_chars(*this);
+    }
+    _String(std::string&& value, bool escape_chars)
+        : m_string(utility::conversions::to_utf16string(std::move(value))), m_has_escape_char(escape_chars)
+    {
+    }
+#endif
+
+    virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_String>(*this); }
+
+    virtual json::value::value_type type() const { return json::value::String; }
+
+    virtual const utility::string_t& as_string() const;
+
+    virtual void serialize_impl(std::string& str) const { serialize_impl_char_type(str); }
+#ifdef _WIN32
+    virtual void serialize_impl(std::wstring& str) const { serialize_impl_char_type(str); }
+#endif
+
+protected:
+    virtual void format(std::basic_string<char>& str) const;
+#ifdef _WIN32
+    virtual void format(std::basic_string<wchar_t>& str) const;
+#endif
+
+private:
+    friend class _Object;
+    friend class _Array;
+
+    size_t get_reserve_size() const { return m_string.size() + 2; }
+
+    template<typename CharType>
+    void serialize_impl_char_type(std::basic_string<CharType>& str) const
+    {
+        // To avoid repeated allocations reserve some space all up front.
+        // size of string + 2 for quotes
+        str.reserve(get_reserve_size());
+        format(str);
+    }
+
+    std::string as_utf8_string() const;
+    utf16string as_utf16_string() const;
+
+    utility::string_t m_string;
+
+    // There are significant performance gains that can be made by knowing whether
+    // or not a character that requires escaping is present.
+    bool m_has_escape_char;
+    static bool has_escape_chars(const _String& str);
+};
+
+template<typename CharType>
+_ASYNCRTIMP void append_escape_string(std::basic_string<CharType>& str, const std::basic_string<CharType>& escaped);
+
+void format_string(const utility::string_t& key, utility::string_t& str);
+
+#ifdef _WIN32
+void format_string(const utility::string_t& key, std::string& str);
+#endif
+
+class _Object : public _Value
+{
+public:
+    _Object(bool keep_order) : m_object(keep_order) {}
+    _Object(object::storage_type fields, bool keep_order) : m_object(std::move(fields), keep_order) {}
+
+    virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Object>(*this); }
+
+    virtual json::object& as_object() { return m_object; }
+
+    virtual const json::object& as_object() const { return m_object; }
+
+    virtual json::value::value_type type() const { return json::value::Object; }
+
+    virtual bool has_field(const utility::string_t&) const;
+
+    virtual json::value& index(const utility::string_t& key);
+
+    bool is_equal(const _Object* other) const
+    {
+        if (m_object.size() != other->m_object.size()) return false;
+
+        return std::equal(std::begin(m_object), std::end(m_object), std::begin(other->m_object));
+    }
+
+    virtual void serialize_impl(std::string& str) const
+    {
+        // To avoid repeated allocations reserve some space all up front.
+        str.reserve(get_reserve_size());
+        format(str);
+    }
+#ifdef _WIN32
+    virtual void serialize_impl(std::wstring& str) const
+    {
+        // To avoid repeated allocations reserve some space all up front.
+        str.reserve(get_reserve_size());
+        format(str);
+    }
+#endif
+    size_t size() const { return m_object.size(); }
+
+protected:
+    virtual void format(std::basic_string<char>& str) const { format_impl(str); }
+#ifdef _WIN32
+    virtual void format(std::basic_string<wchar_t>& str) const { format_impl(str); }
+#endif
+
+private:
+    json::object m_object;
+
+    template<typename CharType>
+    friend class json::details::JSON_Parser;
+
+    template<typename CharType>
+    void format_impl(std::basic_string<CharType>& str) const
+    {
+        str.push_back('{');
+        if (!m_object.empty())
+        {
+            auto lastElement = m_object.end() - 1;
+            for (auto iter = m_object.begin(); iter != lastElement; ++iter)
+            {
+                format_string(iter->first, str);
+                str.push_back(':');
+                iter->second.format(str);
+                str.push_back(',');
+            }
+            format_string(lastElement->first, str);
+            str.push_back(':');
+            lastElement->second.format(str);
+        }
+        str.push_back('}');
+    }
+
+    size_t get_reserve_size() const
+    {
+        // This is a heuristic we can tune more in the future:
+        // Basically size of string plus
+        // sum size of value if an object, array, or string.
+        size_t reserveSize = 2; // For brackets {}
+        for (auto iter = m_object.begin(); iter != m_object.end(); ++iter)
+        {
+            reserveSize += iter->first.length() + 2;     // 2 for quotes
+            size_t valueSize = iter->second.size() * 20; // Multiply by each object/array element
+            if (valueSize == 0)
+            {
+                if (iter->second.type() == json::value::String)
+                {
+                    valueSize = static_cast<_String*>(iter->second.m_value.get())->get_reserve_size();
+                }
+                else
+                {
+                    valueSize = 5; // true, false, or null
+                }
+            }
+            reserveSize += valueSize;
+        }
+        return reserveSize;
+    }
+};
+
+class _Array : public _Value
+{
+public:
+    _Array() {}
+    _Array(array::size_type size) : m_array(size) {}
+    _Array(array::storage_type elements) : m_array(std::move(elements)) {}
+
+    virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Array>(*this); }
+
+    virtual json::value::value_type type() const { return json::value::Array; }
+
+    virtual json::array& as_array() { return m_array; }
+    virtual const json::array& as_array() const { return m_array; }
+
+    virtual json::value& index(json::array::size_type index) { return m_array[index]; }
+
+    bool is_equal(const _Array* other) const
+    {
+        if (m_array.size() != other->m_array.size()) return false;
+
+        auto iterT = m_array.cbegin();
+        auto iterO = other->m_array.cbegin();
+        auto iterTe = m_array.cend();
+        auto iterOe = other->m_array.cend();
+
+        for (; iterT != iterTe && iterO != iterOe; ++iterT, ++iterO)
+        {
+            if (*iterT != *iterO) return false;
+        }
+
+        return true;
+    }
+
+    virtual void serialize_impl(std::string& str) const
+    {
+        // To avoid repeated allocations reserve some space all up front.
+        str.reserve(get_reserve_size());
+        format(str);
+    }
+#ifdef _WIN32
+    virtual void serialize_impl(std::wstring& str) const
+    {
+        // To avoid repeated allocations reserve some space all up front.
+        str.reserve(get_reserve_size());
+        format(str);
+    }
+#endif
+    size_t size() const { return m_array.size(); }
+
+protected:
+    virtual void format(std::basic_string<char>& str) const { format_impl(str); }
+#ifdef _WIN32
+    virtual void format(std::basic_string<wchar_t>& str) const { format_impl(str); }
+#endif
+private:
+    json::array m_array;
+
+    template<typename CharType>
+    friend class json::details::JSON_Parser;
+
+    template<typename CharType>
+    void format_impl(std::basic_string<CharType>& str) const
+    {
+        str.push_back('[');
+        if (!m_array.m_elements.empty())
+        {
+            auto lastElement = m_array.m_elements.end() - 1;
+            for (auto iter = m_array.m_elements.begin(); iter != lastElement; ++iter)
+            {
+                iter->format(str);
+                str.push_back(',');
+            }
+            lastElement->format(str);
+        }
+        str.push_back(']');
+    }
+
+    size_t get_reserve_size() const
+    {
+        // This is a heuristic we can tune more in the future:
+        // Basically sum size of each value if an object, array, or string by a multiplier.
+        size_t reserveSize = 2; // For brackets []
+        for (auto iter = m_array.cbegin(); iter != m_array.cend(); ++iter)
+        {
+            size_t valueSize = iter->size() * 20; // Per each nested array/object
+
+            if (valueSize == 0) valueSize = 5; // true, false, or null
+
+            reserveSize += valueSize;
+        }
+        return reserveSize;
+    }
+};
+} // namespace details
+
+/// <summary>
+/// Gets the number of children of the value.
+/// </summary>
+/// <returns>The number of children. 0 for all non-composites.</returns>
+inline size_t json::value::size() const { return m_value->size(); }
+
+/// <summary>
+/// Test for the presence of a field.
+/// </summary>
+/// <param name="key">The name of the field</param>
+/// <returns>True if the field exists, false otherwise.</returns>
+inline bool json::value::has_field(const utility::string_t& key) const { return m_value->has_field(key); }
+
+/// <summary>
+/// Access a field of a JSON object.
+/// </summary>
+/// <param name="key">The name of the field</param>
+/// <returns>The value kept in the field; null if the field does not exist</returns>
+inline json::value json::value::get(const utility::string_t& key) const { return m_value->get_field(key); }
+
+/// <summary>
+/// Access an element of a JSON array.
+/// </summary>
+/// <param name="index">The index of an element in the JSON array</param>
+/// <returns>The value kept at the array index; null if outside the boundaries of the array</returns>
+inline json::value json::value::get(size_t index) const { return m_value->get_element(index); }
+
+/// <summary>
+/// A standard <c>std::ostream</c> operator to facilitate writing JSON values to streams.
+/// </summary>
+/// <param name="os">The output stream to write the JSON value to.</param>
+/// <param name="val">The JSON value to be written to the stream.</param>
+/// <returns>The output stream object</returns>
+_ASYNCRTIMP utility::ostream_t& __cdecl operator<<(utility::ostream_t& os, const json::value& val);
+
+/// <summary>
+/// A standard <c>std::istream</c> operator to facilitate reading JSON values from streams.
+/// </summary>
+/// <param name="is">The input stream to read the JSON value from.</param>
+/// <param name="val">The JSON value object read from the stream.</param>
+/// <returns>The input stream object.</returns>
+_ASYNCRTIMP utility::istream_t& __cdecl operator>>(utility::istream_t& is, json::value& val);
+} // namespace json
+} // namespace web
+
+#endif
diff --git a/Release/include/cpprest/oauth1.h b/Release/include/cpprest/oauth1.h
new file mode 100644 (file)
index 0000000..dbc45bd
--- /dev/null
@@ -0,0 +1,576 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Oauth 1.0
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_OAUTH1_H
+#define CASA_OAUTH1_H
+
+#include "cpprest/details/web_utilities.h"
+#include "cpprest/http_msg.h"
+
+namespace web
+{
+namespace http
+{
+namespace client
+{
+// Forward declaration to avoid circular include dependency.
+class http_client_config;
+} // namespace client
+
+/// oAuth 1.0 library.
+namespace oauth1
+{
+namespace details
+{
+class oauth1_handler;
+
+// State currently used by oauth1_config to authenticate request.
+// The state varies for every request (due to timestamp and nonce).
+// The state also contains extra transmitted protocol parameters during
+// authorization flow (i.e. 'oauth_callback' or 'oauth_verifier').
+class oauth1_state
+{
+public:
+    oauth1_state(utility::string_t timestamp,
+                 utility::string_t nonce,
+                 utility::string_t extra_key = utility::string_t(),
+                 utility::string_t extra_value = utility::string_t())
+        : m_timestamp(std::move(timestamp))
+        , m_nonce(std::move(nonce))
+        , m_extra_key(std::move(extra_key))
+        , m_extra_value(std::move(extra_value))
+    {
+    }
+
+    const utility::string_t& timestamp() const { return m_timestamp; }
+    void set_timestamp(utility::string_t timestamp) { m_timestamp = std::move(timestamp); }
+
+    const utility::string_t& nonce() const { return m_nonce; }
+    void set_nonce(utility::string_t nonce) { m_nonce = std::move(nonce); }
+
+    const utility::string_t& extra_key() const { return m_extra_key; }
+    void set_extra_key(utility::string_t key) { m_extra_key = std::move(key); }
+
+    const utility::string_t& extra_value() const { return m_extra_value; }
+    void set_extra_value(utility::string_t value) { m_extra_value = std::move(value); }
+
+private:
+    utility::string_t m_timestamp;
+    utility::string_t m_nonce;
+    utility::string_t m_extra_key;
+    utility::string_t m_extra_value;
+};
+
+// Constant strings for OAuth 1.0.
+typedef utility::string_t oauth1_string;
+class oauth1_strings
+{
+public:
+#define _OAUTH1_STRINGS
+#define DAT(a_, b_) _ASYNCRTIMP static const oauth1_string a_;
+#include "cpprest/details/http_constants.dat"
+#undef _OAUTH1_STRINGS
+#undef DAT
+};
+
+} // namespace details
+
+/// oAuth functionality is currently in beta.
+namespace experimental
+{
+/// <summary>
+/// Constant strings for OAuth 1.0 signature methods.
+/// </summary>
+typedef utility::string_t oauth1_method;
+class oauth1_methods
+{
+public:
+#define _OAUTH1_METHODS
+#define DAT(a, b) _ASYNCRTIMP static const oauth1_method a;
+#include "cpprest/details/http_constants.dat"
+#undef _OAUTH1_METHODS
+#undef DAT
+};
+
+/// <summary>
+/// Exception type for OAuth 1.0 errors.
+/// </summary>
+class oauth1_exception : public std::exception
+{
+public:
+    oauth1_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {}
+    ~oauth1_exception() CPPREST_NOEXCEPT {}
+    const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
+
+private:
+    std::string m_msg;
+};
+
+/// <summary>
+/// OAuth 1.0 token and associated information.
+/// </summary>
+class oauth1_token
+{
+public:
+    /// <summary>
+    /// Constructs an initially empty invalid access token.
+    /// </summary>
+    oauth1_token() {}
+
+    /// <summary>
+    /// Constructs a OAuth1 token from a given access token and secret.
+    /// </summary>
+    /// <param name="access_token">Access token string.</param>
+    /// <param name="secret">Token secret string.</param>
+    oauth1_token(utility::string_t access_token, utility::string_t secret)
+        : m_token(std::move(access_token)), m_secret(std::move(secret))
+    {
+    }
+
+    /// <summary>
+    /// Get access token validity state.
+    /// If true, token is a valid access token.
+    /// </summary>
+    /// <returns>Access token validity state of the token.</returns>
+    bool is_valid_access_token() const { return !(access_token().empty() || secret().empty()); }
+
+    /// <summary>
+    /// Get access token.
+    /// </summary>
+    /// <returns>The access token string.</returns>
+    const utility::string_t& access_token() const { return m_token; }
+
+    /// <summary>
+    /// Set access token.
+    /// </summary>
+    /// <param name="access_token">Access token string to set.</param>
+    void set_access_token(utility::string_t&& access_token) { m_token = std::move(access_token); }
+
+    /// <summary>
+    /// Set access token.
+    /// </summary>
+    /// <param name="access_token">Access token string to set.</param>
+    void set_access_token(const utility::string_t& access_token) { m_token = access_token; }
+
+    /// <summary>
+    /// Get token secret.
+    /// </summary>
+    /// <returns>Token secret string.</returns>
+    const utility::string_t& secret() const { return m_secret; }
+
+    /// <summary>
+    /// Set token secret.
+    /// </summary>
+    /// <param name="secret">Token secret string to set.</param>
+    void set_secret(utility::string_t&& secret) { m_secret = std::move(secret); }
+
+    /// <summary>
+    /// Set token secret.
+    /// </summary>
+    /// <param name="secret">Token secret string to set.</param>
+    void set_secret(const utility::string_t& secret) { m_secret = secret; }
+
+    /// <summary>
+    /// Retrieves any additional parameters.
+    /// </summary>
+    /// <returns>A map containing the additional parameters.</returns>
+    const std::map<utility::string_t, utility::string_t>& additional_parameters() const
+    {
+        return m_additional_parameters;
+    }
+
+    /// <summary>
+    /// Sets a specific parameter additional parameter.
+    /// </summary>
+    /// <param name="paramName">Parameter name.</param>
+    /// <param name="paramValue">Parameter value.</param>
+    void set_additional_parameter(utility::string_t&& paramName, utility::string_t&& paramValue)
+    {
+        m_additional_parameters[std::move(paramName)] = std::move(paramValue);
+    }
+
+    /// <summary>
+    /// Sets a specific parameter additional parameter.
+    /// </summary>
+    /// <param name="paramName">Parameter name.</param>
+    /// <param name="paramValue">Parameter value.</param>
+    void set_additional_parameter(const utility::string_t& paramName, const utility::string_t& paramValue)
+    {
+        m_additional_parameters[paramName] = paramValue;
+    }
+
+    /// <summary>
+    /// Clears all additional parameters.
+    /// </summary>
+    void clear_additional_parameters() { m_additional_parameters.clear(); }
+
+private:
+    friend class oauth1_config;
+
+    utility::string_t m_token;
+    utility::string_t m_secret;
+    std::map<utility::string_t, utility::string_t> m_additional_parameters;
+};
+
+/// <summary>
+/// OAuth 1.0 configuration class.
+/// </summary>
+class oauth1_config
+{
+public:
+    oauth1_config(utility::string_t consumer_key,
+                  utility::string_t consumer_secret,
+                  utility::string_t temp_endpoint,
+                  utility::string_t auth_endpoint,
+                  utility::string_t token_endpoint,
+                  utility::string_t callback_uri,
+                  oauth1_method method,
+                  utility::string_t realm = utility::string_t())
+        : m_consumer_key(std::move(consumer_key))
+        , m_consumer_secret(std::move(consumer_secret))
+        , m_temp_endpoint(std::move(temp_endpoint))
+        , m_auth_endpoint(std::move(auth_endpoint))
+        , m_token_endpoint(std::move(token_endpoint))
+        , m_callback_uri(std::move(callback_uri))
+        , m_realm(std::move(realm))
+        , m_method(std::move(method))
+        , m_is_authorization_completed(false)
+    {
+    }
+
+    /// <summary>
+    /// Builds an authorization URI to be loaded in a web browser/view.
+    /// The URI is built with auth_endpoint() as basis.
+    /// The method creates a task for HTTP request to first obtain a
+    /// temporary token. The authorization URI build based on this token.
+    /// </summary>
+    /// <returns>Authorization URI to be loaded in a web browser/view.</returns>
+    _ASYNCRTIMP pplx::task<utility::string_t> build_authorization_uri();
+
+    /// <summary>
+    /// Fetch an access token based on redirected URI.
+    /// The URI is expected to contain 'oauth_verifier'
+    /// parameter, which is then used to fetch an access token using the
+    /// token_from_verifier() method.
+    /// See: http://tools.ietf.org/html/rfc5849#section-2.2
+    /// The received 'oauth_token' is parsed and verified to match the current token().
+    /// When access token is successfully obtained, set_token() is called, and config is
+    /// ready for use by oauth1_handler.
+    /// </summary>
+    /// <param name="redirected_uri">The URI where web browser/view was redirected after resource owner's
+    /// authorization.</param> <returns>Task that fetches the access token based on redirected URI.</returns>
+    _ASYNCRTIMP pplx::task<void> token_from_redirected_uri(const web::http::uri& redirected_uri);
+
+    /// <summary>
+    /// Creates a task with HTTP request to fetch an access token from the token endpoint.
+    /// The request exchanges a verifier code to an access token.
+    /// If successful, the resulting token is set as active via set_token().
+    /// See: http://tools.ietf.org/html/rfc5849#section-2.3
+    /// </summary>
+    /// <param name="verifier">Verifier received via redirect upon successful authorization.</param>
+    /// <returns>Task that fetches the access token based on the verifier.</returns>
+    pplx::task<void> token_from_verifier(utility::string_t verifier)
+    {
+        return _request_token(_generate_auth_state(details::oauth1_strings::verifier, std::move(verifier)), false);
+    }
+
+    /// <summary>
+    /// Creates a task with HTTP request to fetch an access token from the token endpoint.
+    /// If successful, the resulting token is set as active via set_token().
+    /// </summary>
+    /// <returns>Task that fetches the access token based on the verifier.</returns>
+    pplx::task<void> refresh_token(const utility::string_t& key)
+    {
+        return _request_token(_generate_auth_state(key, m_token.additional_parameters().at(key)), false);
+    }
+
+    /// <summary>
+    /// Get consumer key used in authorization and authentication.
+    /// </summary>
+    /// <returns>Consumer key string.</returns>
+    const utility::string_t& consumer_key() const { return m_consumer_key; }
+    /// <summary>
+    /// Set consumer key used in authorization and authentication.
+    /// </summary>
+    /// <param name="key">Consumer key string to set.</param>
+    void set_consumer_key(utility::string_t key) { m_consumer_key = std::move(key); }
+
+    /// <summary>
+    /// Get consumer secret used in authorization and authentication.
+    /// </summary>
+    /// <returns>Consumer secret string.</returns>
+    const utility::string_t& consumer_secret() const { return m_consumer_secret; }
+    /// <summary>
+    /// Set consumer secret used in authorization and authentication.
+    /// </summary>
+    /// <param name="secret">Consumer secret string to set.</param>
+    void set_consumer_secret(utility::string_t secret) { m_consumer_secret = std::move(secret); }
+
+    /// <summary>
+    /// Get temporary token endpoint URI string.
+    /// </summary>
+    /// <returns>Temporary token endpoint URI string.</returns>
+    const utility::string_t& temp_endpoint() const { return m_temp_endpoint; }
+    /// <summary>
+    /// Set temporary token endpoint URI string.
+    /// </summary>
+    /// <param name="temp_endpoint">Temporary token endpoint URI string to set.</param>
+    void set_temp_endpoint(utility::string_t temp_endpoint) { m_temp_endpoint = std::move(temp_endpoint); }
+
+    /// <summary>
+    /// Get authorization endpoint URI string.
+    /// </summary>
+    /// <returns>Authorization endpoint URI string.</returns>
+    const utility::string_t& auth_endpoint() const { return m_auth_endpoint; }
+    /// <summary>
+    /// Set authorization endpoint URI string.
+    /// </summary>
+    /// <param name="auth_endpoint">Authorization endpoint URI string to set.</param>
+    void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); }
+
+    /// <summary>
+    /// Get token endpoint URI string.
+    /// </summary>
+    /// <returns>Token endpoint URI string.</returns>
+    const utility::string_t& token_endpoint() const { return m_token_endpoint; }
+    /// <summary>
+    /// Set token endpoint URI string.
+    /// </summary>
+    /// <param name="token_endpoint">Token endpoint URI string to set.</param>
+    void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); }
+
+    /// <summary>
+    /// Get callback URI string.
+    /// </summary>
+    /// <returns>Callback URI string.</returns>
+    const utility::string_t& callback_uri() const { return m_callback_uri; }
+    /// <summary>
+    /// Set callback URI string.
+    /// </summary>
+    /// <param name="callback_uri">Callback URI string to set.</param>
+    void set_callback_uri(utility::string_t callback_uri) { m_callback_uri = std::move(callback_uri); }
+
+    /// <summary>
+    /// Get token.
+    /// </summary>
+    /// <returns>Token.</returns>
+    _ASYNCRTIMP const oauth1_token& token() const;
+
+    /// <summary>
+    /// Set token.
+    /// </summary>
+    /// <param name="token">Token to set.</param>
+    void set_token(oauth1_token token)
+    {
+        m_token = std::move(token);
+        m_is_authorization_completed = true;
+    }
+
+    /// <summary>
+    /// Get signature method.
+    /// </summary>
+    /// <returns>Signature method.</returns>
+    const oauth1_method& method() const { return m_method; }
+    /// <summary>
+    /// Set signature method.
+    /// </summary>
+    /// <param name="method">Signature method.</param>
+    void set_method(oauth1_method method) { m_method = std::move(method); }
+
+    /// <summary>
+    /// Get authentication realm.
+    /// </summary>
+    /// <returns>Authentication realm string.</returns>
+    const utility::string_t& realm() const { return m_realm; }
+    /// <summary>
+    /// Set authentication realm.
+    /// </summary>
+    /// <param name="realm">Authentication realm string to set.</param>
+    void set_realm(utility::string_t realm) { m_realm = std::move(realm); }
+
+    /// <summary>
+    /// Returns enabled state of the configuration.
+    /// The oauth1_handler will perform OAuth 1.0 authentication only if
+    /// this method returns true.
+    /// Return value is true if access token is valid (=fetched or manually set)
+    /// and both consumer_key() and consumer_secret() are set (=non-empty).
+    /// </summary>
+    /// <returns>The configuration enabled state.</returns>
+    bool is_enabled() const
+    {
+        return token().is_valid_access_token() && !(consumer_key().empty() || consumer_secret().empty());
+    }
+
+    // Builds signature base string according to:
+    // http://tools.ietf.org/html/rfc5849#section-3.4.1.1
+    _ASYNCRTIMP utility::string_t _build_signature_base_string(http_request request, details::oauth1_state state) const;
+
+    // Builds HMAC-SHA1 signature according to:
+    // http://tools.ietf.org/html/rfc5849#section-3.4.2
+    utility::string_t _build_hmac_sha1_signature(http_request request, details::oauth1_state state) const
+    {
+        auto text(_build_signature_base_string(std::move(request), std::move(state)));
+        auto digest(_hmac_sha1(_build_key(), std::move(text)));
+        auto signature(utility::conversions::to_base64(std::move(digest)));
+        return signature;
+    }
+
+    // Builds PLAINTEXT signature according to:
+    // http://tools.ietf.org/html/rfc5849#section-3.4.4
+    utility::string_t _build_plaintext_signature() const { return _build_key(); }
+
+    details::oauth1_state _generate_auth_state(utility::string_t extra_key, utility::string_t extra_value)
+    {
+        return details::oauth1_state(
+            _generate_timestamp(), _generate_nonce(), std::move(extra_key), std::move(extra_value));
+    }
+
+    details::oauth1_state _generate_auth_state()
+    {
+        return details::oauth1_state(_generate_timestamp(), _generate_nonce());
+    }
+
+    /// <summary>
+    /// Gets map of parameters to sign.
+    /// </summary>
+    /// <returns>Map of parameters.</returns>
+    const std::map<utility::string_t, utility::string_t>& parameters() const { return m_parameters_to_sign; }
+
+    /// <summary>
+    /// Adds a key value parameter.
+    /// </summary>
+    /// <param name="key">Key as a string value.</param>
+    /// <param name="value">Value as a string value.</param>
+    void add_parameter(const utility::string_t& key, const utility::string_t& value)
+    {
+        m_parameters_to_sign[key] = value;
+    }
+
+    /// <summary>
+    /// Adds a key value parameter.
+    /// </summary>
+    /// <param name="key">Key as a string value.</param>
+    /// <param name="value">Value as a string value.</param>
+    void add_parameter(utility::string_t&& key, utility::string_t&& value)
+    {
+        m_parameters_to_sign[std::move(key)] = std::move(value);
+    }
+
+    /// <summary>
+    /// Sets entire map or parameters replacing all previously values.
+    /// </summary>
+    /// <param name="parameters">Map of values.</param>
+    void set_parameters(const std::map<utility::string_t, utility::string_t>& parameters)
+    {
+        m_parameters_to_sign.clear();
+        m_parameters_to_sign = parameters;
+    }
+
+    /// <summary>
+    /// Clears all parameters.
+    /// </summary>
+    void clear_parameters() { m_parameters_to_sign.clear(); }
+
+    /// <summary>
+    /// Get the web proxy object
+    /// </summary>
+    /// <returns>A reference to the web proxy object.</returns>
+    const web_proxy& proxy() const { return m_proxy; }
+
+    /// <summary>
+    /// Set the web proxy object that will be used by token_from_code and token_from_refresh
+    /// </summary>
+    /// <param name="proxy">A reference to the web proxy object.</param>
+    void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
+
+private:
+    friend class web::http::client::http_client_config;
+    friend class web::http::oauth1::details::oauth1_handler;
+
+    oauth1_config() : m_is_authorization_completed(false) {}
+
+    utility::string_t _generate_nonce() { return m_nonce_generator.generate(); }
+
+    static utility::string_t _generate_timestamp()
+    {
+        return utility::conversions::details::to_string_t(utility::datetime::utc_timestamp());
+    }
+
+    _ASYNCRTIMP static std::vector<unsigned char> __cdecl _hmac_sha1(const utility::string_t& key,
+                                                                     const utility::string_t& data);
+
+    static utility::string_t _build_base_string_uri(const uri& u);
+
+    utility::string_t _build_normalized_parameters(web::http::uri u, const details::oauth1_state& state) const;
+
+    utility::string_t _build_signature(http_request request, details::oauth1_state state) const;
+
+    utility::string_t _build_key() const
+    {
+        return uri::encode_data_string(consumer_secret()) + _XPLATSTR("&") + uri::encode_data_string(m_token.secret());
+    }
+
+    void _authenticate_request(http_request& req) { _authenticate_request(req, _generate_auth_state()); }
+
+    _ASYNCRTIMP void _authenticate_request(http_request& req, details::oauth1_state state);
+
+    _ASYNCRTIMP pplx::task<void> _request_token(details::oauth1_state state, bool is_temp_token_request);
+
+    utility::string_t m_consumer_key;
+    utility::string_t m_consumer_secret;
+    oauth1_token m_token;
+
+    utility::string_t m_temp_endpoint;
+    utility::string_t m_auth_endpoint;
+    utility::string_t m_token_endpoint;
+    utility::string_t m_callback_uri;
+    utility::string_t m_realm;
+    oauth1_method m_method;
+
+    std::map<utility::string_t, utility::string_t> m_parameters_to_sign;
+
+    web::web_proxy m_proxy;
+
+    utility::nonce_generator m_nonce_generator;
+    bool m_is_authorization_completed;
+};
+
+} // namespace experimental
+
+namespace details
+{
+class oauth1_handler : public http_pipeline_stage
+{
+public:
+    oauth1_handler(std::shared_ptr<experimental::oauth1_config> cfg) : m_config(std::move(cfg)) {}
+
+    virtual pplx::task<http_response> propagate(http_request request) override
+    {
+        if (m_config)
+        {
+            m_config->_authenticate_request(request);
+        }
+        return next_stage()->propagate(request);
+    }
+
+private:
+    std::shared_ptr<experimental::oauth1_config> m_config;
+};
+
+} // namespace details
+} // namespace oauth1
+} // namespace http
+} // namespace web
+
+#endif
diff --git a/Release/include/cpprest/oauth2.h b/Release/include/cpprest/oauth2.h
new file mode 100644 (file)
index 0000000..693ebbe
--- /dev/null
@@ -0,0 +1,540 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Oauth 2.0
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_OAUTH2_H
+#define CASA_OAUTH2_H
+
+#include "cpprest/details/web_utilities.h"
+#include "cpprest/http_msg.h"
+
+namespace web
+{
+namespace http
+{
+namespace client
+{
+// Forward declaration to avoid circular include dependency.
+class http_client_config;
+} // namespace client
+
+/// oAuth 2.0 library.
+namespace oauth2
+{
+namespace details
+{
+class oauth2_handler;
+
+// Constant strings for OAuth 2.0.
+typedef utility::string_t oauth2_string;
+class oauth2_strings
+{
+public:
+#define _OAUTH2_STRINGS
+#define DAT(a_, b_) _ASYNCRTIMP static const oauth2_string a_;
+#include "cpprest/details/http_constants.dat"
+#undef _OAUTH2_STRINGS
+#undef DAT
+};
+
+} // namespace details
+
+/// oAuth functionality is currently in beta.
+namespace experimental
+{
+/// <summary>
+/// Exception type for OAuth 2.0 errors.
+/// </summary>
+class oauth2_exception : public std::exception
+{
+public:
+    oauth2_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {}
+    ~oauth2_exception() CPPREST_NOEXCEPT {}
+    const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
+
+private:
+    std::string m_msg;
+};
+
+/// <summary>
+/// OAuth 2.0 token and associated information.
+/// </summary>
+class oauth2_token
+{
+public:
+    /// <summary>
+    /// Value for undefined expiration time in expires_in().
+    /// </summary>
+    enum
+    {
+        undefined_expiration = -1
+    };
+
+    oauth2_token(utility::string_t access_token = utility::string_t())
+        : m_access_token(std::move(access_token)), m_expires_in(undefined_expiration)
+    {
+    }
+
+    /// <summary>
+    /// Get access token validity state.
+    /// If true, access token is a valid.
+    /// </summary>
+    /// <returns>Access token validity state.</returns>
+    bool is_valid_access_token() const { return !access_token().empty(); }
+
+    /// <summary>
+    /// Get access token.
+    /// </summary>
+    /// <returns>Access token string.</returns>
+    const utility::string_t& access_token() const { return m_access_token; }
+    /// <summary>
+    /// Set access token.
+    /// </summary>
+    /// <param name="access_token">Access token string to set.</param>
+    void set_access_token(utility::string_t access_token) { m_access_token = std::move(access_token); }
+
+    /// <summary>
+    /// Get refresh token.
+    /// </summary>
+    /// <returns>Refresh token string.</returns>
+    const utility::string_t& refresh_token() const { return m_refresh_token; }
+    /// <summary>
+    /// Set refresh token.
+    /// </summary>
+    /// <param name="refresh_token">Refresh token string to set.</param>
+    void set_refresh_token(utility::string_t refresh_token) { m_refresh_token = std::move(refresh_token); }
+
+    /// <summary>
+    /// Get token type.
+    /// </summary>
+    /// <returns>Token type string.</returns>
+    const utility::string_t& token_type() const { return m_token_type; }
+    /// <summary>
+    /// Set token type.
+    /// </summary>
+    /// <param name="token_type">Token type string to set.</param>
+    void set_token_type(utility::string_t token_type) { m_token_type = std::move(token_type); }
+
+    /// <summary>
+    /// Get token scope.
+    /// </summary>
+    /// <returns>Token scope string.</returns>
+    const utility::string_t& scope() const { return m_scope; }
+    /// <summary>
+    /// Set token scope.
+    /// </summary>
+    /// <param name="scope">Token scope string to set.</param>
+    void set_scope(utility::string_t scope) { m_scope = std::move(scope); }
+
+    /// <summary>
+    /// Get the lifetime of the access token in seconds.
+    /// For example, 3600 means the access token will expire in one hour from
+    /// the time when access token response was generated by the authorization server.
+    /// Value of undefined_expiration means expiration time is either
+    /// unset or that it was not returned by the server with the access token.
+    /// </summary>
+    /// <returns>Lifetime of the access token in seconds or undefined_expiration if not set.</returns>
+    int64_t expires_in() const { return m_expires_in; }
+    /// <summary>
+    /// Set lifetime of access token (in seconds).
+    /// </summary>
+    /// <param name="expires_in">Lifetime of access token in seconds.</param>
+    void set_expires_in(int64_t expires_in) { m_expires_in = expires_in; }
+
+private:
+    utility::string_t m_access_token;
+    utility::string_t m_refresh_token;
+    utility::string_t m_token_type;
+    utility::string_t m_scope;
+    int64_t m_expires_in;
+};
+
+/// <summary>
+/// OAuth 2.0 configuration.
+///
+/// Encapsulates functionality for:
+/// -  Authenticating requests with an access token.
+/// -  Performing the OAuth 2.0 authorization code grant authorization flow.
+///    See: http://tools.ietf.org/html/rfc6749#section-4.1
+/// -  Performing the OAuth 2.0 implicit grant authorization flow.
+///    See: http://tools.ietf.org/html/rfc6749#section-4.2
+///
+/// Performing OAuth 2.0 authorization:
+/// 1. Set service and client/app parameters:
+/// -  Client/app key & secret (as provided by the service).
+/// -  The service authorization endpoint and token endpoint.
+/// -  Your client/app redirect URI.
+/// -  Use set_state() to assign a unique state string for the authorization
+///    session (default: "").
+/// -  If needed, use set_bearer_auth() to control bearer token passing in either
+///    query or header (default: header). See: http://tools.ietf.org/html/rfc6750#section-2
+/// -  If needed, use set_access_token_key() to set "non-standard" access token
+///    key (default: "access_token").
+/// -  If needed, use set_implicit_grant() to enable implicit grant flow.
+/// 2. Build authorization URI with build_authorization_uri() and open this in web browser/control.
+/// 3. The resource owner should then clicks "Yes" to authorize your client/app, and
+///    as a result the web browser/control is redirected to redirect_uri().
+/// 5. Capture the redirected URI either in web control or by HTTP listener.
+/// 6. Pass the redirected URI to token_from_redirected_uri() to obtain access token.
+/// -  The method ensures redirected URI contains same state() as set in step 1.
+/// -  In implicit_grant() is false, this will create HTTP request to fetch access token
+///    from the service. Otherwise access token is already included in the redirected URI.
+///
+/// Usage for issuing authenticated requests:
+/// 1. Perform authorization as above to obtain the access token or use an existing token.
+/// -  Some services provide option to generate access tokens for testing purposes.
+/// 2. Pass the resulting oauth2_config with the access token to http_client_config::set_oauth2().
+/// 3. Construct http_client with this http_client_config. As a result, all HTTP requests
+///    by that client will be OAuth 2.0 authenticated.
+///
+/// </summary>
+class oauth2_config
+{
+public:
+    oauth2_config(utility::string_t client_key,
+                  utility::string_t client_secret,
+                  utility::string_t auth_endpoint,
+                  utility::string_t token_endpoint,
+                  utility::string_t redirect_uri,
+                  utility::string_t scope = utility::string_t(),
+                  utility::string_t user_agent = utility::string_t())
+        : m_client_key(std::move(client_key))
+        , m_client_secret(std::move(client_secret))
+        , m_auth_endpoint(std::move(auth_endpoint))
+        , m_token_endpoint(std::move(token_endpoint))
+        , m_redirect_uri(std::move(redirect_uri))
+        , m_scope(std::move(scope))
+        , m_user_agent(std::move(user_agent))
+        , m_implicit_grant(false)
+        , m_bearer_auth(true)
+        , m_http_basic_auth(true)
+        , m_access_token_key(details::oauth2_strings::access_token)
+    {
+    }
+
+    /// <summary>
+    /// Builds an authorization URI to be loaded in the web browser/view.
+    /// The URI is built with auth_endpoint() as basis.
+    /// The implicit_grant() affects the built URI by selecting
+    /// either authorization code or implicit grant flow.
+    /// You can set generate_state to generate a new random state string.
+    /// </summary>
+    /// <param name="generate_state">If true, a new random state() string is generated
+    /// which replaces the current state(). If false, state() is unchanged and used as-is.</param>
+    /// <returns>Authorization URI string.</returns>
+    _ASYNCRTIMP utility::string_t build_authorization_uri(bool generate_state);
+
+    /// <summary>
+    /// Fetch an access token (and possibly a refresh token) based on redirected URI.
+    /// Behavior depends on the implicit_grant() setting.
+    /// If implicit_grant() is false, the URI is parsed for 'code'
+    /// parameter, and then token_from_code() is called with this code.
+    /// See: http://tools.ietf.org/html/rfc6749#section-4.1
+    /// Otherwise, redirect URI fragment part is parsed for 'access_token'
+    /// parameter, which directly contains the token(s).
+    /// See: http://tools.ietf.org/html/rfc6749#section-4.2
+    /// In both cases, the 'state' parameter is parsed and is verified to match state().
+    /// </summary>
+    /// <param name="redirected_uri">The URI where web browser/view was redirected after resource owner's
+    /// authorization.</param> <returns>Task that fetches the token(s) based on redirected URI.</returns>
+    _ASYNCRTIMP pplx::task<void> token_from_redirected_uri(const web::http::uri& redirected_uri);
+
+    /// <summary>
+    /// Fetches an access token (and possibly a refresh token) from the token endpoint.
+    /// The task creates an HTTP request to the token_endpoint() which exchanges
+    /// the authorization code for the token(s).
+    /// This also sets the refresh token if one was returned.
+    /// See: http://tools.ietf.org/html/rfc6749#section-4.1.3
+    /// </summary>
+    /// <param name="authorization_code">Code received via redirect upon successful authorization.</param>
+    /// <returns>Task that fetches token(s) based on the authorization code.</returns>
+    pplx::task<void> token_from_code(utility::string_t authorization_code)
+    {
+        uri_builder ub;
+        ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::authorization_code, false);
+        ub.append_query(details::oauth2_strings::code, uri::encode_data_string(std::move(authorization_code)), false);
+        ub.append_query(details::oauth2_strings::redirect_uri, uri::encode_data_string(redirect_uri()), false);
+        return _request_token(ub);
+    }
+
+    /// <summary>
+    /// Fetches a new access token (and possibly a new refresh token) using the refresh token.
+    /// The task creates a HTTP request to the token_endpoint().
+    /// If successful, resulting access token is set as active via set_token().
+    /// See: http://tools.ietf.org/html/rfc6749#section-6
+    /// This also sets a new refresh token if one was returned.
+    /// </summary>
+    /// <returns>Task that fetches the token(s) using the refresh token.</returns>
+    pplx::task<void> token_from_refresh()
+    {
+        uri_builder ub;
+        ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::refresh_token, false);
+        ub.append_query(
+            details::oauth2_strings::refresh_token, uri::encode_data_string(token().refresh_token()), false);
+        return _request_token(ub);
+    }
+
+    /// <summary>
+    /// Returns enabled state of the configuration.
+    /// The oauth2_handler will perform OAuth 2.0 authentication only if
+    /// this method returns true.
+    /// Return value is true if access token is valid (=fetched or manually set).
+    /// </summary>
+    /// <returns>The configuration enabled state.</returns>
+    bool is_enabled() const { return token().is_valid_access_token(); }
+
+    /// <summary>
+    /// Get client key.
+    /// </summary>
+    /// <returns>Client key string.</returns>
+    const utility::string_t& client_key() const { return m_client_key; }
+    /// <summary>
+    /// Set client key.
+    /// </summary>
+    /// <param name="client_key">Client key string to set.</param>
+    void set_client_key(utility::string_t client_key) { m_client_key = std::move(client_key); }
+
+    /// <summary>
+    /// Get client secret.
+    /// </summary>
+    /// <returns>Client secret string.</returns>
+    const utility::string_t& client_secret() const { return m_client_secret; }
+    /// <summary>
+    /// Set client secret.
+    /// </summary>
+    /// <param name="client_secret">Client secret string to set.</param>
+    void set_client_secret(utility::string_t client_secret) { m_client_secret = std::move(client_secret); }
+
+    /// <summary>
+    /// Get authorization endpoint URI string.
+    /// </summary>
+    /// <returns>Authorization endpoint URI string.</returns>
+    const utility::string_t& auth_endpoint() const { return m_auth_endpoint; }
+    /// <summary>
+    /// Set authorization endpoint URI string.
+    /// </summary>
+    /// <param name="auth_endpoint">Authorization endpoint URI string to set.</param>
+    void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); }
+
+    /// <summary>
+    /// Get token endpoint URI string.
+    /// </summary>
+    /// <returns>Token endpoint URI string.</returns>
+    const utility::string_t& token_endpoint() const { return m_token_endpoint; }
+    /// <summary>
+    /// Set token endpoint URI string.
+    /// </summary>
+    /// <param name="token_endpoint">Token endpoint URI string to set.</param>
+    void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); }
+
+    /// <summary>
+    /// Get redirect URI string.
+    /// </summary>
+    /// <returns>Redirect URI string.</returns>
+    const utility::string_t& redirect_uri() const { return m_redirect_uri; }
+    /// <summary>
+    /// Set redirect URI string.
+    /// </summary>
+    /// <param name="redirect_uri">Redirect URI string to set.</param>
+    void set_redirect_uri(utility::string_t redirect_uri) { m_redirect_uri = std::move(redirect_uri); }
+
+    /// <summary>
+    /// Get scope used in authorization for token.
+    /// </summary>
+    /// <returns>Scope string used in authorization.</returns>
+    const utility::string_t& scope() const { return m_scope; }
+    /// <summary>
+    /// Set scope for authorization for token.
+    /// </summary>
+    /// <param name="scope">Scope string for authorization for token.</param>
+    void set_scope(utility::string_t scope) { m_scope = std::move(scope); }
+
+    /// <summary>
+    /// Get client state string used in authorization.
+    /// </summary>
+    /// <returns>Client state string used in authorization.</returns>
+    const utility::string_t& state() { return m_state; }
+    /// <summary>
+    /// Set client state string for authorization for token.
+    /// The state string is used in authorization for security reasons
+    /// (to uniquely identify authorization sessions).
+    /// If desired, suitably secure state string can be automatically generated
+    /// by build_authorization_uri().
+    /// A good state string consist of 30 or more random alphanumeric characters.
+    /// </summary>
+    /// <param name="state">Client authorization state string to set.</param>
+    void set_state(utility::string_t state) { m_state = std::move(state); }
+
+    /// <summary>
+    /// Get token.
+    /// </summary>
+    /// <returns>Token.</returns>
+    const oauth2_token& token() const { return m_token; }
+    /// <summary>
+    /// Set token.
+    /// </summary>
+    /// <param name="token">Token to set.</param>
+    void set_token(oauth2_token token) { m_token = std::move(token); }
+
+    /// <summary>
+    /// Get implicit grant setting for authorization.
+    /// </summary>
+    /// <returns>Implicit grant setting for authorization.</returns>
+    bool implicit_grant() const { return m_implicit_grant; }
+    /// <summary>
+    /// Set implicit grant setting for authorization.
+    /// False means authorization code grant is used for authorization.
+    /// True means implicit grant is used.
+    /// Default: False.
+    /// </summary>
+    /// <param name="implicit_grant">The implicit grant setting to set.</param>
+    void set_implicit_grant(bool implicit_grant) { m_implicit_grant = implicit_grant; }
+
+    /// <summary>
+    /// Get bearer token authentication setting.
+    /// </summary>
+    /// <returns>Bearer token authentication setting.</returns>
+    bool bearer_auth() const { return m_bearer_auth; }
+    /// <summary>
+    /// Set bearer token authentication setting.
+    /// This must be selected based on what the service accepts.
+    /// True means access token is passed in the request header. (http://tools.ietf.org/html/rfc6750#section-2.1)
+    /// False means access token in passed in the query parameters. (http://tools.ietf.org/html/rfc6750#section-2.3)
+    /// Default: True.
+    /// </summary>
+    /// <param name="bearer_auth">The bearer token authentication setting to set.</param>
+    void set_bearer_auth(bool bearer_auth) { m_bearer_auth = bearer_auth; }
+
+    /// <summary>
+    /// Get HTTP Basic authentication setting for token endpoint.
+    /// </summary>
+    /// <returns>HTTP Basic authentication setting for token endpoint.</returns>
+    bool http_basic_auth() const { return m_http_basic_auth; }
+    /// <summary>
+    /// Set HTTP Basic authentication setting for token endpoint.
+    /// This setting must be selected based on what the service accepts.
+    /// True means HTTP Basic authentication is used for the token endpoint.
+    /// False means client key & secret are passed in the HTTP request body.
+    /// Default: True.
+    /// </summary>
+    /// <param name="http_basic_auth">The HTTP Basic authentication setting to set.</param>
+    void set_http_basic_auth(bool http_basic_auth) { m_http_basic_auth = http_basic_auth; }
+
+    /// <summary>
+    /// Get access token key.
+    /// </summary>
+    /// <returns>Access token key string.</returns>
+    const utility::string_t& access_token_key() const { return m_access_token_key; }
+    /// <summary>
+    /// Set access token key.
+    /// If the service requires a "non-standard" key you must set it here.
+    /// Default: "access_token".
+    /// </summary>
+    void set_access_token_key(utility::string_t access_token_key) { m_access_token_key = std::move(access_token_key); }
+
+    /// <summary>
+    /// Get the web proxy object
+    /// </summary>
+    /// <returns>A reference to the web proxy object.</returns>
+    const web_proxy& proxy() const { return m_proxy; }
+
+    /// <summary>
+    /// Set the web proxy object that will be used by token_from_code and token_from_refresh
+    /// </summary>
+    /// <param name="proxy">A reference to the web proxy object.</param>
+    void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
+
+    /// <summary>
+    /// Get user agent to be used in oauth2 flows.
+    /// </summary>
+    /// <returns>User agent string.</returns>
+    const utility::string_t& user_agent() const { return m_user_agent; }
+    /// <summary>
+    /// Set user agent to be used in oauth2 flows.
+    /// If none is provided a default user agent is provided.
+    /// </summary>
+    void set_user_agent(utility::string_t user_agent) { m_user_agent = std::move(user_agent); }
+
+private:
+    friend class web::http::client::http_client_config;
+    friend class web::http::oauth2::details::oauth2_handler;
+
+    oauth2_config() : m_implicit_grant(false), m_bearer_auth(true), m_http_basic_auth(true) {}
+
+    _ASYNCRTIMP pplx::task<void> _request_token(uri_builder& request_body);
+
+    oauth2_token _parse_token_from_json(const json::value& token_json);
+
+    void _authenticate_request(http_request& req) const
+    {
+        if (bearer_auth())
+        {
+            req.headers().add(header_names::authorization, _XPLATSTR("Bearer ") + token().access_token());
+        }
+        else
+        {
+            uri_builder ub(req.request_uri());
+            ub.append_query(access_token_key(), token().access_token());
+            req.set_request_uri(ub.to_uri());
+        }
+    }
+
+    utility::string_t m_client_key;
+    utility::string_t m_client_secret;
+    utility::string_t m_auth_endpoint;
+    utility::string_t m_token_endpoint;
+    utility::string_t m_redirect_uri;
+    utility::string_t m_scope;
+    utility::string_t m_state;
+    utility::string_t m_user_agent;
+
+    web::web_proxy m_proxy;
+
+    bool m_implicit_grant;
+    bool m_bearer_auth;
+    bool m_http_basic_auth;
+    utility::string_t m_access_token_key;
+
+    oauth2_token m_token;
+
+    utility::nonce_generator m_state_generator;
+};
+
+} // namespace experimental
+
+namespace details
+{
+class oauth2_handler : public http_pipeline_stage
+{
+public:
+    oauth2_handler(std::shared_ptr<experimental::oauth2_config> cfg) : m_config(std::move(cfg)) {}
+
+    virtual pplx::task<http_response> propagate(http_request request) override
+    {
+        if (m_config)
+        {
+            m_config->_authenticate_request(request);
+        }
+        return next_stage()->propagate(request);
+    }
+
+private:
+    std::shared_ptr<experimental::oauth2_config> m_config;
+};
+
+} // namespace details
+} // namespace oauth2
+} // namespace http
+} // namespace web
+
+#endif
diff --git a/Release/include/cpprest/producerconsumerstream.h b/Release/include/cpprest/producerconsumerstream.h
new file mode 100644 (file)
index 0000000..2846337
--- /dev/null
@@ -0,0 +1,656 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * This file defines a basic memory-based stream buffer, which allows consumer / producer pairs to communicate
+ * data via a buffer.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_PRODUCER_CONSUMER_STREAMS_H
+#define CASA_PRODUCER_CONSUMER_STREAMS_H
+
+#include "cpprest/astreambuf.h"
+#include "pplx/pplxtasks.h"
+#include <algorithm>
+#include <iterator>
+#include <queue>
+#include <vector>
+
+namespace Concurrency
+{
+namespace streams
+{
+namespace details
+{
+/// <summary>
+/// The basic_producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and
+/// reading sequences of characters. It can be used as a consumer/producer buffer.
+/// </summary>
+template<typename _CharType>
+class basic_producer_consumer_buffer : public streams::details::streambuf_state_manager<_CharType>
+{
+public:
+    typedef typename ::concurrency::streams::char_traits<_CharType> traits;
+    typedef typename basic_streambuf<_CharType>::int_type int_type;
+    typedef typename basic_streambuf<_CharType>::pos_type pos_type;
+    typedef typename basic_streambuf<_CharType>::off_type off_type;
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    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)
+        , m_total_read(0)
+        , m_total_written(0)
+        , m_synced(0)
+    {
+    }
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    virtual ~basic_producer_consumer_buffer()
+    {
+        // Note: there is no need to call 'wait()' on the result of close(),
+        // since we happen to know that close() will return without actually
+        // doing anything asynchronously. Should the implementation of _close_write()
+        // change in that regard, this logic may also have to change.
+        this->_close_read();
+        this->_close_write();
+
+        _ASSERTE(m_requests.empty());
+        m_blocks.clear();
+    }
+
+    /// <summary>
+    /// <c>can_seek<c/> is used to determine whether a stream buffer supports seeking.
+    /// </summary>
+    virtual bool can_seek() const { return false; }
+
+    /// <summary>
+    /// <c>has_size<c/> is used to determine whether a stream buffer supports size().
+    /// </summary>
+    virtual bool has_size() const { return false; }
+
+    /// <summary>
+    /// Get the stream buffer size, if one has been set.
+    /// </summary>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
+    virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
+
+    /// <summary>
+    /// Sets the stream buffer implementation to buffer or not buffer.
+    /// </summary>
+    /// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
+    /// will not have any effect on what is returned by subsequent calls to <see cref="::buffer_size method"
+    /// />.</remarks>
+    virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
+
+    /// <summary>
+    /// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
+    /// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
+    /// incurring the overhead of using tasks.
+    /// </summary>
+    virtual size_t in_avail() const { return m_total; }
+
+    /// <summary>
+    /// Gets the current read or write position in the stream.
+    /// </summary>
+    /// <param name="direction">The I/O direction to seek (see remarks)</param>
+    /// <returns>The current position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the direction parameter defines whether to move the read or the write
+    ///          cursor.</remarks>
+    virtual pos_type getpos(std::ios_base::openmode mode) const
+    {
+        if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
+            return static_cast<pos_type>(traits::eof());
+
+        if (mode == std::ios_base::in)
+            return (pos_type)m_total_read;
+        else if (mode == std::ios_base::out)
+            return (pos_type)m_total_written;
+        else
+            return (pos_type)traits::eof();
+    }
+
+    // Seeking is not supported
+    virtual pos_type seekpos(pos_type, std::ios_base::openmode) { return (pos_type)traits::eof(); }
+    virtual pos_type seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode)
+    {
+        return (pos_type)traits::eof();
+    }
+
+    /// <summary>
+    /// Allocates a contiguous memory block and returns it.
+    /// </summary>
+    /// <param name="count">The number of characters to allocate.</param>
+    /// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
+    /// alloc/commit.</returns>
+    virtual _CharType* _alloc(size_t count)
+    {
+        if (!this->can_write())
+        {
+            return nullptr;
+        }
+
+        // We always allocate a new block even if the count could be satisfied by
+        // the current write block. While this does lead to wasted space it allows for
+        // easier book keeping
+
+        _ASSERTE(!m_allocBlock);
+        m_allocBlock = std::make_shared<_block>(count);
+        return m_allocBlock->wbegin();
+    }
+
+    /// <summary>
+    /// Submits a block already allocated by the stream buffer.
+    /// </summary>
+    /// <param name="count">The number of characters to be committed.</param>
+    virtual void _commit(size_t count)
+    {
+        pplx::extensibility::scoped_critical_section_t l(m_lock);
+
+        // The count does not reflect the actual size of the block.
+        // Since we do not allow any more writes to this block it would suffice.
+        // If we ever change the algorithm to reuse blocks then this needs to be revisited.
+
+        _ASSERTE((bool)m_allocBlock);
+        m_allocBlock->update_write_head(count);
+        m_blocks.push_back(m_allocBlock);
+        m_allocBlock = nullptr;
+
+        update_write_head(count);
+    }
+
+    /// <summary>
+    /// Gets a pointer to the next already allocated contiguous block of data.
+    /// </summary>
+    /// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
+    /// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
+    /// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
+    /// <remarks>
+    /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
+    /// there is no block to return immediately or that the stream buffer does not support the operation.
+    /// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
+    /// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
+    /// a subsequent read will not succeed.
+    /// </remarks>
+    virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
+    {
+        count = 0;
+        ptr = nullptr;
+
+        if (!this->can_read()) return false;
+
+        pplx::extensibility::scoped_critical_section_t l(m_lock);
+
+        if (m_blocks.empty())
+        {
+            // If the write head has been closed then have reached the end of the
+            // stream (return true), otherwise more data could be written later (return false).
+            return !this->can_write();
+        }
+        else
+        {
+            auto block = m_blocks.front();
+
+            count = block->rd_chars_left();
+            ptr = block->rbegin();
+
+            _ASSERTE(ptr != nullptr);
+            return true;
+        }
+    }
+
+    /// <summary>
+    /// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
+    /// de-allocate the memory, if it so desires. Move the read position ahead by the count.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be released.</param>
+    /// <param name="count">The number of characters that were read.</param>
+    virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
+    {
+        if (ptr == nullptr) return;
+
+        pplx::extensibility::scoped_critical_section_t l(m_lock);
+        auto block = m_blocks.front();
+
+        _ASSERTE(block->rd_chars_left() >= count);
+        block->m_read += count;
+
+        update_read_head(count);
+    }
+
+protected:
+    virtual pplx::task<bool> _sync()
+    {
+        pplx::extensibility::scoped_critical_section_t l(m_lock);
+
+        m_synced = in_avail();
+
+        fulfill_outstanding();
+
+        return pplx::task_from_result(true);
+    }
+
+    virtual pplx::task<int_type> _putc(_CharType ch)
+    {
+        return pplx::task_from_result((this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof());
+    }
+
+    virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
+    {
+        return pplx::task_from_result<size_t>(this->write(ptr, count));
+    }
+
+    virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        pplx::task_completion_event<size_t> tce;
+        enqueue_request(_request(count, [this, ptr, count, tce]() {
+            // VS 2010 resolves read to a global function.  Explicit
+            // invocation through the "this" pointer fixes the issue.
+            tce.set(this->read(ptr, count));
+        }));
+        return pplx::create_task(tce);
+    }
+
+    virtual size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        pplx::extensibility::scoped_critical_section_t l(m_lock);
+        return can_satisfy(count) ? this->read(ptr, count) : (size_t)traits::requires_async();
+    }
+
+    virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        pplx::extensibility::scoped_critical_section_t l(m_lock);
+        return can_satisfy(count) ? this->read(ptr, count, false) : (size_t)traits::requires_async();
+    }
+
+    virtual pplx::task<int_type> _bumpc()
+    {
+        pplx::task_completion_event<int_type> tce;
+        enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(true)); }));
+        return pplx::create_task(tce);
+    }
+
+    virtual int_type _sbumpc()
+    {
+        pplx::extensibility::scoped_critical_section_t l(m_lock);
+        return can_satisfy(1) ? this->read_byte(true) : traits::requires_async();
+    }
+
+    virtual pplx::task<int_type> _getc()
+    {
+        pplx::task_completion_event<int_type> tce;
+        enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(false)); }));
+        return pplx::create_task(tce);
+    }
+
+    int_type _sgetc()
+    {
+        pplx::extensibility::scoped_critical_section_t l(m_lock);
+        return can_satisfy(1) ? this->read_byte(false) : traits::requires_async();
+    }
+
+    virtual pplx::task<int_type> _nextc()
+    {
+        pplx::task_completion_event<int_type> tce;
+        enqueue_request(_request(1, [this, tce]() {
+            this->read_byte(true);
+            tce.set(this->read_byte(false));
+        }));
+        return pplx::create_task(tce);
+    }
+
+    virtual pplx::task<int_type> _ungetc() { return pplx::task_from_result<int_type>(traits::eof()); }
+
+private:
+    /// <summary>
+    /// Close the stream buffer for writing
+    /// </summary>
+    pplx::task<void> _close_write()
+    {
+        // First indicate that there could be no more writes.
+        // Fulfill outstanding relies on that to flush all the
+        // read requests.
+        this->m_stream_can_write = false;
+
+        {
+            pplx::extensibility::scoped_critical_section_t l(this->m_lock);
+
+            // This runs on the thread that called close.
+            this->fulfill_outstanding();
+        }
+
+        return pplx::task_from_result();
+    }
+
+    /// <summary>
+    /// Updates the write head by an offset specified by count
+    /// </summary>
+    /// <remarks>This should be called with the lock held</remarks>
+    void update_write_head(size_t count)
+    {
+        m_total += count;
+        m_total_written += count;
+        fulfill_outstanding();
+    }
+
+    /// <summary>
+    /// Writes count characters from ptr into the stream buffer
+    /// </summary>
+    size_t write(const _CharType* ptr, size_t count)
+    {
+        if (!this->can_write() || (count == 0)) return 0;
+
+        // If no one is going to read, why bother?
+        // Just pretend to be writing!
+        if (!this->can_read()) return count;
+
+        pplx::extensibility::scoped_critical_section_t l(m_lock);
+
+        // Allocate a new block if necessary
+        if (m_blocks.empty() || m_blocks.back()->wr_chars_left() < count)
+        {
+            msl::safeint3::SafeInt<size_t> alloc = m_alloc_size.Max(count);
+            m_blocks.push_back(std::make_shared<_block>(alloc));
+        }
+
+        // The block at the back is always the write head
+        auto last = m_blocks.back();
+        auto countWritten = last->write(ptr, count);
+        _ASSERTE(countWritten == count);
+
+        update_write_head(countWritten);
+        return countWritten;
+    }
+
+    /// <summary>
+    /// Fulfill pending requests
+    /// </summary>
+    /// <remarks>This should be called with the lock held</remarks>
+    void fulfill_outstanding()
+    {
+        while (!m_requests.empty())
+        {
+            auto req = m_requests.front();
+
+            // If we cannot satisfy the request then we need
+            // to wait for the producer to write data
+            if (!can_satisfy(req.size())) return;
+
+            // We have enough data to satisfy this request
+            req.complete();
+
+            // Remove it from the request queue
+            m_requests.pop();
+        }
+    }
+
+    /// <summary>
+    /// Represents a memory block
+    /// </summary>
+    class _block
+    {
+    public:
+        _block(size_t size) : m_read(0), m_pos(0), m_size(size), m_data(new _CharType[size]) {}
+
+        ~_block() { delete[] m_data; }
+
+        // Read head
+        size_t m_read;
+
+        // Write head
+        size_t m_pos;
+
+        // Allocation size (of m_data)
+        size_t m_size;
+
+        // The data store
+        _CharType* m_data;
+
+        // Pointer to the read head
+        _CharType* rbegin() { return m_data + m_read; }
+
+        // Pointer to the write head
+        _CharType* wbegin() { return m_data + m_pos; }
+
+        // Read up to count characters from the block
+        size_t read(_Out_writes_(count) _CharType* dest, _In_ size_t count, bool advance = true)
+        {
+            msl::safeint3::SafeInt<size_t> avail(rd_chars_left());
+            auto countRead = static_cast<size_t>(avail.Min(count));
+
+            _CharType* beg = rbegin();
+            _CharType* end = rbegin() + countRead;
+
+#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
+            // Avoid warning C4996: Use checked iterators under SECURE_SCL
+            std::copy(beg, end, stdext::checked_array_iterator<_CharType*>(dest, count));
+#else
+            std::copy(beg, end, dest);
+#endif // _WIN32
+
+            if (advance)
+            {
+                m_read += countRead;
+            }
+
+            return countRead;
+        }
+
+        // Write count characters into the block
+        size_t write(const _CharType* src, size_t count)
+        {
+            msl::safeint3::SafeInt<size_t> avail(wr_chars_left());
+            auto countWritten = static_cast<size_t>(avail.Min(count));
+
+            const _CharType* srcEnd = src + countWritten;
+
+#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
+            // Avoid warning C4996: Use checked iterators under SECURE_SCL
+            std::copy(src, srcEnd, stdext::checked_array_iterator<_CharType*>(wbegin(), static_cast<size_t>(avail)));
+#else
+            std::copy(src, srcEnd, wbegin());
+#endif // _WIN32
+
+            update_write_head(countWritten);
+            return countWritten;
+        }
+
+        void update_write_head(size_t count) { m_pos += count; }
+
+        size_t rd_chars_left() const { return m_pos - m_read; }
+        size_t wr_chars_left() const { return m_size - m_pos; }
+
+    private:
+        // Copy is not supported
+        _block(const _block&);
+        _block& operator=(const _block&);
+    };
+
+    /// <summary>
+    /// Represents a request on the stream buffer - typically reads
+    /// </summary>
+    class _request
+    {
+    public:
+        typedef std::function<void()> func_type;
+        _request(size_t count, const func_type& func) : m_func(func), m_count(count) {}
+
+        void complete() { m_func(); }
+
+        size_t size() const { return m_count; }
+
+    private:
+        func_type m_func;
+        size_t m_count;
+    };
+
+    void enqueue_request(_request req)
+    {
+        pplx::extensibility::scoped_critical_section_t l(m_lock);
+
+        if (can_satisfy(req.size()))
+        {
+            // We can immediately fulfill the request.
+            req.complete();
+        }
+        else
+        {
+            // We must wait for data to arrive.
+            m_requests.push(req);
+        }
+    }
+
+    /// <summary>
+    /// Determine if the request can be satisfied.
+    /// </summary>
+    bool can_satisfy(size_t count) { return (m_synced > 0) || (this->in_avail() >= count) || !this->can_write(); }
+
+    /// <summary>
+    /// Reads a byte from the stream and returns it as int_type.
+    /// Note: This routine shall only be called if can_satisfy() returned true.
+    /// </summary>
+    /// <remarks>This should be called with the lock held</remarks>
+    int_type read_byte(bool advance = true)
+    {
+        _CharType value;
+        auto read_size = this->read(&value, 1, advance);
+        return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
+    }
+
+    /// <summary>
+    /// Reads up to count characters into ptr and returns the count of characters copied.
+    /// The return value (actual characters copied) could be <= count.
+    /// Note: This routine shall only be called if can_satisfy() returned true.
+    /// </summary>
+    /// <remarks>This should be called with the lock held</remarks>
+    size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
+    {
+        _ASSERTE(can_satisfy(count));
+
+        size_t read = 0;
+
+        for (auto iter = begin(m_blocks); iter != std::end(m_blocks); ++iter)
+        {
+            auto block = *iter;
+            auto read_from_block = block->read(ptr + read, count - read, advance);
+
+            read += read_from_block;
+
+            _ASSERTE(count >= read);
+            if (read == count) break;
+        }
+
+        if (advance)
+        {
+            update_read_head(read);
+        }
+
+        return read;
+    }
+
+    /// <summary>
+    /// Updates the read head by the specified offset
+    /// </summary>
+    /// <remarks>This should be called with the lock held</remarks>
+    void update_read_head(size_t count)
+    {
+        m_total -= count;
+        m_total_read += count;
+
+        if (m_synced > 0) m_synced = (m_synced > count) ? (m_synced - count) : 0;
+
+        // The block at the front is always the read head.
+        // Purge empty blocks so that the block at the front reflects the read head
+        while (!m_blocks.empty())
+        {
+            // 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
+            m_blocks.pop_front();
+        }
+    }
+
+    // The in/out mode for the buffer
+    std::ios_base::openmode m_mode;
+
+    // Default block size
+    msl::safeint3::SafeInt<size_t> m_alloc_size;
+
+    // Block used for alloc/commit
+    std::shared_ptr<_block> m_allocBlock;
+
+    // Total available data
+    size_t m_total;
+
+    size_t m_total_read;
+    size_t m_total_written;
+
+    // Keeps track of the number of chars that have been flushed but still
+    // remain to be consumed by a read operation.
+    size_t m_synced;
+
+    // The producer-consumer buffer is intended to be used concurrently by a reader
+    // and a writer, who are not coordinating their accesses to the buffer (coordination
+    // being what the buffer is for in the first place). Thus, we have to protect
+    // against some of the internal data elements against concurrent accesses
+    // and the possibility of inconsistent states. A simple non-recursive lock
+    // should be sufficient for those purposes.
+    pplx::extensibility::critical_section_t m_lock;
+
+    // Memory blocks
+    std::deque<std::shared_ptr<_block>> m_blocks;
+
+    // Queue of requests
+    std::queue<_request> m_requests;
+};
+
+} // namespace details
+
+/// <summary>
+/// The producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and reading
+/// sequences of bytes. It can be used as a consumer/producer buffer.
+/// </summary>
+/// <typeparam name="_CharType">
+/// The data type of the basic element of the <c>producer_consumer_buffer</c>.
+/// </typeparam>
+/// <remarks>
+/// This is a reference-counted version of basic_producer_consumer_buffer.</remarks>
+template<typename _CharType>
+class producer_consumer_buffer : public streambuf<_CharType>
+{
+public:
+    typedef _CharType char_type;
+
+    /// <summary>
+    /// Create a producer_consumer_buffer.
+    /// </summary>
+    /// <param name="alloc_size">The internal default block size.</param>
+    producer_consumer_buffer(size_t alloc_size = 512)
+        : streambuf<_CharType>(std::make_shared<details::basic_producer_consumer_buffer<_CharType>>(alloc_size))
+    {
+    }
+};
+
+} // namespace streams
+} // namespace Concurrency
+
+#endif
diff --git a/Release/include/cpprest/rawptrstream.h b/Release/include/cpprest/rawptrstream.h
new file mode 100644 (file)
index 0000000..1f15ecb
--- /dev/null
@@ -0,0 +1,598 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * This file defines a stream buffer that is based on a raw pointer and block size. Unlike a vector-based
+ * stream buffer, the buffer cannot be expanded or contracted, it has a fixed capacity.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_RAWPTR_STREAMS_H
+#define CASA_RAWPTR_STREAMS_H
+
+#include "cpprest/astreambuf.h"
+#include "cpprest/streams.h"
+#include "pplx/pplxtasks.h"
+#include <algorithm>
+#include <iterator>
+#include <queue>
+#include <vector>
+
+namespace Concurrency
+{
+namespace streams
+{
+// Forward declarations
+template<typename _CharType>
+class rawptr_buffer;
+
+namespace details
+{
+/// <summary>
+/// The basic_rawptr_buffer class serves as a memory-based steam buffer that supports both writing and reading
+/// sequences of characters to and from a fixed-size block.
+/// </summary>
+template<typename _CharType>
+class basic_rawptr_buffer : public streams::details::streambuf_state_manager<_CharType>
+{
+public:
+    typedef _CharType char_type;
+
+    typedef typename basic_streambuf<_CharType>::traits traits;
+    typedef typename basic_streambuf<_CharType>::int_type int_type;
+    typedef typename basic_streambuf<_CharType>::pos_type pos_type;
+    typedef typename basic_streambuf<_CharType>::off_type off_type;
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    basic_rawptr_buffer()
+        : streambuf_state_manager<_CharType>(std::ios_base::in | std::ios_base::out)
+        , m_data(nullptr)
+        , m_current_position(0)
+        , m_size(0)
+    {
+    }
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    virtual ~basic_rawptr_buffer()
+    {
+        this->_close_read();
+        this->_close_write();
+    }
+
+protected:
+    /// <summary>
+    /// can_seek is used to determine whether a stream buffer supports seeking.
+    /// </summary>
+    virtual bool can_seek() const { return this->is_open(); }
+
+    /// <summary>
+    /// <c>has_size<c/> is used to determine whether a stream buffer supports size().
+    /// </summary>
+    virtual bool has_size() const { return this->is_open(); }
+
+    /// <summary>
+    /// Gets the size of the stream, if known. Calls to <c>has_size</c> will determine whether
+    /// the result of <c>size</c> can be relied on.
+    /// </summary>
+    virtual utility::size64_t size() const { return utility::size64_t(m_size); }
+
+    /// <summary>
+    /// Get the stream buffer size, if one has been set.
+    /// </summary>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
+    virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
+
+    /// <summary>
+    /// Set the stream buffer implementation to buffer or not buffer.
+    /// </summary>
+    /// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
+    /// <param name="direction">The direction of buffering (in or out)</param>
+    /// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
+    /// will not have
+    ///          any effect on what is returned by subsequent calls to buffer_size().</remarks>
+    virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
+
+    /// <summary>
+    /// For any input stream, in_avail returns the number of characters that are immediately available
+    /// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> and sgetn() to
+    /// read data without incurring the overhead of using tasks.
+    /// </summary>
+    virtual size_t in_avail() const
+    {
+        // See the comment in seek around the restiction that we do not allow read head to
+        // seek beyond the current size.
+        _ASSERTE(m_current_position <= m_size);
+
+        msl::safeint3::SafeInt<size_t> readhead(m_current_position);
+        msl::safeint3::SafeInt<size_t> writeend(m_size);
+        return (size_t)(writeend - readhead);
+    }
+
+    /// <summary>
+    /// Closes the stream buffer, preventing further read or write operations.
+    /// </summary>
+    /// <param name="mode">The I/O mode (in or out) to close for.</param>
+    virtual pplx::task<void> close(std::ios_base::openmode mode)
+    {
+        if (mode & std::ios_base::in)
+        {
+            this->_close_read().get(); // Safe to call get() here.
+        }
+
+        if (mode & std::ios_base::out)
+        {
+            this->_close_write().get(); // Safe to call get() here.
+        }
+
+        if (!this->can_read() && !this->can_write())
+        {
+            m_data = nullptr;
+        }
+
+        // Exceptions will be propagated out of _close_read or _close_write
+        return pplx::task_from_result();
+    }
+
+    virtual pplx::task<bool> _sync() { return pplx::task_from_result(true); }
+
+    virtual pplx::task<int_type> _putc(_CharType ch)
+    {
+        if (m_current_position >= m_size) return pplx::task_from_result<int_type>(traits::eof());
+        int_type retVal = (this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof();
+        return pplx::task_from_result<int_type>(retVal);
+    }
+
+    virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
+    {
+        msl::safeint3::SafeInt<size_t> newSize = msl::safeint3::SafeInt<size_t>(count) + m_current_position;
+        if (newSize > m_size)
+            return pplx::task_from_exception<size_t>(
+                std::make_exception_ptr(std::runtime_error("Writing past the end of the buffer")));
+        return pplx::task_from_result<size_t>(this->write(ptr, count));
+    }
+
+    /// <summary>
+    /// Allocates a contiguous memory block and returns it.
+    /// </summary>
+    /// <param name="count">The number of characters to allocate.</param>
+    /// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
+    /// alloc/commit.</returns>
+    _CharType* _alloc(size_t count)
+    {
+        if (!this->can_write()) return nullptr;
+
+        msl::safeint3::SafeInt<size_t> readhead(m_current_position);
+        msl::safeint3::SafeInt<size_t> writeend(m_size);
+        size_t space_left = (size_t)(writeend - readhead);
+
+        if (space_left < count) return nullptr;
+
+        // Let the caller copy the data
+        return (_CharType*)(m_data + m_current_position);
+    }
+
+    /// <summary>
+    /// Submits a block already allocated by the stream buffer.
+    /// </summary>
+    /// <param name="count">The number of characters to be committed.</param>
+    void _commit(size_t actual)
+    {
+        // Update the write position and satisfy any pending reads
+        update_current_position(m_current_position + actual);
+    }
+
+    /// <summary>
+    /// Gets a pointer to the next already allocated contiguous block of data.
+    /// </summary>
+    /// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
+    /// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
+    /// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
+    /// <remarks>
+    /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
+    /// there is no block to return immediately or that the stream buffer does not support the operation.
+    /// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
+    /// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
+    /// a subsequent read will not succeed.
+    /// </remarks>
+    virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
+    {
+        count = 0;
+        ptr = nullptr;
+
+        if (!this->can_read()) return false;
+
+        count = in_avail();
+
+        if (count > 0)
+        {
+            ptr = (_CharType*)(m_data + m_current_position);
+            return true;
+        }
+        else
+        {
+            ptr = nullptr;
+
+            // Can only be open for read OR write, not both. If there is no data then
+            // we have reached the end of the stream so indicate such with true.
+            return true;
+        }
+    }
+
+    /// <summary>
+    /// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
+    /// de-allocate the memory, if it so desires. Move the read position ahead by the count.
+    /// </summary>
+    /// <param name="ptr">A pointer to the block of data to be released.</param>
+    /// <param name="count">The number of characters that were read.</param>
+    virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
+    {
+        if (ptr != nullptr) update_current_position(m_current_position + count);
+    }
+
+    virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        return pplx::task_from_result(this->read(ptr, count));
+    }
+
+    size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return this->read(ptr, count); }
+
+    virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+    {
+        return this->read(ptr, count, false);
+    }
+
+    virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result(this->read_byte(true)); }
+
+    virtual int_type _sbumpc() { return this->read_byte(true); }
+
+    virtual pplx::task<int_type> _getc() { return pplx::task_from_result(this->read_byte(false)); }
+
+    int_type _sgetc() { return this->read_byte(false); }
+
+    virtual pplx::task<int_type> _nextc()
+    {
+        if (m_current_position >= m_size - 1) return pplx::task_from_result(basic_streambuf<_CharType>::traits::eof());
+
+        this->read_byte(true);
+        return pplx::task_from_result(this->read_byte(false));
+    }
+
+    virtual pplx::task<int_type> _ungetc()
+    {
+        auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in);
+        if (pos == (pos_type)traits::eof()) return pplx::task_from_result(traits::eof());
+        return this->getc();
+    }
+
+    /// <summary>
+    /// Gets the current read or write position in the stream.
+    /// </summary>
+    /// <param name="direction">The I/O direction to seek (see remarks)</param>
+    /// <returns>The current position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the direction parameter defines whether to move the read or the write
+    ///          cursor.</remarks>
+    virtual pos_type getpos(std::ios_base::openmode mode) const
+    {
+        if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
+            return static_cast<pos_type>(traits::eof());
+
+        if (mode == std::ios_base::in)
+            return (pos_type)m_current_position;
+        else if (mode == std::ios_base::out)
+            return (pos_type)m_current_position;
+        else
+            return (pos_type)traits::eof();
+    }
+
+    /// <summary>
+    /// Seeks to the given position.
+    /// </summary>
+    /// <param name="pos">The offset from the beginning of the stream.</param>
+    /// <param name="direction">The I/O direction to seek (see remarks).</param>
+    /// <returns>The position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors. For such streams, the direction parameter
+    /// defines whether to move the read or the write cursor.</remarks>
+    virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode)
+    {
+        pos_type beg(0);
+        pos_type end(m_size);
+
+        if (position >= beg)
+        {
+            auto pos = static_cast<size_t>(position);
+
+            // Read head
+            if ((mode & std::ios_base::in) && this->can_read())
+            {
+                if (position <= end)
+                {
+                    // We do not allow reads to seek beyond the end or before the start position.
+                    update_current_position(pos);
+                    return static_cast<pos_type>(m_current_position);
+                }
+            }
+
+            // Write head
+            if ((mode & std::ios_base::out) && this->can_write())
+            {
+                // Update write head and satisfy read requests if any
+                update_current_position(pos);
+
+                return static_cast<pos_type>(m_current_position);
+            }
+        }
+
+        return static_cast<pos_type>(traits::eof());
+    }
+
+    /// <summary>
+    /// Seeks to a position given by a relative offset.
+    /// </summary>
+    /// <param name="offset">The relative position to seek to</param>
+    /// <param name="way">The starting point (beginning, end, current) for the seek.</param>
+    /// <param name="mode">The I/O direction to seek (see remarks)</param>
+    /// <returns>The position. EOF if the operation fails.</returns>
+    /// <remarks>Some streams may have separate write and read cursors.
+    ///          For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
+    virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
+    {
+        pos_type beg = 0;
+        pos_type cur = static_cast<pos_type>(m_current_position);
+        pos_type end = static_cast<pos_type>(m_size);
+
+        switch (way)
+        {
+            case std::ios_base::beg: return seekpos(beg + offset, mode);
+
+            case std::ios_base::cur: return seekpos(cur + offset, mode);
+
+            case std::ios_base::end: return seekpos(end + offset, mode);
+
+            default: return static_cast<pos_type>(traits::eof());
+        }
+    }
+
+private:
+    template<typename _CharType1>
+    friend class ::concurrency::streams::rawptr_buffer;
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <param name="data">The address (pointer to) the memory block.</param>
+    /// <param name="size">The memory block size, measured in number of characters.</param>
+    basic_rawptr_buffer(const _CharType* data, size_t size)
+        : streambuf_state_manager<_CharType>(std::ios_base::in)
+        , m_data(const_cast<_CharType*>(data))
+        , m_size(size)
+        , m_current_position(0)
+    {
+        validate_mode(std::ios_base::in);
+    }
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <param name="data">The address (pointer to) the memory block.</param>
+    /// <param name="size">The memory block size, measured in number of characters.</param>
+    /// <param name="mode">The stream mode (in, out, etc.).</param>
+    basic_rawptr_buffer(_CharType* data, size_t size, std::ios_base::openmode mode)
+        : streambuf_state_manager<_CharType>(mode), m_data(data), m_size(size), m_current_position(0)
+    {
+        validate_mode(mode);
+    }
+
+    static void validate_mode(std::ios_base::openmode mode)
+    {
+        // Disallow simultaneous use of the stream buffer for writing and reading.
+        if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
+            throw std::invalid_argument("this combination of modes on raw pointer stream not supported");
+    }
+
+    /// <summary>
+    /// Determines if the request can be satisfied.
+    /// </summary>
+    bool can_satisfy(size_t) const
+    {
+        // We can always satisfy a read, at least partially, unless the
+        // read position is at the very end of the buffer.
+        return (in_avail() > 0);
+    }
+
+    /// <summary>
+    /// Reads a byte from the stream and returns it as int_type.
+    /// Note: This routine must only be called if can_satisfy() returns true.
+    /// </summary>
+    int_type read_byte(bool advance = true)
+    {
+        _CharType value;
+        auto read_size = this->read(&value, 1, advance);
+        return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
+    }
+
+    /// <summary>
+    /// Reads up to count characters into ptr and returns the count of characters copied.
+    /// The return value (actual characters copied) could be <= count.
+    /// Note: This routine must only be called if can_satisfy() returns true.
+    /// </summary>
+    size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
+    {
+        if (!can_satisfy(count)) return 0;
+
+        msl::safeint3::SafeInt<size_t> request_size(count);
+        msl::safeint3::SafeInt<size_t> read_size = request_size.Min(in_avail());
+
+        size_t newPos = m_current_position + read_size;
+
+        auto readBegin = m_data + m_current_position;
+        auto readEnd = m_data + newPos;
+
+#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
+        // Avoid warning C4996: Use checked iterators under SECURE_SCL
+        std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType*>(ptr, count));
+#else
+        std::copy(readBegin, readEnd, ptr);
+#endif // _WIN32
+
+        if (advance)
+        {
+            update_current_position(newPos);
+        }
+
+        return (size_t)read_size;
+    }
+
+    /// <summary>
+    /// Write count characters from the ptr into the stream buffer
+    /// </summary>
+    size_t write(const _CharType* ptr, size_t count)
+    {
+        if (!this->can_write() || (count == 0)) return 0;
+
+        msl::safeint3::SafeInt<size_t> newSize = msl::safeint3::SafeInt<size_t>(count) + m_current_position;
+
+        if (newSize > m_size) throw std::runtime_error("Writing past the end of the buffer");
+
+            // Copy the data
+#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
+        // Avoid warning C4996: Use checked iterators under SECURE_SCL
+        std::copy(ptr, ptr + count, stdext::checked_array_iterator<_CharType*>(m_data, m_size, m_current_position));
+#else
+        std::copy(ptr, ptr + count, m_data + m_current_position);
+#endif // _WIN32
+
+        // Update write head and satisfy pending reads if any
+        update_current_position(newSize);
+
+        return count;
+    }
+
+    /// <summary>
+    /// Updates the current read or write position
+    /// </summary>
+    void update_current_position(size_t newPos)
+    {
+        // The new write head
+        m_current_position = newPos;
+
+        _ASSERTE(m_current_position <= m_size);
+    }
+
+    // The actual memory block
+    _CharType* m_data;
+
+    // The size of the memory block
+    size_t m_size;
+
+    // Read/write head
+    size_t m_current_position;
+};
+
+} // namespace details
+
+/// <summary>
+/// The <c>rawptr_buffer</c> class serves as a memory-based stream buffer that supports reading
+/// sequences of characters to or from a fixed-size block. Note that it cannot be used simultaneously for reading as
+/// well as writing.
+/// </summary>
+/// <typeparam name="_CharType">
+/// The data type of the basic element of the <c>rawptr_buffer</c>.
+/// </typeparam>
+template<typename _CharType>
+class rawptr_buffer : public streambuf<_CharType>
+{
+public:
+    typedef _CharType char_type;
+
+    /// <summary>
+    /// Create a rawptr_buffer given a pointer to a memory block and the size of the block.
+    /// </summary>
+    /// <param name="data">The address (pointer to) the memory block.</param>
+    /// <param name="size">The memory block size, measured in number of characters.</param>
+    rawptr_buffer(const char_type* data, size_t size)
+        : streambuf<char_type>(std::shared_ptr<details::basic_rawptr_buffer<char_type>>(
+              new details::basic_rawptr_buffer<char_type>(data, size)))
+    {
+    }
+
+    /// <summary>
+    /// Create a rawptr_buffer given a pointer to a memory block and the size of the block.
+    /// </summary>
+    /// <param name="data">The address (pointer to) the memory block.</param>
+    /// <param name="size">The memory block size, measured in number of characters.</param>
+    rawptr_buffer(char_type* data, size_t size, std::ios_base::openmode mode = std::ios::out)
+        : streambuf<char_type>(std::shared_ptr<details::basic_rawptr_buffer<char_type>>(
+              new details::basic_rawptr_buffer<char_type>(data, size, mode)))
+    {
+    }
+
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    rawptr_buffer() {}
+};
+
+/// <summary>
+/// The rawptr_stream class is used to create memory-backed streams that support writing or reading
+/// sequences of characters to / from a fixed-size block.
+/// </summary>
+/// <typeparam name="_CharType">
+/// The data type of the basic element of the <c>rawptr_stream</c>.
+/// </typeparam>
+template<typename _CharType>
+class rawptr_stream
+{
+public:
+    typedef _CharType char_type;
+    typedef rawptr_buffer<_CharType> buffer_type;
+
+    /// <summary>
+    /// Create a rawptr-stream given a pointer to a read-only memory block and the size of the block.
+    /// </summary>
+    /// <param name="data">The address (pointer to) the memory block.</param>
+    /// <param name="size">The memory block size, measured in number of characters.</param>
+    /// <returns>An opened input stream.</returns>
+    static concurrency::streams::basic_istream<char_type> open_istream(const char_type* data, size_t size)
+    {
+        return concurrency::streams::basic_istream<char_type>(buffer_type(data, size));
+    }
+
+    /// <summary>
+    /// Create a rawptr-stream given a pointer to a writable memory block and the size of the block.
+    /// </summary>
+    /// <param name="data">The address (pointer to) the memory block.</param>
+    /// <param name="size">The memory block size, measured in number of characters.</param>
+    /// <returns>An opened input stream.</returns>
+    static concurrency::streams::basic_istream<char_type> open_istream(char_type* data, size_t size)
+    {
+        return concurrency::streams::basic_istream<char_type>(buffer_type(data, size, std::ios::in));
+    }
+
+    /// <summary>
+    /// Create a rawptr-stream given a pointer to a writable memory block and the size of the block.
+    /// </summary>
+    /// <param name="data">The address (pointer to) the memory block.</param>
+    /// <param name="size">The memory block size, measured in number of characters.</param>
+    /// <returns>An opened output stream.</returns>
+    static concurrency::streams::basic_ostream<char_type> open_ostream(char_type* data, size_t size)
+    {
+        return concurrency::streams::basic_ostream<char_type>(buffer_type(data, size, std::ios::out));
+    }
+};
+
+} // namespace streams
+} // namespace Concurrency
+
+#endif
diff --git a/Release/include/cpprest/streams.h b/Release/include/cpprest/streams.h
new file mode 100644 (file)
index 0000000..b6c3864
--- /dev/null
@@ -0,0 +1,1761 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Asynchronous I/O: streams API, used for formatted input and output, based on unformatted I/O using stream buffers
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_STREAMS_H
+#define CASA_STREAMS_H
+
+#include "cpprest/astreambuf.h"
+#include <iosfwd>
+#include <cstdio>
+
+namespace Concurrency
+{
+namespace streams
+{
+template<typename CharType>
+class basic_ostream;
+template<typename CharType>
+class basic_istream;
+
+namespace details
+{
+template<typename CharType>
+class basic_ostream_helper
+{
+public:
+    basic_ostream_helper(streams::streambuf<CharType> buffer) : m_buffer(buffer) {}
+
+    ~basic_ostream_helper() {}
+
+private:
+    template<typename CharType1>
+    friend class streams::basic_ostream;
+
+    concurrency::streams::streambuf<CharType> m_buffer;
+};
+
+template<typename CharType>
+class basic_istream_helper
+{
+public:
+    basic_istream_helper(streams::streambuf<CharType> buffer) : m_buffer(buffer) {}
+
+    ~basic_istream_helper() {}
+
+private:
+    template<typename CharType1>
+    friend class streams::basic_istream;
+
+    concurrency::streams::streambuf<CharType> m_buffer;
+};
+
+template<typename CharType>
+struct Value2StringFormatter
+{
+    template<typename T>
+    static std::basic_string<CharType> format(const T& val)
+    {
+        std::basic_ostringstream<CharType> ss;
+        ss << val;
+        return ss.str();
+    }
+};
+
+template<>
+struct Value2StringFormatter<uint8_t>
+{
+    template<typename T>
+    static std::basic_string<uint8_t> format(const T& val)
+    {
+        std::basic_ostringstream<char> ss;
+        ss << val;
+        return reinterpret_cast<const uint8_t*>(ss.str().c_str());
+    }
+
+    static std::basic_string<uint8_t> format(const utf16string& val)
+    {
+        return format(utility::conversions::utf16_to_utf8(val));
+    }
+};
+
+static const char* _in_stream_msg = "stream not set up for input of data";
+static const char* _in_streambuf_msg = "stream buffer not set up for input of data";
+static const char* _out_stream_msg = "stream not set up for output of data";
+static const char* _out_streambuf_msg = "stream buffer not set up for output of data";
+} // namespace details
+
+/// <summary>
+/// Base interface for all asynchronous output streams.
+/// </summary>
+template<typename CharType>
+class basic_ostream
+{
+public:
+    typedef char_traits<CharType> traits;
+    typedef typename traits::int_type int_type;
+    typedef typename traits::pos_type pos_type;
+    typedef typename traits::off_type off_type;
+
+    /// <summary>
+    /// Default constructor
+    /// </summary>
+    basic_ostream() {}
+
+    /// <summary>
+    /// Copy constructor
+    /// </summary>
+    /// <param name="other">The source object</param>
+    basic_ostream(const basic_ostream& other) : m_helper(other.m_helper) {}
+
+    /// <summary>
+    /// Assignment operator
+    /// </summary>
+    /// <param name="other">The source object</param>
+    /// <returns>A reference to the stream object that contains the result of the assignment.</returns>
+    basic_ostream& operator=(const basic_ostream& other)
+    {
+        m_helper = other.m_helper;
+        return *this;
+    }
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <param name="buffer">A stream buffer.</param>
+    basic_ostream(streams::streambuf<CharType> buffer)
+        : m_helper(std::make_shared<details::basic_ostream_helper<CharType>>(buffer))
+    {
+        _verify_and_throw(details::_out_streambuf_msg);
+    }
+
+    /// <summary>
+    /// Close the stream, preventing further write operations.
+    /// </summary>
+    pplx::task<void> close() const
+    {
+        return is_valid() ? helper()->m_buffer.close(std::ios_base::out) : pplx::task_from_result();
+    }
+
+    /// <summary>
+    /// Close the stream with exception, preventing further write operations.
+    /// </summary>
+    /// <param name="eptr">Pointer to the exception.</param>
+    pplx::task<void> close(std::exception_ptr eptr) const
+    {
+        return is_valid() ? helper()->m_buffer.close(std::ios_base::out, eptr) : pplx::task_from_result();
+    }
+
+    /// <summary>
+    /// Put a single character into the stream.
+    /// </summary>
+    /// <param name="ch">A character</param>
+    pplx::task<int_type> write(CharType ch) const
+    {
+        pplx::task<int_type> result;
+        if (!_verify_and_return_task(details::_out_stream_msg, result)) return result;
+        return helper()->m_buffer.putc(ch);
+    }
+
+    /// <summary>
+    /// Write a single value of "blittable" type T into the stream.
+    /// </summary>
+    /// <param name="value">A value of type T.</param>
+    /// <remarks>
+    /// This is not a replacement for a proper binary serialization solution, but it may
+    /// form the foundation for one. Writing data bit-wise to a stream is a primitive
+    /// operation of binary serialization.
+    /// Currently, no attention is paid to byte order. All data is written in the platform's
+    /// native byte order, which means little-endian on all platforms that have been tested.
+    /// This function is only available for streams using a single-byte character size.
+    /// </remarks>
+    template<typename T>
+    CASABLANCA_DEPRECATED(
+        "Unsafe API that will be removed in future releases, use one of the other write overloads instead.")
+    pplx::task<size_t> write(T value) const
+    {
+        static_assert(sizeof(CharType) == 1, "binary write is only supported for single-byte streams");
+        static_assert(std::is_trivial<T>::value, "unsafe to use with non-trivial types");
+
+        pplx::task<size_t> result;
+        if (!_verify_and_return_task(details::_out_stream_msg, result)) return result;
+
+        auto copy = std::make_shared<T>(std::move(value));
+        return helper()
+            ->m_buffer.putn_nocopy((CharType*)copy.get(), sizeof(T))
+            .then([copy](pplx::task<size_t> op) -> size_t { return op.get(); });
+    }
+
+    /// <summary>
+    /// Write a number of characters from a given stream buffer into the stream.
+    /// </summary>
+    /// <param name="source">A source stream buffer.</param>
+    /// <param name="count">The number of characters to write.</param>
+    pplx::task<size_t> write(streams::streambuf<CharType> source, size_t count) const
+    {
+        pplx::task<size_t> result;
+        if (!_verify_and_return_task(details::_out_stream_msg, result)) return result;
+        if (!source.can_read())
+            return pplx::task_from_exception<size_t>(
+                std::make_exception_ptr(std::runtime_error("source buffer not set up for input of data")));
+
+        if (count == 0) return pplx::task_from_result((size_t)0);
+
+        auto buffer = helper()->m_buffer;
+        auto data = buffer.alloc(count);
+
+        if (data != nullptr)
+        {
+            auto post_read = [buffer](pplx::task<size_t> op) -> pplx::task<size_t> {
+                auto b = buffer;
+                b.commit(op.get());
+                return op;
+            };
+            return source.getn(data, count).then(post_read);
+        }
+        else
+        {
+            size_t available = 0;
+
+            const bool acquired = source.acquire(data, available);
+            if (available >= count)
+            {
+                auto post_write = [source, data](pplx::task<size_t> op) -> pplx::task<size_t> {
+                    auto s = source;
+                    s.release(data, op.get());
+                    return op;
+                };
+                return buffer.putn_nocopy(data, count).then(post_write);
+            }
+            else
+            {
+                // Always have to release if acquire returned true.
+                if (acquired)
+                {
+                    source.release(data, 0);
+                }
+
+                std::shared_ptr<CharType> buf(new CharType[count], [](CharType* buf) { delete[] buf; });
+
+                auto post_write = [buf](pplx::task<size_t> op) -> pplx::task<size_t> { return op; };
+                auto post_read = [buf, post_write, buffer](pplx::task<size_t> op) -> pplx::task<size_t> {
+                    auto b = buffer;
+                    return b.putn_nocopy(buf.get(), op.get()).then(post_write);
+                };
+
+                return source.getn(buf.get(), count).then(post_read);
+            }
+        }
+    }
+
+    /// <summary>
+    /// Write the specified string to the output stream.
+    /// </summary>
+    /// <param name="str">Input string.</param>
+    pplx::task<size_t> print(const std::basic_string<CharType>& str) const
+    {
+        pplx::task<size_t> result;
+        if (!_verify_and_return_task(details::_out_stream_msg, result)) return result;
+
+        if (str.empty())
+        {
+            return pplx::task_from_result<size_t>(0);
+        }
+        else
+        {
+            auto sharedStr = std::make_shared<std::basic_string<CharType>>(str);
+            return helper()->m_buffer.putn_nocopy(sharedStr->c_str(), sharedStr->size()).then([sharedStr](size_t size) {
+                return size;
+            });
+        }
+    }
+
+    /// <summary>
+    /// Write a value of type <c>T</c> to the output stream.
+    /// </summary>
+    /// <typeparam name="T">
+    /// The data type of the object to be written to the stream
+    /// </typeparam>
+    /// <param name="val">Input object.</param>
+    template<typename T>
+    pplx::task<size_t> print(const T& val) const
+    {
+        pplx::task<size_t> result;
+        if (!_verify_and_return_task(details::_out_stream_msg, result)) return result;
+        // TODO in the future this could be improved to have Value2StringFormatter avoid another unnecessary copy
+        // by putting the string on the heap before calling the print string overload.
+        return print(details::Value2StringFormatter<CharType>::format(val));
+    }
+
+    /// <summary>
+    /// Write a value of type <c>T</c> to the output stream and append a newline character.
+    /// </summary>
+    /// <typeparam name="T">
+    /// The data type of the object to be written to the stream
+    /// </typeparam>
+    /// <param name="val">Input object.</param>
+    template<typename T>
+    pplx::task<size_t> print_line(const T& val) const
+    {
+        pplx::task<size_t> result;
+        if (!_verify_and_return_task(details::_out_stream_msg, result)) return result;
+        auto str = details::Value2StringFormatter<CharType>::format(val);
+        str.push_back(CharType('\n'));
+        return print(str);
+    }
+
+    /// <summary>
+    /// Flush any buffered output data.
+    /// </summary>
+    pplx::task<void> flush() const
+    {
+        pplx::task<void> result;
+        if (!_verify_and_return_task(details::_out_stream_msg, result)) return result;
+        return helper()->m_buffer.sync();
+    }
+
+    /// <summary>
+    /// Seeks to the specified write position.
+    /// </summary>
+    /// <param name="pos">An offset relative to the beginning of the stream.</param>
+    /// <returns>The new position in the stream.</returns>
+    pos_type seek(pos_type pos) const
+    {
+        _verify_and_throw(details::_out_stream_msg);
+        return helper()->m_buffer.seekpos(pos, std::ios_base::out);
+    }
+
+    /// <summary>
+    /// Seeks to the specified write position.
+    /// </summary>
+    /// <param name="off">An offset relative to the beginning, current write position, or the end of the stream.</param>
+    /// <param name="way">The starting point (beginning, current, end) for the seek.</param>
+    /// <returns>The new position in the stream.</returns>
+    pos_type seek(off_type off, std::ios_base::seekdir way) const
+    {
+        _verify_and_throw(details::_out_stream_msg);
+        return helper()->m_buffer.seekoff(off, way, std::ios_base::out);
+    }
+
+    /// <summary>
+    /// Get the current write position, i.e. the offset from the beginning of the stream.
+    /// </summary>
+    /// <returns>The current write position.</returns>
+    pos_type tell() const
+    {
+        _verify_and_throw(details::_out_stream_msg);
+        return helper()->m_buffer.getpos(std::ios_base::out);
+    }
+
+    /// <summary>
+    /// <c>can_seek<c/> is used to determine whether the stream supports seeking.
+    /// </summary>
+    /// <returns><c>true</c> if the stream supports seeking, <c>false</c> otherwise.</returns>
+    bool can_seek() const { return is_valid() && m_helper->m_buffer.can_seek(); }
+
+    /// <summary>
+    /// Test whether the stream has been initialized with a valid stream buffer.
+    /// </summary>
+    /// <returns><c>true</c> if the stream has been initialized with a valid stream buffer, <c>false</c>
+    /// otherwise.</returns>
+    bool is_valid() const { return (m_helper != nullptr) && ((bool)m_helper->m_buffer); }
+
+    /// <summary>
+    /// Test whether the stream has been initialized or not.
+    /// </summary>
+    operator bool() const { return is_valid(); }
+
+    /// <summary>
+    /// Test whether the stream is open for writing.
+    /// </summary>
+    /// <returns><c>true</c> if the stream is open for writing, <c>false</c> otherwise.</returns>
+    bool is_open() const { return is_valid() && m_helper->m_buffer.can_write(); }
+
+    /// <summary>
+    /// Get the underlying stream buffer.
+    /// </summary>
+    /// <returns>The underlying stream buffer.</returns>
+    concurrency::streams::streambuf<CharType> streambuf() const { return helper()->m_buffer; }
+
+protected:
+    void set_helper(std::shared_ptr<details::basic_ostream_helper<CharType>> helper) { m_helper = helper; }
+
+private:
+    template<typename T>
+    bool _verify_and_return_task(const char* msg, pplx::task<T>& tsk) const
+    {
+        auto buffer = helper()->m_buffer;
+        if (!(buffer.exception() == nullptr))
+        {
+            tsk = pplx::task_from_exception<T>(buffer.exception());
+            return false;
+        }
+        if (!buffer.can_write())
+        {
+            tsk = pplx::task_from_exception<T>(std::make_exception_ptr(std::runtime_error(msg)));
+            return false;
+        }
+        return true;
+    }
+
+    void _verify_and_throw(const char* msg) const
+    {
+        auto buffer = helper()->m_buffer;
+        if (!(buffer.exception() == nullptr)) std::rethrow_exception(buffer.exception());
+        if (!buffer.can_write()) throw std::runtime_error(msg);
+    }
+
+    std::shared_ptr<details::basic_ostream_helper<CharType>> helper() const
+    {
+        if (!m_helper) throw std::logic_error("uninitialized stream object");
+        return m_helper;
+    }
+
+    std::shared_ptr<details::basic_ostream_helper<CharType>> m_helper;
+};
+
+template<typename int_type>
+struct _type_parser_integral_traits
+{
+    typedef std::false_type _is_integral;
+    typedef std::false_type _is_unsigned;
+};
+
+#ifdef _WIN32
+#define _INT_TRAIT(_t, _low, _high)                                                                                    \
+    template<>                                                                                                         \
+    struct _type_parser_integral_traits<_t>                                                                            \
+    {                                                                                                                  \
+        typedef std::true_type _is_integral;                                                                           \
+        typedef std::false_type _is_unsigned;                                                                          \
+        static const int64_t _min = _low;                                                                              \
+        static const int64_t _max = _high;                                                                             \
+    };
+#define _UINT_TRAIT(_t, _low, _high)                                                                                   \
+    template<>                                                                                                         \
+    struct _type_parser_integral_traits<_t>                                                                            \
+    {                                                                                                                  \
+        typedef std::true_type _is_integral;                                                                           \
+        typedef std::true_type _is_unsigned;                                                                           \
+        static const uint64_t _max = _high;                                                                            \
+    };
+
+_INT_TRAIT(char, INT8_MIN, INT8_MAX)
+_INT_TRAIT(signed char, INT8_MIN, INT8_MAX)
+_INT_TRAIT(short, INT16_MIN, INT16_MAX)
+#if defined(_NATIVE_WCHAR_T_DEFINED)
+_INT_TRAIT(wchar_t, WCHAR_MIN, WCHAR_MAX)
+#endif
+_INT_TRAIT(int, INT32_MIN, INT32_MAX)
+_INT_TRAIT(long, LONG_MIN, LONG_MAX)
+_INT_TRAIT(long long, LLONG_MIN, LLONG_MAX)
+_UINT_TRAIT(unsigned char, UINT8_MIN, UINT8_MAX)
+_UINT_TRAIT(unsigned short, UINT16_MIN, UINT16_MAX)
+_UINT_TRAIT(unsigned int, UINT32_MIN, UINT32_MAX)
+_UINT_TRAIT(unsigned long, ULONG_MIN, ULONG_MAX)
+_UINT_TRAIT(unsigned long long, ULLONG_MIN, ULLONG_MAX)
+#else
+#define _INT_TRAIT(_t)                                                                                                 \
+    template<>                                                                                                         \
+    struct _type_parser_integral_traits<_t>                                                                            \
+    {                                                                                                                  \
+        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 _max = (std::numeric_limits<_t>::max)();                                                  \
+    };
+#define _UINT_TRAIT(_t)                                                                                                \
+    template<>                                                                                                         \
+    struct _type_parser_integral_traits<_t>                                                                            \
+    {                                                                                                                  \
+        typedef std::true_type _is_integral;                                                                           \
+        typedef std::true_type _is_unsigned;                                                                           \
+        static const uint64_t _max = (std::numeric_limits<_t>::max)();                                                 \
+    };
+
+_INT_TRAIT(char)
+_INT_TRAIT(signed char)
+_INT_TRAIT(short)
+_INT_TRAIT(utf16char)
+_INT_TRAIT(int)
+_INT_TRAIT(long)
+_INT_TRAIT(long long)
+_UINT_TRAIT(unsigned char)
+_UINT_TRAIT(unsigned short)
+_UINT_TRAIT(unsigned int)
+_UINT_TRAIT(unsigned long)
+_UINT_TRAIT(unsigned long long)
+#endif
+
+template<typename CharType>
+class _type_parser_base
+{
+public:
+    typedef char_traits<CharType> traits;
+    typedef typename traits::int_type int_type;
+
+    _type_parser_base() {}
+
+protected:
+    // Aid in parsing input: skipping whitespace characters.
+    static pplx::task<void> _skip_whitespace(streams::streambuf<CharType> buffer);
+
+    // Aid in parsing input: peek at a character at a time, call type-specific code to examine, extract value when done.
+    // <remark>AcceptFunctor should model std::function<bool(std::shared_ptr<X>, int_type)></remark>
+    // <remark>ExtractFunctor should model std::function<pplx::task<ReturnType>(std::shared_ptr<X>)></remark>
+    template<typename StateType, typename ReturnType, typename AcceptFunctor, typename ExtractFunctor>
+    static pplx::task<ReturnType> _parse_input(streams::streambuf<CharType> buffer,
+                                               AcceptFunctor accept_character,
+                                               ExtractFunctor extract);
+};
+
+/// <summary>
+/// Class used to handle asynchronous parsing for basic_istream::extract. To support new
+/// types create a new template specialization and implement the parse function.
+/// </summary>
+template<typename CharType, typename T>
+class type_parser
+{
+public:
+    static pplx::task<T> parse(streams::streambuf<CharType> buffer)
+    {
+        typedef typename _type_parser_integral_traits<T>::_is_integral ii;
+        typedef typename _type_parser_integral_traits<T>::_is_unsigned ui;
+
+        static_assert(ii::value || !ui::value, "type is not supported for extraction from a stream");
+
+        return _parse(buffer, ii {}, ui {});
+    }
+
+private:
+    static pplx::task<T> _parse(streams::streambuf<CharType> buffer, std::false_type, std::false_type)
+    {
+        _parse_floating_point(buffer);
+    }
+
+    static pplx::task<T> _parse(streams::streambuf<CharType> buffer, std::true_type, std::false_type)
+    {
+        return type_parser<CharType, int64_t>::parse(buffer).then([](pplx::task<int64_t> op) -> T {
+            int64_t val = op.get();
+            if (val <= _type_parser_integral_traits<T>::_max && val >= _type_parser_integral_traits<T>::_min)
+                return (T)val;
+            else
+                throw std::range_error("input out of range for target type");
+        });
+    }
+
+    static pplx::task<T> _parse(streams::streambuf<CharType> buffer, std::true_type, std::true_type)
+    {
+        return type_parser<CharType, uint64_t>::parse(buffer).then([](pplx::task<uint64_t> op) -> T {
+            uint64_t val = op.get();
+            if (val <= _type_parser_integral_traits<T>::_max)
+                return (T)val;
+            else
+                throw std::range_error("input out of range for target type");
+        });
+    }
+};
+
+/// <summary>
+/// Base interface for all asynchronous input streams.
+/// </summary>
+template<typename CharType>
+class basic_istream
+{
+public:
+    typedef char_traits<CharType> traits;
+    typedef typename traits::int_type int_type;
+    typedef typename traits::pos_type pos_type;
+    typedef typename traits::off_type off_type;
+
+    /// <summary>
+    /// Default constructor
+    /// </summary>
+    basic_istream() {}
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <typeparam name="CharType">
+    /// The data type of the basic element of the stream.
+    /// </typeparam>
+    /// <param name="buffer">A stream buffer.</param>
+    template<class AlterCharType>
+    basic_istream(streams::streambuf<AlterCharType> buffer)
+        : m_helper(std::make_shared<details::basic_istream_helper<CharType>>(std::move(buffer)))
+    {
+        _verify_and_throw(details::_in_streambuf_msg);
+    }
+
+    /// <summary>
+    /// Copy constructor
+    /// </summary>
+    /// <param name="other">The source object</param>
+    basic_istream(const basic_istream& other) : m_helper(other.m_helper) {}
+
+    /// <summary>
+    /// Assignment operator
+    /// </summary>
+    /// <param name="other">The source object</param>
+    /// <returns>A reference to the stream object that contains the result of the assignment.</returns>
+    basic_istream& operator=(const basic_istream& other)
+    {
+        m_helper = other.m_helper;
+        return *this;
+    }
+
+    /// <summary>
+    /// Close the stream, preventing further read operations.
+    /// </summary>
+    pplx::task<void> close() const
+    {
+        return is_valid() ? helper()->m_buffer.close(std::ios_base::in) : pplx::task_from_result();
+    }
+
+    /// <summary>
+    /// Close the stream with exception, preventing further read operations.
+    /// </summary>
+    /// <param name="eptr">Pointer to the exception.</param>
+    pplx::task<void> close(std::exception_ptr eptr) const
+    {
+        return is_valid() ? m_helper->m_buffer.close(std::ios_base::in, eptr) : pplx::task_from_result();
+    }
+
+    /// <summary>
+    /// Tests whether last read cause the stream reach EOF.
+    /// </summary>
+    /// <returns>True if the read head has reached the end of the stream, false otherwise.</returns>
+    bool is_eof() const { return is_valid() ? m_helper->m_buffer.is_eof() : false; }
+
+    /// <summary>
+    /// Get the next character and return it as an int_type. Advance the read position.
+    /// </summary>
+    /// <returns>A <c>task</c> that holds the next character as an <c>int_type</c> on successful completion.</returns>
+    pplx::task<int_type> read() const
+    {
+        pplx::task<int_type> result;
+        if (!_verify_and_return_task(details::_in_stream_msg, result)) return result;
+        return helper()->m_buffer.bumpc();
+    }
+
+    /// <summary>
+    /// Read a single value of "blittable" type T from the stream.
+    /// </summary>
+    /// <returns>A value of type T.</returns>
+    /// <remarks>
+    /// This is not a replacement for a proper binary serialization solution, but it may
+    /// form the foundation for one. Reading data bit-wise to a stream is a primitive
+    /// operation of binary serialization.
+    /// Currently, no attention is paid to byte order. All data is read in the platform's
+    /// native byte order, which means little-endian on all platforms that have been tested.
+    /// This function is only available for streams using a single-byte character size.
+    /// </remarks>
+    template<typename T>
+    CASABLANCA_DEPRECATED(
+        "Unsafe API that will be removed in future releases, use one of the other read overloads instead.")
+    pplx::task<T> read() const
+    {
+        static_assert(sizeof(CharType) == 1, "binary read is only supported for single-byte streams");
+        static_assert(std::is_trivial<T>::value, "unsafe to use with non-trivial types");
+
+        pplx::task<T> result;
+        if (!_verify_and_return_task(details::_in_stream_msg, result)) return result;
+
+        auto copy = std::make_shared<T>();
+        return helper()->m_buffer.getn((CharType*)copy.get(), sizeof(T)).then([copy](pplx::task<size_t>) -> T {
+            return std::move(*copy);
+        });
+    }
+
+    /// <summary>
+    /// Reads up to <c>count</c> characters and place into the provided buffer.
+    /// </summary>
+    /// <param name="target">An async stream buffer supporting write operations.</param>
+    /// <param name="count">The maximum number of characters to read</param>
+    /// <returns>A <c>task</c> that holds the number of characters read. This number is 0 if the end of the stream is
+    /// reached.</returns>
+    pplx::task<size_t> read(streams::streambuf<CharType> target, size_t count) const
+    {
+        pplx::task<size_t> result;
+        if (!_verify_and_return_task(details::_in_stream_msg, result)) return result;
+        if (!target.can_write())
+            return pplx::task_from_exception<size_t>(
+                std::make_exception_ptr(std::runtime_error("target not set up for output of data")));
+
+        // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations.
+        auto buffer = helper()->m_buffer;
+
+        auto data = target.alloc(count);
+
+        if (data != nullptr)
+        {
+            auto post_read = [target](pplx::task<size_t> op) -> pplx::task<size_t> {
+                auto t = target;
+                t.commit(op.get());
+                return op;
+            };
+            return buffer.getn(data, count).then(post_read);
+        }
+        else
+        {
+            size_t available = 0;
+
+            const bool acquired = buffer.acquire(data, available);
+            if (available >= count)
+            {
+                auto post_write = [buffer, data](pplx::task<size_t> op) -> pplx::task<size_t> {
+                    auto b = buffer;
+                    b.release(data, op.get());
+                    return op;
+                };
+                return target.putn_nocopy(data, count).then(post_write);
+            }
+            else
+            {
+                // Always have to release if acquire returned true.
+                if (acquired)
+                {
+                    buffer.release(data, 0);
+                }
+
+                std::shared_ptr<CharType> buf(new CharType[count], [](CharType* buf) { delete[] buf; });
+
+                auto post_write = [buf](pplx::task<size_t> op) -> pplx::task<size_t> { return op; };
+                auto post_read = [buf, target, post_write](pplx::task<size_t> op) -> pplx::task<size_t> {
+                    auto trg = target;
+                    return trg.putn_nocopy(buf.get(), op.get()).then(post_write);
+                };
+
+                return helper()->m_buffer.getn(buf.get(), count).then(post_read);
+            }
+        }
+    }
+
+    /// <summary>
+    /// Get the next character and return it as an int_type. Do not advance the read position.
+    /// </summary>
+    /// <returns>A <c>task</c> that holds the character, widened to an integer. This character is EOF when the peek
+    /// operation fails.</returns>
+    pplx::task<int_type> peek() const
+    {
+        pplx::task<int_type> result;
+        if (!_verify_and_return_task(details::_in_stream_msg, result)) return result;
+        return helper()->m_buffer.getc();
+    }
+
+    /// <summary>
+    /// Read characters until a delimiter or EOF is found, and place them into the target.
+    /// Proceed past the delimiter, but don't include it in the target buffer.
+    /// </summary>
+    /// <param name="target">An async stream buffer supporting write operations.</param>
+    /// <param name="delim">The delimiting character to stop the read at.</param>
+    /// <returns>A <c>task</c> that holds the number of characters read.</returns>
+    pplx::task<size_t> read_to_delim(streams::streambuf<CharType> target, int_type delim) const
+    {
+        pplx::task<size_t> result;
+        if (!_verify_and_return_task(details::_in_stream_msg, result)) return result;
+        if (!target.can_write())
+            return pplx::task_from_exception<size_t>(
+                std::make_exception_ptr(std::runtime_error("target not set up for output of data")));
+
+        // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations.
+        auto buffer = helper()->m_buffer;
+
+        int_type req_async = traits::requires_async();
+
+        std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>();
+
+        auto flush = [=]() mutable {
+            return target.putn_nocopy(_locals->outbuf, _locals->write_pos).then([=](size_t wrote) mutable {
+                _locals->total += wrote;
+                _locals->write_pos = 0;
+                return target.sync();
+            });
+        };
+
+        auto update = [=](int_type ch) mutable {
+            if (ch == traits::eof()) return false;
+            if (ch == delim) return false;
+
+            _locals->outbuf[_locals->write_pos] = static_cast<CharType>(ch);
+            _locals->write_pos += 1;
+
+            if (_locals->is_full())
+            {
+                // Flushing synchronously because performance is terrible if we
+                // schedule an empty task. This isn't on a user's thread.
+                flush().get();
+            }
+
+            return true;
+        };
+
+        auto loop = pplx::details::_do_while([=]() mutable -> pplx::task<bool> {
+            while (buffer.in_avail() > 0)
+            {
+                int_type ch = buffer.sbumpc();
+
+                if (ch == req_async)
+                {
+                    break;
+                }
+
+                if (!update(ch))
+                {
+                    return pplx::task_from_result(false);
+                }
+            }
+            return buffer.bumpc().then(update);
+        });
+
+        return loop.then([=](bool) mutable { return flush().then([=] { return _locals->total; }); });
+    }
+
+    /// <summary>
+    /// Read until reaching a newline character. The newline is not included in the target.
+    /// </summary>
+    /// <param name="target">An asynchronous stream buffer supporting write operations.</param>
+    /// <returns>A <c>task</c> that holds the number of characters read. This number is 0 if the end of the stream is
+    /// reached.</returns>
+    pplx::task<size_t> read_line(streams::streambuf<CharType> target) const
+    {
+        pplx::task<size_t> result;
+        if (!_verify_and_return_task(details::_in_stream_msg, result)) return result;
+        if (!target.can_write())
+            return pplx::task_from_exception<size_t>(
+                std::make_exception_ptr(std::runtime_error("target not set up for receiving data")));
+
+        // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations.
+        concurrency::streams::streambuf<CharType> buffer = helper()->m_buffer;
+
+        int_type req_async = traits::requires_async();
+
+        std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>();
+
+        auto flush = [=]() mutable {
+            return target.putn_nocopy(_locals->outbuf, _locals->write_pos).then([=](size_t wrote) mutable {
+                _locals->total += wrote;
+                _locals->write_pos = 0;
+                return target.sync();
+            });
+        };
+
+        auto update = [=](int_type ch) mutable {
+            if (ch == traits::eof()) return false;
+            if (ch == '\n') return false;
+            if (ch == '\r')
+            {
+                _locals->saw_CR = true;
+                return true;
+            }
+
+            _locals->outbuf[_locals->write_pos] = static_cast<CharType>(ch);
+            _locals->write_pos += 1;
+
+            if (_locals->is_full())
+            {
+                // Flushing synchronously because performance is terrible if we
+                // schedule an empty task. This isn't on a user's thread.
+                flush().wait();
+            }
+
+            return true;
+        };
+
+        auto update_after_cr = [=](int_type ch) mutable -> pplx::task<bool> {
+            if (ch == traits::eof()) return pplx::task_from_result(false);
+            if (ch == '\n')
+            {
+                return buffer.bumpc().then([](int_type) { return false; });
+            }
+            return pplx::task_from_result(false);
+        };
+
+        auto loop = pplx::details::_do_while([=]() mutable -> pplx::task<bool> {
+            while (buffer.in_avail() > 0)
+            {
+                int_type ch;
+
+                if (_locals->saw_CR)
+                {
+                    ch = buffer.sgetc();
+                    if (ch == '\n') buffer.sbumpc();
+                    return pplx::task_from_result(false);
+                }
+
+                ch = buffer.sbumpc();
+
+                if (ch == req_async) break;
+
+                if (!update(ch))
+                {
+                    return pplx::task_from_result(false);
+                }
+            }
+
+            if (_locals->saw_CR)
+            {
+                return buffer.getc().then(update_after_cr);
+            }
+            return buffer.bumpc().then(update);
+        });
+
+        return loop.then([=](bool) mutable { return flush().then([=] { return _locals->total; }); });
+    }
+
+    /// <summary>
+    /// Read until reaching the end of the stream.
+    /// </summary>
+    /// <param name="target">An asynchronous stream buffer supporting write operations.</param>
+    /// <returns>The number of characters read.</returns>
+    pplx::task<size_t> read_to_end(streams::streambuf<CharType> target) const
+    {
+        pplx::task<size_t> result;
+        if (!_verify_and_return_task("stream not set up for output of data", result)) return result;
+        if (!target.can_write())
+            return pplx::task_from_exception<size_t>(
+                std::make_exception_ptr(std::runtime_error("source buffer not set up for input of data")));
+
+        auto l_buffer = helper()->m_buffer;
+        auto l_buf_size = this->buf_size;
+        std::shared_ptr<_read_helper> l_locals = std::make_shared<_read_helper>();
+
+        auto copy_to_target = [l_locals, target, l_buffer, l_buf_size]() mutable -> pplx::task<bool> {
+            // We need to capture these, because the object itself may go away
+            // before we're done processing the data.
+            // auto locs = _locals;
+            // auto trg = target;
+
+            return l_buffer.getn(l_locals->outbuf, l_buf_size).then([=](size_t rd) mutable -> pplx::task<bool> {
+                if (rd == 0) return pplx::task_from_result(false);
+
+                // Must be nested to capture rd
+                return target.putn_nocopy(l_locals->outbuf, rd)
+                    .then([target, l_locals, rd](size_t wr) mutable -> pplx::task<bool> {
+                        l_locals->total += wr;
+
+                        if (rd != wr)
+                            // Number of bytes written is less than number of bytes received.
+                            throw std::runtime_error("failed to write all bytes");
+
+                        return target.sync().then([]() { return true; });
+                    });
+            });
+        };
+
+        auto loop = pplx::details::_do_while(copy_to_target);
+
+        return loop.then([=](bool) mutable -> size_t { return l_locals->total; });
+    }
+
+    /// <summary>
+    /// Seeks to the specified write position.
+    /// </summary>
+    /// <param name="pos">An offset relative to the beginning of the stream.</param>
+    /// <returns>The new position in the stream.</returns>
+    pos_type seek(pos_type pos) const
+    {
+        _verify_and_throw(details::_in_stream_msg);
+        return helper()->m_buffer.seekpos(pos, std::ios_base::in);
+    }
+
+    /// <summary>
+    /// Seeks to the specified write position.
+    /// </summary>
+    /// <param name="off">An offset relative to the beginning, current write position, or the end of the stream.</param>
+    /// <param name="way">The starting point (beginning, current, end) for the seek.</param>
+    /// <returns>The new position in the stream.</returns>
+    pos_type seek(off_type off, std::ios_base::seekdir way) const
+    {
+        _verify_and_throw(details::_in_stream_msg);
+        return helper()->m_buffer.seekoff(off, way, std::ios_base::in);
+    }
+
+    /// <summary>
+    /// Get the current write position, i.e. the offset from the beginning of the stream.
+    /// </summary>
+    /// <returns>The current write position.</returns>
+    pos_type tell() const
+    {
+        _verify_and_throw(details::_in_stream_msg);
+        return helper()->m_buffer.getpos(std::ios_base::in);
+    }
+
+    /// <summary>
+    /// <c>can_seek<c/> is used to determine whether the stream supports seeking.
+    /// </summary>
+    /// <returns><c>true</c> if the stream supports seeking, <c>false</c> otherwise.</returns>
+    bool can_seek() const { return is_valid() && m_helper->m_buffer.can_seek(); }
+
+    /// <summary>
+    /// Test whether the stream has been initialized with a valid stream buffer.
+    /// </summary>
+    bool is_valid() const { return (m_helper != nullptr) && ((bool)m_helper->m_buffer); }
+
+    /// <summary>
+    /// Test whether the stream has been initialized or not.
+    /// </summary>
+    operator bool() const { return is_valid(); }
+
+    /// <summary>
+    /// Test whether the stream is open for writing.
+    /// </summary>
+    /// <returns><c>true</c> if the stream is open for writing, <c>false</c> otherwise.</returns>
+    bool is_open() const { return is_valid() && m_helper->m_buffer.can_read(); }
+
+    /// <summary>
+    /// Get the underlying stream buffer.
+    /// </summary>
+    concurrency::streams::streambuf<CharType> streambuf() const { return helper()->m_buffer; }
+
+    /// <summary>
+    /// Read a value of type <c>T</c> from the stream.
+    /// </summary>
+    /// <remarks>
+    /// Supports the C++ primitive types. Can be expanded to additional types
+    /// by adding template specializations for <c>type_parser</c>.
+    /// </remarks>
+    /// <typeparam name="T">
+    /// The data type of the element to be read from the stream.
+    /// </typeparam>
+    /// <returns>A <c>task</c> that holds the element read from the stream.</returns>
+    template<typename T>
+    pplx::task<T> extract() const
+    {
+        pplx::task<T> result;
+        if (!_verify_and_return_task(details::_in_stream_msg, result)) return result;
+        return type_parser<CharType, T>::parse(helper()->m_buffer);
+    }
+
+private:
+    template<typename T>
+    bool _verify_and_return_task(const char* msg, pplx::task<T>& tsk) const
+    {
+        auto buffer = helper()->m_buffer;
+        if (!(buffer.exception() == nullptr))
+        {
+            tsk = pplx::task_from_exception<T>(buffer.exception());
+            return false;
+        }
+        if (!buffer.can_read())
+        {
+            tsk = pplx::task_from_exception<T>(std::make_exception_ptr(std::runtime_error(msg)));
+            return false;
+        }
+        return true;
+    }
+
+    void _verify_and_throw(const char* msg) const
+    {
+        auto buffer = helper()->m_buffer;
+        if (!(buffer.exception() == nullptr)) std::rethrow_exception(buffer.exception());
+        if (!buffer.can_read()) throw std::runtime_error(msg);
+    }
+
+    std::shared_ptr<details::basic_istream_helper<CharType>> helper() const
+    {
+        if (!m_helper) throw std::logic_error("uninitialized stream object");
+        return m_helper;
+    }
+
+    static const size_t buf_size = 16 * 1024;
+
+    struct _read_helper
+    {
+        size_t total;
+        CharType outbuf[buf_size];
+        size_t write_pos;
+        bool saw_CR;
+
+        bool is_full() const { return write_pos == buf_size; }
+
+        _read_helper() : total(0), write_pos(0), saw_CR(false) {}
+    };
+
+    std::shared_ptr<details::basic_istream_helper<CharType>> m_helper;
+};
+
+typedef basic_ostream<uint8_t> ostream;
+typedef basic_istream<uint8_t> istream;
+
+typedef basic_ostream<utf16char> wostream;
+typedef basic_istream<utf16char> wistream;
+
+template<typename CharType>
+pplx::task<void> _type_parser_base<CharType>::_skip_whitespace(streams::streambuf<CharType> buffer)
+{
+    int_type req_async = traits::requires_async();
+
+    auto update = [=](int_type ch) mutable {
+        if (isspace(ch))
+        {
+            if (buffer.sbumpc() == req_async)
+            {
+                // Synchronously because performance is terrible if we
+                // schedule an empty task. This isn't on a user's thread.
+                buffer.nextc().wait();
+            }
+            return true;
+        }
+
+        return false;
+    };
+
+    auto loop = pplx::details::_do_while([=]() mutable -> pplx::task<bool> {
+        while (buffer.in_avail() > 0)
+        {
+            int_type ch = buffer.sgetc();
+
+            if (ch == req_async) break;
+
+            if (!update(ch))
+            {
+                return pplx::task_from_result(false);
+            }
+        }
+        return buffer.getc().then(update);
+    });
+
+    return loop.then([=](pplx::task<bool> op) { op.wait(); });
+}
+
+template<typename CharType>
+template<typename StateType, typename ReturnType, typename AcceptFunctor, typename ExtractFunctor>
+pplx::task<ReturnType> _type_parser_base<CharType>::_parse_input(concurrency::streams::streambuf<CharType> buffer,
+                                                                 AcceptFunctor accept_character,
+                                                                 ExtractFunctor extract)
+{
+    std::shared_ptr<StateType> state = std::make_shared<StateType>();
+
+    auto update = [=](pplx::task<int_type> op) -> pplx::task<bool> {
+        int_type ch = op.get();
+        if (ch == traits::eof()) 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<CharType> buf = buffer;
+        return buf.bumpc().then([](int_type) { return true; });
+    };
+
+    auto peek_char = [=]() -> pplx::task<bool> {
+        concurrency::streams::streambuf<CharType> buf = buffer;
+
+        // If task results are immediately available, there's little need to use ".then(),"
+        // so optimize for prompt values.
+
+        auto get_op = buf.getc();
+        while (get_op.is_done())
+        {
+            auto condition = update(get_op);
+            if (!condition.is_done() || !condition.get()) return condition;
+
+            get_op = buf.getc();
+        }
+
+        return get_op.then(update);
+    };
+
+    auto finish = [=](pplx::task<bool> op) -> pplx::task<ReturnType> {
+        op.wait();
+        pplx::task<ReturnType> result = extract(state);
+        return result;
+    };
+
+    return _skip_whitespace(buffer).then([=](pplx::task<void> op) -> pplx::task<ReturnType> {
+        op.wait();
+        return pplx::details::_do_while(peek_char).then(finish);
+    });
+}
+
+template<typename CharType>
+class type_parser<CharType, std::basic_string<CharType>> : public _type_parser_base<CharType>
+{
+    typedef _type_parser_base<CharType> base;
+
+public:
+    typedef typename base::traits traits;
+    typedef typename base::int_type int_type;
+
+    static pplx::task<std::string> parse(streams::streambuf<CharType> buffer)
+    {
+        return base::template _parse_input<std::basic_string<CharType>, std::string>(
+            buffer, _accept_char, _extract_result);
+    }
+
+private:
+    static bool _accept_char(std::shared_ptr<std::basic_string<CharType>> state, int_type ch)
+    {
+        if (ch == traits::eof() || isspace(ch)) return false;
+        state->push_back(CharType(ch));
+        return true;
+    }
+    static pplx::task<std::basic_string<CharType>> _extract_result(std::shared_ptr<std::basic_string<CharType>> state)
+    {
+        return pplx::task_from_result(*state);
+    }
+};
+
+template<typename CharType>
+class type_parser<CharType, int64_t> : public _type_parser_base<CharType>
+{
+    typedef _type_parser_base<CharType> base;
+
+public:
+    typedef typename base::traits traits;
+    typedef typename base::int_type int_type;
+
+    static pplx::task<int64_t> parse(streams::streambuf<CharType> buffer)
+    {
+        return base::template _parse_input<_int64_state, int64_t>(buffer, _accept_char, _extract_result);
+    }
+
+private:
+    struct _int64_state
+    {
+        _int64_state() : result(0), correct(false), minus(0) {}
+
+        int64_t result;
+        bool correct;
+        char minus; // 0 -- no sign, 1 -- plus, 2 -- minus
+    };
+
+    static bool _accept_char(std::shared_ptr<_int64_state> state, int_type ch)
+    {
+        if (ch == traits::eof()) return false;
+        if (state->minus == 0)
+        {
+            // OK to find a sign.
+            if (!::isdigit(ch) && ch != int_type('+') && ch != int_type('-')) return false;
+        }
+        else
+        {
+            if (!::isdigit(ch)) return false;
+        }
+
+        // At least one digit was found.
+        state->correct = true;
+
+        if (ch == int_type('+'))
+        {
+            state->minus = 1;
+        }
+        else if (ch == int_type('-'))
+        {
+            state->minus = 2;
+        }
+        else
+        {
+            if (state->minus == 0) state->minus = 1;
+
+            // Shift the existing value by 10, then add the new value.
+            bool positive = state->result >= 0;
+
+            state->result *= 10;
+            state->result += int64_t(ch - int_type('0'));
+
+            if ((state->result >= 0) != positive)
+            {
+                state->correct = false;
+                return false;
+            }
+        }
+        return true;
+    }
+
+    static pplx::task<int64_t> _extract_result(std::shared_ptr<_int64_state> state)
+    {
+        if (!state->correct) throw std::range_error("integer value is too large to fit in 64 bits");
+
+        int64_t result = (state->minus == 2) ? -state->result : state->result;
+        return pplx::task_from_result<int64_t>(result);
+    }
+};
+
+template<typename FloatingPoint>
+struct _double_state
+{
+    _double_state()
+        : result(0)
+        , minus(0)
+        , after_comma(0)
+        , exponent(false)
+        , exponent_number(0)
+        , exponent_minus(0)
+        , complete(false)
+        , p_exception_string()
+    {
+    }
+
+    FloatingPoint result;
+    char minus; // 0 -- no sign, 1 -- plus, 2 -- minus
+    int after_comma;
+    bool exponent;
+    int exponent_number;
+    char exponent_minus; // 0 -- no sign, 1 -- plus, 2 -- minus
+    bool complete;
+    std::string p_exception_string;
+};
+
+template<typename FloatingPoint, typename int_type>
+static std::string create_exception_message(int_type ch, bool exponent)
+{
+    std::string result;
+    if (exponent)
+    {
+        result.assign("Invalid character 'X' in exponent");
+    }
+    else
+    {
+        result.assign("Invalid character 'X'");
+    }
+
+    result[19] = static_cast<char>(ch);
+    return result;
+}
+
+template<typename FloatingPoint, typename int_type>
+static bool _accept_char(std::shared_ptr<_double_state<FloatingPoint>> state, int_type ch)
+{
+    if (state->minus == 0)
+    {
+        if (!::isdigit(ch) && ch != int_type('.') && ch != int_type('+') && ch != int_type('-'))
+        {
+            if (!state->complete)
+                state->p_exception_string = create_exception_message<FloatingPoint, int_type>(ch, false);
+            return false;
+        }
+    }
+    else
+    {
+        if (!state->exponent && !::isdigit(ch) && ch != int_type('.') && ch != int_type('E') && ch != int_type('e'))
+        {
+            if (!state->complete)
+                state->p_exception_string = create_exception_message<FloatingPoint, int_type>(ch, false);
+            return false;
+        }
+
+        if (state->exponent && !::isdigit(ch) && ch != int_type('+') && ch != int_type('-'))
+        {
+            if (!state->complete)
+                state->p_exception_string = create_exception_message<FloatingPoint, int_type>(ch, true);
+            return false;
+        }
+    }
+
+    switch (ch)
+    {
+        case int_type('+'):
+            state->complete = false;
+            if (state->exponent)
+            {
+                if (state->exponent_minus != 0)
+                {
+                    state->p_exception_string = "The exponent sign already set";
+                    return false;
+                }
+                state->exponent_minus = 1;
+            }
+            else
+            {
+                state->minus = 1;
+            }
+            break;
+        case int_type('-'):
+            state->complete = false;
+            if (state->exponent)
+            {
+                if (state->exponent_minus != 0)
+                {
+                    state->p_exception_string = "The exponent sign already set";
+                    return false;
+                }
+
+                state->exponent_minus = 2;
+            }
+            else
+            {
+                state->minus = 2;
+            }
+            break;
+        case int_type('.'):
+            state->complete = false;
+            if (state->after_comma > 0) return false;
+
+            state->after_comma = 1;
+            break;
+        case int_type('E'):
+        case int_type('e'):
+            state->complete = false;
+            if (state->exponent) return false;
+            state->exponent_number = 0;
+            state->exponent = true;
+            break;
+        default:
+            state->complete = true;
+            if (!state->exponent)
+            {
+                if (state->minus == 0) state->minus = 1;
+
+                state->result *= 10;
+                state->result += int64_t(ch - int_type('0'));
+
+                if (state->after_comma > 0) state->after_comma++;
+            }
+            else
+            {
+                if (state->exponent_minus == 0) state->exponent_minus = 1;
+                state->exponent_number *= 10;
+                state->exponent_number += int64_t(ch - int_type('0'));
+            }
+    }
+    return true;
+}
+
+template<typename FloatingPoint>
+static pplx::task<FloatingPoint> _extract_result(std::shared_ptr<_double_state<FloatingPoint>> state)
+{
+    if (state->p_exception_string.length() > 0) throw std::runtime_error(state->p_exception_string.c_str());
+
+    if (!state->complete && state->exponent) throw std::runtime_error("Incomplete exponent");
+
+    FloatingPoint result = static_cast<FloatingPoint>((state->minus == 2) ? -state->result : state->result);
+    if (state->exponent_minus == 2) state->exponent_number = 0 - state->exponent_number;
+
+    if (state->after_comma > 0) state->exponent_number -= state->after_comma - 1;
+
+    if (state->exponent_number >= 0)
+    {
+        result *= static_cast<FloatingPoint>(
+            std::pow(static_cast<FloatingPoint>(10.0), static_cast<FloatingPoint>(state->exponent_number)));
+
+#pragma push_macro("max")
+#undef max
+
+        if (result > std::numeric_limits<FloatingPoint>::max() || result < -std::numeric_limits<FloatingPoint>::max())
+            throw std::overflow_error("The value is too big");
+#pragma pop_macro("max")
+    }
+    else
+    {
+        bool is_zero = (result == 0);
+
+        result /= static_cast<FloatingPoint>(
+            std::pow(static_cast<FloatingPoint>(10.0), static_cast<FloatingPoint>(-state->exponent_number)));
+
+        if (!is_zero && result > -std::numeric_limits<FloatingPoint>::denorm_min() &&
+            result < std::numeric_limits<FloatingPoint>::denorm_min())
+            throw std::underflow_error("The value is too small");
+    }
+
+    return pplx::task_from_result<FloatingPoint>(result);
+}
+
+template<typename CharType>
+class type_parser<CharType, double> : public _type_parser_base<CharType>
+{
+    typedef _type_parser_base<CharType> base;
+
+public:
+    typedef typename base::traits traits;
+    typedef typename base::int_type int_type;
+
+    static pplx::task<double> parse(streams::streambuf<CharType> buffer)
+    {
+        return base::template _parse_input<_double_state<double>, double>(
+            buffer, _accept_char<double, int_type>, _extract_result<double>);
+    }
+
+protected:
+};
+
+template<typename CharType>
+class type_parser<CharType, float> : public _type_parser_base<CharType>
+{
+    typedef _type_parser_base<CharType> base;
+
+public:
+    typedef typename base::traits traits;
+    typedef typename base::int_type int_type;
+
+    static pplx::task<float> parse(streams::streambuf<CharType> buffer)
+    {
+        return base::template _parse_input<_double_state<float>, float>(
+            buffer, _accept_char<float, int_type>, _extract_result<float>);
+    }
+
+protected:
+};
+
+template<typename CharType>
+class type_parser<CharType, uint64_t> : public _type_parser_base<CharType>
+{
+    typedef _type_parser_base<CharType> base;
+
+public:
+    typedef typename base::traits traits;
+    typedef typename base::int_type int_type;
+
+    static pplx::task<uint64_t> parse(streams::streambuf<CharType> buffer)
+    {
+        return base::template _parse_input<_uint64_state, uint64_t>(buffer, _accept_char, _extract_result);
+    }
+
+private:
+    struct _uint64_state
+    {
+        _uint64_state() : result(0), correct(false) {}
+        uint64_t result;
+        bool correct;
+    };
+
+    static bool _accept_char(std::shared_ptr<_uint64_state> state, int_type ch)
+    {
+        if (!::isdigit(ch)) return false;
+
+        // At least one digit was found.
+        state->correct = true;
+
+        // Shift the existing value by 10, then add the new value.
+        state->result *= 10;
+        state->result += uint64_t(ch - int_type('0'));
+
+        return true;
+    }
+
+    static pplx::task<uint64_t> _extract_result(std::shared_ptr<_uint64_state> state)
+    {
+        if (!state->correct) throw std::range_error("integer value is too large to fit in 64 bits");
+        return pplx::task_from_result(state->result);
+    }
+};
+
+template<typename CharType>
+class type_parser<CharType, bool> : public _type_parser_base<CharType>
+{
+    typedef _type_parser_base<CharType> base;
+
+public:
+    typedef typename base::traits traits;
+    typedef typename base::int_type int_type;
+
+    static pplx::task<bool> parse(streams::streambuf<CharType> buffer)
+    {
+        return base::template _parse_input<_bool_state, bool>(buffer, _accept_char, _extract_result);
+    }
+
+private:
+    struct _bool_state
+    {
+        _bool_state() : state(0) {}
+        // { 0 -- not started, 1 -- 't', 2 -- 'tr', 3 -- 'tru', 4 -- 'f', 5 -- 'fa', 6 -- 'fal', 7 -- 'fals', 8 --
+        // 'true', 9 -- 'false' }
+        short state;
+    };
+
+    static bool _accept_char(std::shared_ptr<_bool_state> state, int_type ch)
+    {
+        switch (state->state)
+        {
+            case 0:
+                if (ch == int_type('t'))
+                    state->state = 1;
+                else if (ch == int_type('f'))
+                    state->state = 4;
+                else if (ch == int_type('1'))
+                    state->state = 8;
+                else if (ch == int_type('0'))
+                    state->state = 9;
+                else
+                    return false;
+                break;
+            case 1:
+                if (ch == int_type('r'))
+                    state->state = 2;
+                else
+                    return false;
+                break;
+            case 2:
+                if (ch == int_type('u'))
+                    state->state = 3;
+                else
+                    return false;
+                break;
+            case 3:
+                if (ch == int_type('e'))
+                    state->state = 8;
+                else
+                    return false;
+                break;
+            case 4:
+                if (ch == int_type('a'))
+                    state->state = 5;
+                else
+                    return false;
+                break;
+            case 5:
+                if (ch == int_type('l'))
+                    state->state = 6;
+                else
+                    return false;
+                break;
+            case 6:
+                if (ch == int_type('s'))
+                    state->state = 7;
+                else
+                    return false;
+                break;
+            case 7:
+                if (ch == int_type('e'))
+                    state->state = 9;
+                else
+                    return false;
+                break;
+            case 8:
+            case 9: return false;
+        }
+        return true;
+    }
+    static pplx::task<bool> _extract_result(std::shared_ptr<_bool_state> state)
+    {
+        bool correct = (state->state == 8 || state->state == 9);
+        if (!correct)
+        {
+            std::runtime_error exc("cannot parse as Boolean value");
+            throw exc;
+        }
+        return pplx::task_from_result(state->state == 8);
+    }
+};
+
+template<typename CharType>
+class type_parser<CharType, signed char> : public _type_parser_base<CharType>
+{
+    typedef _type_parser_base<CharType> base;
+
+public:
+    typedef typename base::traits traits;
+    typedef typename base::int_type int_type;
+
+    static pplx::task<signed char> parse(streams::streambuf<CharType> buffer)
+    {
+        return base::_skip_whitespace(buffer).then([=](pplx::task<void> op) -> pplx::task<signed char> {
+            op.wait();
+            return type_parser<CharType, signed char>::_get_char(buffer);
+        });
+    }
+
+private:
+    static pplx::task<signed char> _get_char(streams::streambuf<CharType> buffer)
+    {
+        concurrency::streams::streambuf<CharType> buf = buffer;
+        return buf.bumpc().then([=](pplx::task<int_type> op) -> signed char {
+            int_type val = op.get();
+            if (val == traits::eof()) throw std::runtime_error("reached end-of-stream while constructing a value");
+            return static_cast<signed char>(val);
+        });
+    }
+};
+
+template<typename CharType>
+class type_parser<CharType, unsigned char> : public _type_parser_base<CharType>
+{
+    typedef _type_parser_base<CharType> base;
+
+public:
+    typedef typename base::traits traits;
+    typedef typename base::int_type int_type;
+
+    static pplx::task<unsigned char> parse(streams::streambuf<CharType> buffer)
+    {
+        return base::_skip_whitespace(buffer).then([=](pplx::task<void> op) -> pplx::task<unsigned char> {
+            op.wait();
+            return type_parser<CharType, unsigned char>::_get_char(buffer);
+        });
+    }
+
+private:
+    static pplx::task<unsigned char> _get_char(streams::streambuf<CharType> buffer)
+    {
+        concurrency::streams::streambuf<CharType> buf = buffer;
+        return buf.bumpc().then([=](pplx::task<int_type> op) -> unsigned char {
+            int_type val = op.get();
+            if (val == traits::eof()) throw std::runtime_error("reached end-of-stream while constructing a value");
+            return static_cast<unsigned char>(val);
+        });
+    }
+};
+
+template<typename CharType>
+class type_parser<CharType, char> : public _type_parser_base<CharType>
+{
+    typedef _type_parser_base<CharType> base;
+
+public:
+    typedef typename base::traits traits;
+    typedef typename base::int_type int_type;
+
+    static pplx::task<char> parse(streams::streambuf<CharType> buffer)
+    {
+        return base::_skip_whitespace(buffer).then([=](pplx::task<void> op) -> pplx::task<char> {
+            op.wait();
+            return _get_char(buffer);
+        });
+    }
+
+private:
+    static pplx::task<char> _get_char(streams::streambuf<CharType> buffer)
+    {
+        concurrency::streams::streambuf<CharType> buf = buffer;
+        return buf.bumpc().then([=](pplx::task<int_type> op) -> char {
+            int_type val = op.get();
+            if (val == traits::eof()) throw std::runtime_error("reached end-of-stream while constructing a value");
+            return char(val);
+        });
+    }
+};
+
+#ifdef _WIN32
+template<class CharType>
+class type_parser<CharType, std::enable_if_t<sizeof(CharType) == 1, std::basic_string<wchar_t>>>
+    : public _type_parser_base<CharType>
+{
+    typedef _type_parser_base<CharType> base;
+
+public:
+    typedef typename base::traits traits;
+    typedef typename base::int_type int_type;
+
+    static pplx::task<std::wstring> parse(streams::streambuf<CharType> buffer)
+    {
+        return base::template _parse_input<std::basic_string<char>, std::basic_string<wchar_t>>(
+            buffer, _accept_char, _extract_result);
+    }
+
+private:
+    static bool _accept_char(const std::shared_ptr<std::basic_string<char>>& state, int_type ch)
+    {
+        if (ch == concurrency::streams::char_traits<char>::eof() || isspace(ch)) return false;
+        state->push_back(char(ch));
+        return true;
+    }
+    static pplx::task<std::basic_string<wchar_t>> _extract_result(std::shared_ptr<std::basic_string<char>> state)
+    {
+        return pplx::task_from_result(utility::conversions::utf8_to_utf16(*state));
+    }
+};
+#endif //_WIN32
+
+} // namespace streams
+} // namespace Concurrency
+
+#endif
diff --git a/Release/include/cpprest/uri.h b/Release/include/cpprest/uri.h
new file mode 100644 (file)
index 0000000..00f96e1
--- /dev/null
@@ -0,0 +1,21 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Protocol independent support for URIs.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_URI_H
+#define CASA_URI_H
+
+#include "cpprest/base_uri.h"
+#include "cpprest/uri_builder.h"
+
+#endif
diff --git a/Release/include/cpprest/uri_builder.h b/Release/include/cpprest/uri_builder.h
new file mode 100644 (file)
index 0000000..8cc2a92
--- /dev/null
@@ -0,0 +1,295 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Builder style class for creating URIs.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/base_uri.h"
+#include <string>
+
+namespace web
+{
+/// <summary>
+/// Builder for constructing URIs incrementally.
+/// </summary>
+class uri_builder
+{
+public:
+    /// <summary>
+    /// Creates a builder with an initially empty URI.
+    /// </summary>
+    uri_builder() = default;
+
+    /// <summary>
+    /// Creates a builder with a existing URI object.
+    /// </summary>
+    /// <param name="uri_str">Encoded string containing the URI.</param>
+    uri_builder(const uri& uri_str) : m_uri(uri_str.m_components) {}
+
+    /// <summary>
+    /// Get the scheme component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI scheme as a string.</returns>
+    const utility::string_t& scheme() const { return m_uri.m_scheme; }
+
+    /// <summary>
+    /// Get the user information component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI user information as a string.</returns>
+    const utility::string_t& user_info() const { return m_uri.m_user_info; }
+
+    /// <summary>
+    /// Get the host component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI host as a string.</returns>
+    const utility::string_t& host() const { return m_uri.m_host; }
+
+    /// <summary>
+    /// Get the port component of the URI. Returns -1 if no port is specified.
+    /// </summary>
+    /// <returns>The URI port as an integer.</returns>
+    int port() const { return m_uri.m_port; }
+
+    /// <summary>
+    /// Get the path component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI path as a string.</returns>
+    const utility::string_t& path() const { return m_uri.m_path; }
+
+    /// <summary>
+    /// Get the query component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI query as a string.</returns>
+    const utility::string_t& query() const { return m_uri.m_query; }
+
+    /// <summary>
+    /// Get the fragment component of the URI as an encoded string.
+    /// </summary>
+    /// <returns>The URI fragment as a string.</returns>
+    const utility::string_t& fragment() const { return m_uri.m_fragment; }
+
+    /// <summary>
+    /// Set the scheme of the URI.
+    /// </summary>
+    /// <param name="scheme">Uri scheme.</param>
+    /// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
+    uri_builder& set_scheme(const utility::string_t& scheme)
+    {
+        m_uri.m_scheme = scheme;
+        return *this;
+    }
+
+    /// <summary>
+    /// Set the user info component of the URI.
+    /// </summary>
+    /// <param name="user_info">User info as a decoded string.</param>
+    /// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
+    /// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
+    uri_builder& set_user_info(const utility::string_t& user_info, bool do_encoding = false)
+    {
+        if (do_encoding)
+        {
+            m_uri.m_user_info = uri::encode_uri(user_info, uri::components::user_info);
+        }
+        else
+        {
+            m_uri.m_user_info = user_info;
+        }
+
+        return *this;
+    }
+
+    /// <summary>
+    /// Set the host component of the URI.
+    /// </summary>
+    /// <param name="host">Host as a decoded string.</param>
+    /// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
+    /// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
+    uri_builder& set_host(const utility::string_t& host, bool do_encoding = false)
+    {
+        if (do_encoding)
+        {
+            m_uri.m_host = uri::encode_uri(host, uri::components::host);
+        }
+        else
+        {
+            m_uri.m_host = host;
+        }
+
+        return *this;
+    }
+
+    /// <summary>
+    /// Set the port component of the URI.
+    /// </summary>
+    /// <param name="port">Port as an integer.</param>
+    /// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
+    uri_builder& set_port(int port)
+    {
+        m_uri.m_port = port;
+        return *this;
+    }
+
+    /// <summary>
+    /// Set the port component of the URI.
+    /// </summary>
+    /// <param name="port">Port as a string.</param>
+    /// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
+    /// <remarks>When string can't be converted to an integer the port is left unchanged.</remarks>
+    _ASYNCRTIMP uri_builder& set_port(const utility::string_t& port);
+
+    /// <summary>
+    /// Set the path component of the URI.
+    /// </summary>
+    /// <param name="path">Path as a decoded string.</param>
+    /// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
+    /// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
+    uri_builder& set_path(const utility::string_t& path, bool do_encoding = false)
+    {
+        if (do_encoding)
+        {
+            m_uri.m_path = uri::encode_uri(path, uri::components::path);
+        }
+        else
+        {
+            m_uri.m_path = path;
+        }
+
+        return *this;
+    }
+
+    /// <summary>
+    /// Set the query component of the URI.
+    /// </summary>
+    /// <param name="query">Query as a decoded string.</param>
+    /// <param name="do_encoding">Specify whether apply URI encoding to the given string.</param>
+    /// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
+    uri_builder& set_query(const utility::string_t& query, bool do_encoding = false)
+    {
+        if (do_encoding)
+        {
+            m_uri.m_query = uri::encode_uri(query, uri::components::query);
+        }
+        else
+        {
+            m_uri.m_query = query;
+        }
+
+        return *this;
+    }
+
+    /// <summary>
+    /// Set the fragment component of the URI.
+    /// </summary>
+    /// <param name="fragment">Fragment as a decoded string.</param>
+    /// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
+    /// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
+    uri_builder& set_fragment(const utility::string_t& fragment, bool do_encoding = false)
+    {
+        if (do_encoding)
+        {
+            m_uri.m_fragment = uri::encode_uri(fragment, uri::components::fragment);
+        }
+        else
+        {
+            m_uri.m_fragment = fragment;
+        }
+
+        return *this;
+    }
+
+    /// <summary>
+    /// Clears all components of the underlying URI in this uri_builder.
+    /// </summary>
+    void clear() { m_uri = details::uri_components(); }
+
+    /// <summary>
+    /// Appends another path to the path of this uri_builder.
+    /// </summary>
+    /// <param name="path">Path to append as a already encoded string.</param>
+    /// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
+    /// <returns>A reference to this uri_builder to support chaining.</returns>
+    _ASYNCRTIMP uri_builder& append_path(const utility::string_t& path, bool do_encoding = false);
+
+    /// <summary>
+    /// Appends the raw contents of the path argument to the path of this uri_builder with no separator de-duplication.
+    /// </summary>
+    /// <remarks>
+    /// The path argument is appended after adding a '/' separator without regards to the contents of path. If an empty
+    /// string is provided, this function will immediately return without changes to the stored path value. For example:
+    /// if the current contents are "/abc" and path="/xyz", the result will be "/abc//xyz".
+    /// </remarks>
+    /// <param name="path">Path to append as a already encoded string.</param>
+    /// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
+    /// <returns>A reference to this uri_builder to support chaining.</returns>
+    _ASYNCRTIMP uri_builder& append_path_raw(const utility::string_t& path, bool do_encoding = false);
+
+    /// <summary>
+    /// Appends another query to the query of this uri_builder.
+    /// </summary>
+    /// <param name="query">Query to append as a decoded string.</param>
+    /// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
+    /// <returns>A reference to this uri_builder to support chaining.</returns>
+    _ASYNCRTIMP uri_builder& append_query(const utility::string_t& query, bool do_encoding = false);
+
+    /// <summary>
+    /// Appends an relative uri (Path, Query and fragment) at the end of the current uri.
+    /// </summary>
+    /// <param name="relative_uri">The relative uri to append.</param>
+    /// <returns>A reference to this uri_builder to support chaining.</returns>
+    _ASYNCRTIMP uri_builder& append(const uri& relative_uri);
+
+    /// <summary>
+    /// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building
+    /// a query segment of the form "element=10", where the right hand side of the query is stored as a type other than
+    /// a string, for instance, an integral type.
+    /// </summary>
+    /// <param name="name">The name portion of the query string</param>
+    /// <param name="value">The value portion of the query string</param>
+    /// <returns>A reference to this uri_builder to support chaining.</returns>
+    template<typename T>
+    uri_builder& append_query(const utility::string_t& name, const T& value, bool do_encoding = true)
+    {
+        if (do_encoding)
+            append_query_encode_impl(name, utility::conversions::details::print_utf8string(value));
+        else
+            append_query_no_encode_impl(name, utility::conversions::details::print_string(value));
+        return *this;
+    }
+
+    /// <summary>
+    /// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is
+    /// invalid.
+    /// </summary>
+    /// <returns>The created URI as a string.</returns>
+    _ASYNCRTIMP utility::string_t to_string() const;
+
+    /// <summary>
+    /// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is
+    /// invalid.
+    /// </summary>
+    /// <returns>The create URI as a URI class instance.</returns>
+    _ASYNCRTIMP uri to_uri() const;
+
+    /// <summary>
+    /// Validate the generated URI from all existing components of this uri_builder.
+    /// </summary>
+    /// <returns>Whether the URI is valid.</returns>
+    _ASYNCRTIMP bool is_valid();
+
+private:
+    _ASYNCRTIMP void append_query_encode_impl(const utility::string_t& name, const utf8string& value);
+    _ASYNCRTIMP void append_query_no_encode_impl(const utility::string_t& name, const utility::string_t& value);
+
+    details::uri_components m_uri;
+};
+} // namespace web
diff --git a/Release/include/cpprest/version.h b/Release/include/cpprest/version.h
new file mode 100644 (file)
index 0000000..d877158
--- /dev/null
@@ -0,0 +1,10 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ */
+#define CPPREST_VERSION_MINOR 10
+#define CPPREST_VERSION_MAJOR 2
+#define CPPREST_VERSION_REVISION 18
+
+#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
new file mode 100644 (file)
index 0000000..af17bd6
--- /dev/null
@@ -0,0 +1,610 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Websocket client side implementation
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#ifndef CASA_WS_CLIENT_H
+#define CASA_WS_CLIENT_H
+
+#include "cpprest/details/basic_types.h"
+
+#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/details/web_utilities.h"
+#include "cpprest/http_headers.h"
+#include "cpprest/uri.h"
+#include "cpprest/ws_msg.h"
+#include "pplx/pplxtasks.h"
+#include <condition_variable>
+#include <limits>
+#include <memory>
+#include <mutex>
+
+#if !defined(_WIN32) || !defined(__cplusplus_winrt)
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#endif
+#include "boost/asio/ssl.hpp"
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+#endif
+
+namespace web
+{
+// For backwards compatibility for when in the experimental namespace.
+// At next major release this should be deleted.
+namespace experimental = web;
+
+// In the past namespace was accidentally called 'web_sockets'. To avoid breaking code
+// alias it. At our next major release this should be deleted.
+namespace web_sockets = websockets;
+
+namespace websockets
+{
+/// WebSocket client side library.
+namespace client
+{
+/// Websocket close status values.
+enum class websocket_close_status
+{
+    normal = 1000,
+    going_away = 1001,
+    protocol_error = 1002,
+    unsupported = 1003, // or data_mismatch
+    abnormal_close = 1006,
+    inconsistent_datatype = 1007,
+    policy_violation = 1008,
+    too_large = 1009,
+    negotiate_error = 1010,
+    server_terminate = 1011,
+};
+
+/// <summary>
+/// Websocket client configuration class, used to set the possible configuration options
+/// used to create an websocket_client instance.
+/// </summary>
+class websocket_client_config
+{
+public:
+    /// <summary>
+    /// Creates a websocket client configuration with default settings.
+    /// </summary>
+    websocket_client_config() : m_sni_enabled(true), m_validate_certificates(true) {}
+
+    /// <summary>
+    /// Get the web proxy object
+    /// </summary>
+    /// <returns>A reference to the web proxy object.</returns>
+    const web_proxy& proxy() const { return m_proxy; }
+
+    /// <summary>
+    /// Set the web proxy object
+    /// </summary>
+    /// <param name="proxy">The web proxy object.</param>
+    void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
+
+    /// <summary>
+    /// Get the client credentials
+    /// </summary>
+    /// <returns>A reference to the client credentials.</returns>
+    const web::credentials& credentials() const { return m_credentials; }
+
+    /// <summary>
+    /// Set the client credentials
+    /// </summary>
+    /// <param name="cred">The client credentials.</param>
+    void set_credentials(const web::credentials& cred) { m_credentials = cred; }
+
+    /// <summary>
+    /// Disables Server Name Indication (SNI). Default is on.
+    /// </summary>
+    void disable_sni() { m_sni_enabled = false; }
+
+    /// <summary>
+    /// Determines if Server Name Indication (SNI) is enabled.
+    /// </summary>
+    /// <returns>True if enabled, false otherwise.</returns>
+    bool is_sni_enabled() const { return m_sni_enabled; }
+
+    /// <summary>
+    /// Sets the server host name to use for TLS Server Name Indication (SNI).
+    /// </summary>
+    /// <remarks>By default the host name is set to the websocket URI host.</remarks>
+    /// <param name="name">The host name to use, as a string.</param>
+    void set_server_name(const utf8string& name) { m_sni_hostname = name; }
+
+    /// <summary>
+    /// Gets the server host name to use for TLS Server Name Indication (SNI).
+    /// </summary>
+    /// <returns>Host name as a string.</returns>
+    const utf8string& server_name() const { return m_sni_hostname; }
+
+    /// <summary>
+    /// Sets the User Agent to be used for the connection
+    /// </summary>
+    /// <param name="name">The User Agent to use, as a string.</param>
+    _ASYNCRTIMP void set_user_agent(const utf8string& user_agent);
+
+    /// <summary>
+    /// Gets the headers of the HTTP request message used in the WebSocket protocol handshake.
+    /// </summary>
+    /// <returns>HTTP headers for the WebSocket protocol handshake.</returns>
+    /// <remarks>
+    /// Use the <seealso cref="http_headers::add Method"/> to fill in desired headers.
+    /// </remarks>
+    web::http::http_headers& headers() { return m_headers; }
+
+    /// <summary>
+    /// Gets a const reference to the headers of the WebSocket protocol handshake HTTP message.
+    /// </summary>
+    /// <returns>HTTP headers.</returns>
+    const web::http::http_headers& headers() const { return m_headers; }
+
+    /// <summary>
+    /// Adds a subprotocol to the request headers.
+    /// </summary>
+    /// <param name="name">The name of the subprotocol.</param>
+    /// <remarks>If additional subprotocols have already been specified, the new one will just be added.</remarks>
+    _ASYNCRTIMP void add_subprotocol(const ::utility::string_t& name);
+
+    /// <summary>
+    /// Gets list of the specified subprotocols.
+    /// </summary>
+    /// <returns>Vector of all the subprotocols </returns>
+    /// <remarks>If you want all the subprotocols in a comma separated string
+    /// they can be directly looked up in the headers using 'Sec-WebSocket-Protocol'.</remarks>
+    _ASYNCRTIMP std::vector<::utility::string_t> subprotocols() const;
+
+    /// <summary>
+    /// Gets the server certificate validation property.
+    /// </summary>
+    /// <returns>True if certificates are to be verified, false otherwise.</returns>
+    bool validate_certificates() const { return m_validate_certificates; }
+
+    /// <summary>
+    /// Sets the server certificate validation property.
+    /// </summary>
+    /// <param name="validate_certs">False to turn ignore all server certificate validation errors, true
+    /// otherwise.</param> <remarks>Note ignoring certificate errors can be dangerous and should be done with
+    /// caution.</remarks>
+    void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
+
+#if !defined(_WIN32) || !defined(__cplusplus_winrt)
+    /// <summary>
+    /// Sets a callback to enable custom setting of the ssl context, at construction time.
+    /// </summary>
+    /// <param name="callback">A user callback allowing for customization of the ssl context at construction
+    /// time.</param>
+    void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& callback)
+    {
+        m_ssl_context_callback = callback;
+    }
+
+    /// <summary>
+    /// Gets the user's callback to allow for customization of the ssl context.
+    /// </summary>
+    const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
+    {
+        return m_ssl_context_callback;
+    }
+#endif
+
+private:
+    web::web_proxy m_proxy;
+    web::credentials m_credentials;
+    web::http::http_headers m_headers;
+    bool m_sni_enabled;
+    utf8string m_sni_hostname;
+    bool m_validate_certificates;
+#if !defined(_WIN32) || !defined(__cplusplus_winrt)
+    std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
+#endif
+};
+
+/// <summary>
+/// Represents a websocket error. This class holds an error message and an optional error code.
+/// </summary>
+class websocket_exception : public std::exception
+{
+public:
+    /// <summary>
+    /// Creates an <c>websocket_exception</c> with just a string message and no error code.
+    /// </summary>
+    /// <param name="whatArg">Error message string.</param>
+    websocket_exception(const utility::string_t& whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {}
+
+#ifdef _WIN32
+    /// <summary>
+    /// Creates an <c>websocket_exception</c> with just a string message and no error code.
+    /// </summary>
+    /// <param name="whatArg">Error message string.</param>
+    websocket_exception(std::string whatArg) : m_msg(std::move(whatArg)) {}
+#endif
+
+    /// <summary>
+    /// Creates a <c>websocket_exception</c> from a error code using the current platform error category.
+    /// The message of the error code will be used as the what() string message.
+    /// </summary>
+    /// <param name="errorCode">Error code value.</param>
+    websocket_exception(int errorCode) : m_errorCode(utility::details::create_error_code(errorCode))
+    {
+        m_msg = m_errorCode.message();
+    }
+
+    /// <summary>
+    /// Creates a <c>websocket_exception</c> from a error code using the current platform error category.
+    /// </summary>
+    /// <param name="errorCode">Error code value.</param>
+    /// <param name="whatArg">Message to use in what() string.</param>
+    websocket_exception(int errorCode, const utility::string_t& whatArg)
+        : m_errorCode(utility::details::create_error_code(errorCode))
+        , m_msg(utility::conversions::to_utf8string(whatArg))
+    {
+    }
+
+#ifdef _WIN32
+    /// <summary>
+    /// Creates a <c>websocket_exception</c> from a error code and string message.
+    /// </summary>
+    /// <param name="errorCode">Error code value.</param>
+    /// <param name="whatArg">Message to use in what() string.</param>
+    websocket_exception(int errorCode, std::string whatArg)
+        : m_errorCode(utility::details::create_error_code(errorCode)), m_msg(std::move(whatArg))
+    {
+    }
+
+    /// <summary>
+    /// Creates a <c>websocket_exception</c> from a error code and string message to use as the what() argument.
+    /// <param name="code">Error code.</param>
+    /// <param name="whatArg">Message to use in what() string.</param>
+    /// </summary>
+    websocket_exception(std::error_code code, std::string whatArg)
+        : m_errorCode(std::move(code)), m_msg(std::move(whatArg))
+    {
+    }
+#endif
+
+    /// <summary>
+    /// Creates a <c>websocket_exception</c> from a error code and category. The message of the error code will be used
+    /// as the <c>what</c> string message.
+    /// </summary>
+    /// <param name="errorCode">Error code value.</param>
+    /// <param name="cat">Error category for the code.</param>
+    websocket_exception(int errorCode, const std::error_category& cat) : m_errorCode(std::error_code(errorCode, cat))
+    {
+        m_msg = m_errorCode.message();
+    }
+
+    /// <summary>
+    /// Creates a <c>websocket_exception</c> from a error code and string message to use as the what() argument.
+    /// <param name="code">Error code.</param>
+    /// <param name="whatArg">Message to use in what() string.</param>
+    /// </summary>
+    websocket_exception(std::error_code code, const utility::string_t& whatArg)
+        : m_errorCode(std::move(code)), m_msg(utility::conversions::to_utf8string(whatArg))
+    {
+    }
+
+    /// <summary>
+    /// Gets a string identifying the cause of the exception.
+    /// </summary>
+    /// <returns>A null terminated character string.</returns>
+    const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
+
+    /// <summary>
+    /// Gets the underlying error code for the cause of the exception.
+    /// </summary>
+    /// <returns>The <c>error_code</c> object associated with the exception.</returns>
+    const std::error_code& error_code() const CPPREST_NOEXCEPT { return m_errorCode; }
+
+private:
+    std::error_code m_errorCode;
+    std::string m_msg;
+};
+
+namespace details
+{
+// Interface to be implemented by the websocket client callback implementations.
+class websocket_client_callback_impl
+{
+public:
+    websocket_client_callback_impl(websocket_client_config config) : m_config(std::move(config)) {}
+
+    virtual ~websocket_client_callback_impl() CPPREST_NOEXCEPT {}
+
+    virtual pplx::task<void> connect() = 0;
+
+    virtual pplx::task<void> send(websocket_outgoing_message& msg) = 0;
+
+    virtual void set_message_handler(const std::function<void(const websocket_incoming_message&)>& handler) = 0;
+
+    virtual pplx::task<void> close() = 0;
+
+    virtual pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = {}) = 0;
+
+    virtual void set_close_handler(
+        const std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)>&
+            handler) = 0;
+
+    const web::uri& uri() const { return m_uri; }
+
+    void set_uri(const web::uri& uri) { m_uri = uri; }
+
+    const websocket_client_config& config() const { return m_config; }
+
+    static void verify_uri(const web::uri& uri)
+    {
+        // Most of the URI schema validation is taken care by URI class.
+        // We only need to check certain things specific to websockets.
+        if (uri.scheme() != _XPLATSTR("ws") && uri.scheme() != _XPLATSTR("wss"))
+        {
+            throw std::invalid_argument("URI scheme must be 'ws' or 'wss'");
+        }
+
+        if (uri.host().empty())
+        {
+            throw std::invalid_argument("URI must contain a hostname.");
+        }
+
+        // Fragment identifiers are meaningless in the context of WebSocket URIs
+        // and MUST NOT be used on these URIs.
+        if (!uri.fragment().empty())
+        {
+            throw std::invalid_argument("WebSocket URI must not contain fragment identifiers");
+        }
+    }
+
+protected:
+    web::uri m_uri;
+    websocket_client_config m_config;
+};
+
+// Interface to be implemented by the websocket client task implementations.
+class websocket_client_task_impl
+{
+public:
+    _ASYNCRTIMP websocket_client_task_impl(websocket_client_config config);
+
+    _ASYNCRTIMP virtual ~websocket_client_task_impl() CPPREST_NOEXCEPT;
+
+    _ASYNCRTIMP pplx::task<websocket_incoming_message> receive();
+
+    _ASYNCRTIMP void close_pending_tasks_with_error(const websocket_exception& exc);
+
+    const std::shared_ptr<websocket_client_callback_impl>& callback_client() const { return m_callback_client; };
+
+private:
+    void set_handler();
+
+    // When a message arrives, if there are tasks waiting for a message, signal the topmost one.
+    // Else enqueue the message in a queue.
+    // m_receive_queue_lock : to guard access to the queue & m_client_closed
+    std::mutex m_receive_queue_lock;
+    // Queue to store incoming messages when there are no tasks waiting for a message
+    std::queue<websocket_incoming_message> m_receive_msg_queue;
+    // Queue to maintain the receive tasks when there are no messages(yet).
+    std::queue<pplx::task_completion_event<websocket_incoming_message>> m_receive_task_queue;
+
+    // Initially set to false, becomes true if a close frame is received from the server or
+    // if the underlying connection is aborted or terminated.
+    bool m_client_closed;
+
+    std::shared_ptr<websocket_client_callback_impl> m_callback_client;
+};
+} // namespace details
+
+/// <summary>
+/// Websocket client class, used to maintain a connection to a remote host for an extended session.
+/// </summary>
+class websocket_client
+{
+public:
+    /// <summary>
+    ///  Creates a new websocket_client.
+    /// </summary>
+    websocket_client() : m_client(std::make_shared<details::websocket_client_task_impl>(websocket_client_config())) {}
+
+    /// <summary>
+    ///  Creates a new websocket_client.
+    /// </summary>
+    /// <param name="config">The client configuration object containing the possible configuration options to initialize
+    /// the <c>websocket_client</c>. </param>
+    websocket_client(websocket_client_config config)
+        : m_client(std::make_shared<details::websocket_client_task_impl>(std::move(config)))
+    {
+    }
+
+    /// <summary>
+    /// Connects to the remote network destination. The connect method initiates the websocket handshake with the
+    /// remote network destination, takes care of the protocol upgrade request.
+    /// </summary>
+    /// <param name="uri">The uri address to connect. </param>
+    /// <returns>An asynchronous operation that is completed once the client has successfully connected to the websocket
+    /// server.</returns>
+    pplx::task<void> connect(const web::uri& uri)
+    {
+        m_client->callback_client()->verify_uri(uri);
+        m_client->callback_client()->set_uri(uri);
+        auto client = m_client;
+        return m_client->callback_client()->connect().then([client](pplx::task<void> result) {
+            try
+            {
+                result.get();
+            }
+            catch (const websocket_exception& ex)
+            {
+                client->close_pending_tasks_with_error(ex);
+                throw;
+            }
+        });
+    }
+
+    /// <summary>
+    /// Sends a websocket message to the server .
+    /// </summary>
+    /// <returns>An asynchronous operation that is completed once the message is sent.</returns>
+    pplx::task<void> send(websocket_outgoing_message msg) { return m_client->callback_client()->send(msg); }
+
+    /// <summary>
+    /// Receive a websocket message.
+    /// </summary>
+    /// <returns>An asynchronous operation that is completed when a message has been received by the client
+    /// endpoint.</returns>
+    pplx::task<websocket_incoming_message> receive() { return m_client->receive(); }
+
+    /// <summary>
+    /// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
+    /// server.
+    /// </summary>
+    /// <returns>An asynchronous operation that is completed the connection has been successfully closed.</returns>
+    pplx::task<void> close() { return m_client->callback_client()->close(); }
+
+    /// <summary>
+    /// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
+    /// server.
+    /// </summary>
+    /// <param name="close_status">Endpoint MAY use the following pre-defined status codes when sending a Close
+    /// frame.</param> <param name="close_reason">While closing an established connection, an endpoint may indicate the
+    /// reason for closure.</param> <returns>An asynchronous operation that is completed the connection has been
+    /// successfully closed.</returns>
+    pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = {})
+    {
+        return m_client->callback_client()->close(close_status, close_reason);
+    }
+
+    /// <summary>
+    /// Gets the websocket client URI.
+    /// </summary>
+    /// <returns>URI connected to.</returns>
+    const web::uri& uri() const { return m_client->callback_client()->uri(); }
+
+    /// <summary>
+    /// Gets the websocket client config object.
+    /// </summary>
+    /// <returns>A reference to the client configuration object.</returns>
+    const websocket_client_config& config() const { return m_client->callback_client()->config(); }
+
+private:
+    std::shared_ptr<details::websocket_client_task_impl> m_client;
+};
+
+/// <summary>
+/// Websocket client class, used to maintain a connection to a remote host for an extended session, uses callback APIs
+/// for handling receive and close event instead of async task. For some scenarios would be a alternative for the
+/// websocket_client like if you want to special handling on close event.
+/// </summary>
+class websocket_callback_client
+{
+public:
+    /// <summary>
+    ///  Creates a new websocket_callback_client.
+    /// </summary>
+    _ASYNCRTIMP websocket_callback_client();
+
+    /// <summary>
+    ///  Creates a new websocket_callback_client.
+    /// </summary>
+    /// <param name="client_config">The client configuration object containing the possible configuration options to
+    /// initialize the <c>websocket_client</c>. </param>
+    _ASYNCRTIMP websocket_callback_client(websocket_client_config client_config);
+
+    /// <summary>
+    /// Connects to the remote network destination. The connect method initiates the websocket handshake with the
+    /// remote network destination, takes care of the protocol upgrade request.
+    /// </summary>
+    /// <param name="uri">The uri address to connect. </param>
+    /// <returns>An asynchronous operation that is completed once the client has successfully connected to the websocket
+    /// server.</returns>
+    pplx::task<void> connect(const web::uri& uri)
+    {
+        m_client->verify_uri(uri);
+        m_client->set_uri(uri);
+        return m_client->connect();
+    }
+
+    /// <summary>
+    /// Sends a websocket message to the server .
+    /// </summary>
+    /// <returns>An asynchronous operation that is completed once the message is sent.</returns>
+    pplx::task<void> send(websocket_outgoing_message msg) { return m_client->send(msg); }
+
+    /// <summary>
+    /// Set the received handler for notification of client websocket messages.
+    /// </summary>
+    /// <param name="handler">A function representing the incoming websocket messages handler. It's parameters are:
+    ///    msg:  a <c>websocket_incoming_message</c> value indicating the message received
+    /// </param>
+    /// <remarks>If this handler is not set before connecting incoming messages will be missed.</remarks>
+    void set_message_handler(const std::function<void(const websocket_incoming_message& msg)>& handler)
+    {
+        m_client->set_message_handler(handler);
+    }
+
+    /// <summary>
+    /// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
+    /// server.
+    /// </summary>
+    /// <returns>An asynchronous operation that is completed the connection has been successfully closed.</returns>
+    pplx::task<void> close() { return m_client->close(); }
+
+    /// <summary>
+    /// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
+    /// server.
+    /// </summary>
+    /// <param name="close_status">Endpoint MAY use the following pre-defined status codes when sending a Close
+    /// frame.</param> <param name="close_reason">While closing an established connection, an endpoint may indicate the
+    /// reason for closure.</param> <returns>An asynchronous operation that is completed the connection has been
+    /// successfully closed.</returns>
+    pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = {})
+    {
+        return m_client->close(close_status, close_reason);
+    }
+
+    /// <summary>
+    /// Set the closed handler for notification of client websocket closing event.
+    /// </summary>
+    /// <param name="handler">The handler for websocket closing event, It's parameters are:
+    ///   close_status: The pre-defined status codes used by the endpoint when sending a Close frame.
+    ///   reason: The reason string used by the endpoint when sending a Close frame.
+    ///   error: The error code if the websocket is closed with abnormal error.
+    /// </param>
+    void set_close_handler(const std::function<void(websocket_close_status close_status,
+                                                    const utility::string_t& reason,
+                                                    const std::error_code& error)>& handler)
+    {
+        m_client->set_close_handler(handler);
+    }
+
+    /// <summary>
+    /// Gets the websocket client URI.
+    /// </summary>
+    /// <returns>URI connected to.</returns>
+    const web::uri& uri() const { return m_client->uri(); }
+
+    /// <summary>
+    /// Gets the websocket client config object.
+    /// </summary>
+    /// <returns>A reference to the client configuration object.</returns>
+    const websocket_client_config& config() const { return m_client->config(); }
+
+private:
+    std::shared_ptr<details::websocket_client_callback_impl> m_client;
+};
+
+} // namespace client
+} // namespace websockets
+} // namespace web
+
+#endif
+
+#endif
diff --git a/Release/include/cpprest/ws_msg.h b/Release/include/cpprest/ws_msg.h
new file mode 100644 (file)
index 0000000..9b13a80
--- /dev/null
@@ -0,0 +1,249 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Websocket incoming and outgoing message definitions.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "cpprest/details/basic_types.h"
+
+#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/containerstream.h"
+#include "cpprest/streams.h"
+#include "cpprest/uri.h"
+#include "pplx/pplxtasks.h"
+#include <limits>
+#include <memory>
+
+namespace web
+{
+namespace websockets
+{
+namespace client
+{
+namespace details
+{
+class winrt_callback_client;
+class wspp_callback_client;
+#if defined(__cplusplus_winrt)
+ref class ReceiveContext;
+#endif
+} // namespace details
+
+/// <summary>
+/// The different types of websocket message.
+/// Text type contains UTF-8 encoded data.
+/// Interpretation of Binary type is left to the application.
+/// Note: The fragment types and control frames like close, ping, pong are not supported on WinRT.
+/// </summary>
+enum class websocket_message_type
+{
+    text_message,
+    binary_message,
+    close,
+    ping,
+    pong
+};
+
+/// <summary>
+/// Represents an outgoing websocket message
+/// </summary>
+class websocket_outgoing_message
+{
+public:
+#if !defined(__cplusplus_winrt)
+    /// <summary>
+    /// Sets the outgoing message to be a ping message.
+    /// This is useful when the client side wants to check whether the server is alive.
+    /// </summary>
+    /// <param name="data">UTF-8 String containing the optional ping message.</param>
+    void set_ping_message(const std::string& data = {})
+    {
+        this->set_message_ping(concurrency::streams::container_buffer<std::string>(data));
+    }
+
+    /// <summary>
+    /// Sets the outgoing message to be an unsolicited pong message.
+    /// </summary>
+    /// <param name="data">UTF-8 String containing the optional pong message.</param>
+    void set_pong_message(const std::string& data = {})
+    {
+        this->set_message_pong(concurrency::streams::container_buffer<std::string>(data));
+    }
+#endif
+
+    /// <summary>
+    /// Sets a UTF-8 message as the message body.
+    /// </summary>
+    /// <param name="data">UTF-8 String containing body of the message.</param>
+    void set_utf8_message(std::string&& data)
+    {
+        this->set_message(concurrency::streams::container_buffer<std::string>(std::move(data)));
+    }
+
+    /// <summary>
+    /// Sets a UTF-8 message as the message body.
+    /// </summary>
+    /// <param name="data">UTF-8 String containing body of the message.</param>
+    void set_utf8_message(const std::string& data)
+    {
+        this->set_message(concurrency::streams::container_buffer<std::string>(data));
+    }
+
+    /// <summary>
+    /// Sets a UTF-8 message as the message body.
+    /// </summary>
+    /// <param name="istream">casablanca input stream representing the body of the message.</param>
+    /// <remarks>Upon sending, the entire stream may be buffered to determine the length.</remarks>
+    void set_utf8_message(const concurrency::streams::istream& istream)
+    {
+        this->set_message(istream, SIZE_MAX, websocket_message_type::text_message);
+    }
+
+    /// <summary>
+    /// Sets a UTF-8 message as the message body.
+    /// </summary>
+    /// <param name="istream">casablanca input stream representing the body of the message.</param>
+    /// <param name="len">number of bytes to send.</param>
+    void set_utf8_message(const concurrency::streams::istream& istream, size_t len)
+    {
+        this->set_message(istream, len, websocket_message_type::text_message);
+    }
+
+    /// <summary>
+    /// Sets binary data as the message body.
+    /// </summary>
+    /// <param name="istream">casablanca input stream representing the body of the message.</param>
+    /// <param name="len">number of bytes to send.</param>
+    void set_binary_message(const concurrency::streams::istream& istream, size_t len)
+    {
+        this->set_message(istream, len, websocket_message_type::binary_message);
+    }
+
+    /// <summary>
+    /// Sets binary data as the message body.
+    /// </summary>
+    /// <param name="istream">Input stream representing the body of the message.</param>
+    /// <remarks>Upon sending, the entire stream may be buffered to determine the length.</remarks>
+    void set_binary_message(const concurrency::streams::istream& istream)
+    {
+        this->set_message(istream, SIZE_MAX, websocket_message_type::binary_message);
+    }
+
+private:
+    friend class details::winrt_callback_client;
+    friend class details::wspp_callback_client;
+
+    pplx::task_completion_event<void> m_body_sent;
+    concurrency::streams::streambuf<uint8_t> m_body;
+    websocket_message_type m_msg_type;
+    size_t m_length;
+
+    void signal_body_sent() const { m_body_sent.set(); }
+
+    void signal_body_sent(const std::exception_ptr& e) const { m_body_sent.set_exception(e); }
+
+    const pplx::task_completion_event<void>& body_sent() const { return m_body_sent; }
+
+#if !defined(__cplusplus_winrt)
+    void set_message_ping(const concurrency::streams::container_buffer<std::string>& buffer)
+    {
+        m_msg_type = websocket_message_type::ping;
+        m_length = static_cast<size_t>(buffer.size());
+        m_body = buffer;
+    }
+    void set_message_pong(const concurrency::streams::container_buffer<std::string>& buffer)
+    {
+        m_msg_type = websocket_message_type::pong;
+        m_length = static_cast<size_t>(buffer.size());
+        m_body = buffer;
+    }
+#endif
+
+    void set_message(const concurrency::streams::container_buffer<std::string>& buffer)
+    {
+        m_msg_type = websocket_message_type::text_message;
+        m_length = static_cast<size_t>(buffer.size());
+        m_body = buffer;
+    }
+
+    void set_message(const concurrency::streams::istream& istream, size_t len, websocket_message_type msg_type)
+    {
+        m_msg_type = msg_type;
+        m_length = len;
+        m_body = istream.streambuf();
+    }
+};
+
+/// <summary>
+/// Represents an incoming websocket message
+/// </summary>
+class websocket_incoming_message
+{
+public:
+    /// <summary>
+    /// Extracts the body of the incoming message as a string value, only if the message type is UTF-8.
+    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
+    /// </summary>
+    /// <returns>String containing body of the message.</returns>
+    _ASYNCRTIMP pplx::task<std::string> extract_string() const;
+
+    /// <summary>
+    /// Produces a stream which the caller may use to retrieve body from an incoming message.
+    /// Can be used for both UTF-8 (text) and binary message types.
+    /// </summary>
+    /// <returns>A readable, open asynchronous stream.</returns>
+    /// <remarks>
+    /// This cannot be used in conjunction with any other means of getting the body of the message.
+    /// </remarks>
+    concurrency::streams::istream body() const
+    {
+        auto to_uint8_t_stream =
+            [](const concurrency::streams::streambuf<uint8_t>& buf) -> concurrency::streams::istream {
+            return buf.create_istream();
+        };
+        return to_uint8_t_stream(m_body);
+    }
+
+    /// <summary>
+    /// Returns the length of the received message.
+    /// </summary>
+    size_t length() const { return static_cast<size_t>(m_body.size()); }
+
+    /// <summary>
+    /// Returns the type of the received message.
+    /// </summary>
+    CASABLANCA_DEPRECATED("Incorrectly spelled API, use message_type() instead.")
+    websocket_message_type messge_type() const { return m_msg_type; }
+
+    /// <summary>
+    /// Returns the type of the received message, either string or binary.
+    /// </summary>
+    /// <returns>websocket_message_type</returns>
+    websocket_message_type message_type() const { return m_msg_type; }
+
+private:
+    friend class details::winrt_callback_client;
+    friend class details::wspp_callback_client;
+#if defined(__cplusplus_winrt)
+    friend ref class details::ReceiveContext;
+#endif
+
+    // Store message body in a container buffer backed by a string.
+    // Allows for optimization in the string message cases.
+    concurrency::streams::container_buffer<std::string> m_body;
+    websocket_message_type m_msg_type;
+};
+
+} // namespace client
+} // namespace websockets
+} // namespace web
+
+#endif
diff --git a/Release/include/pplx/pplx.h b/Release/include/pplx/pplx.h
new file mode 100644 (file)
index 0000000..d9ba9c6
--- /dev/null
@@ -0,0 +1,211 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Parallel Patterns Library
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#ifndef _PPLX_H
+#define _PPLX_H
+
+#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
+#error This file must not be included for Visual Studio 12 or later
+#endif
+
+#ifndef _WIN32
+#if defined(_WIN32) || defined(__cplusplus_winrt)
+#define _WIN32
+#endif
+#endif // _WIN32
+
+#ifdef _NO_PPLXIMP
+#define _PPLXIMP
+#else
+#ifdef _PPLX_EXPORT
+#define _PPLXIMP __declspec(dllexport)
+#else
+#define _PPLXIMP __declspec(dllimport)
+#endif
+#endif
+
+#include "cpprest/details/cpprest_compat.h"
+
+// Use PPLx
+#ifdef _WIN32
+#include "pplx/pplxwin.h"
+#elif defined(__APPLE__)
+#undef _PPLXIMP
+#define _PPLXIMP
+#include "pplx/pplxlinux.h"
+#else
+#include "pplx/pplxlinux.h"
+#endif // _WIN32
+
+// Common implementation across all the non-concrt versions
+#include "pplx/pplxcancellation_token.h"
+#include <functional>
+
+// conditional expression is constant
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4127)
+#endif
+
+#pragma pack(push, _CRT_PACKING)
+
+/// <summary>
+///     The <c>pplx</c> namespace provides classes and functions that give you access to the Concurrency Runtime,
+///     a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>.
+/// </summary>
+/**/
+namespace pplx
+{
+/// <summary>
+/// Sets the ambient scheduler to be used by the PPL constructs.
+/// </summary>
+_PPLXIMP void _pplx_cdecl set_ambient_scheduler(std::shared_ptr<pplx::scheduler_interface> _Scheduler);
+
+/// <summary>
+/// Gets the ambient scheduler to be used by the PPL constructs
+/// </summary>
+_PPLXIMP std::shared_ptr<pplx::scheduler_interface> _pplx_cdecl get_ambient_scheduler();
+
+namespace details
+{
+//
+// An internal exception that is used for cancellation. Users do not "see" this exception except through the
+// resulting stack unwind. This exception should never be intercepted by user code. It is intended
+// for use by the runtime only.
+//
+class _Interruption_exception : public std::exception
+{
+public:
+    _Interruption_exception() {}
+};
+
+template<typename _T>
+struct _AutoDeleter
+{
+    _AutoDeleter(_T* _PPtr) : _Ptr(_PPtr) {}
+    ~_AutoDeleter() { delete _Ptr; }
+    _T* _Ptr;
+};
+
+struct _TaskProcHandle
+{
+    _TaskProcHandle() {}
+
+    virtual ~_TaskProcHandle() {}
+    virtual void invoke() const = 0;
+
+    static void _pplx_cdecl _RunChoreBridge(void* _Parameter)
+    {
+        auto _PTaskHandle = static_cast<_TaskProcHandle*>(_Parameter);
+        _AutoDeleter<_TaskProcHandle> _AutoDeleter(_PTaskHandle);
+        _PTaskHandle->invoke();
+    }
+};
+
+enum _TaskInliningMode
+{
+    // Disable inline scheduling
+    _NoInline = 0,
+    // Let runtime decide whether to do inline scheduling or not
+    _DefaultAutoInline = 16,
+    // Always do inline scheduling
+    _ForceInline = -1,
+};
+
+// This is an abstraction that is built on top of the scheduler to provide these additional functionalities
+// - Ability to wait on a work item
+// - Ability to cancel a work item
+// - Ability to inline work on invocation of RunAndWait
+class _TaskCollectionImpl
+{
+public:
+    typedef _TaskProcHandle _TaskProcHandle_t;
+
+    _TaskCollectionImpl(scheduler_ptr _PScheduler) : _M_pScheduler(_PScheduler) {}
+
+    void _ScheduleTask(_TaskProcHandle_t* _PTaskHandle, _TaskInliningMode _InliningMode)
+    {
+        if (_InliningMode == _ForceInline)
+        {
+            _TaskProcHandle_t::_RunChoreBridge(_PTaskHandle);
+        }
+        else
+        {
+            _M_pScheduler->schedule(_TaskProcHandle_t::_RunChoreBridge, _PTaskHandle);
+        }
+    }
+
+    void _Cancel()
+    {
+        // No cancellation support
+    }
+
+    void _RunAndWait()
+    {
+        // No inlining support yet
+        _Wait();
+    }
+
+    void _Wait() { _M_Completed.wait(); }
+
+    void _Complete() { _M_Completed.set(); }
+
+    scheduler_ptr _GetScheduler() const { return _M_pScheduler; }
+
+    // Fire and forget
+    static void _RunTask(TaskProc_t _Proc, void* _Parameter, _TaskInliningMode _InliningMode)
+    {
+        if (_InliningMode == _ForceInline)
+        {
+            _Proc(_Parameter);
+        }
+        else
+        {
+            // Schedule the work on the ambient scheduler
+            get_ambient_scheduler()->schedule(_Proc, _Parameter);
+        }
+    }
+
+    static bool _pplx_cdecl _Is_cancellation_requested()
+    {
+        // We do not yet have the ability to determine the current task. So return false always
+        return false;
+    }
+
+private:
+    extensibility::event_t _M_Completed;
+    scheduler_ptr _M_pScheduler;
+};
+
+// For create_async lambdas that return a (non-task) result, we oversubscriber the current task for the duration of the
+// lambda.
+struct _Task_generator_oversubscriber
+{
+};
+
+typedef _TaskCollectionImpl _TaskCollection_t;
+typedef _TaskInliningMode _TaskInliningMode_t;
+typedef _Task_generator_oversubscriber _Task_generator_oversubscriber_t;
+
+} // namespace details
+
+} // namespace pplx
+
+#pragma pack(pop)
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#endif // _PPLX_H
diff --git a/Release/include/pplx/pplxcancellation_token.h b/Release/include/pplx/pplxcancellation_token.h
new file mode 100644 (file)
index 0000000..b7ad1a0
--- /dev/null
@@ -0,0 +1,920 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Parallel Patterns Library : cancellation_token
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#ifndef _PPLX_H
+#error This header must not be included directly
+#endif
+
+#ifndef _PPLXCANCELLATION_TOKEN_H
+#define _PPLXCANCELLATION_TOKEN_H
+
+#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
+#error This file must not be included for Visual Studio 12 or later
+#endif
+
+#include "pplx/pplxinterface.h"
+#include <cstdlib>
+#include <string>
+
+#pragma pack(push, _CRT_PACKING)
+// All header files are required to be protected from the macro new
+#pragma push_macro("new")
+#undef new
+
+namespace pplx
+{
+/// <summary>
+///     This class describes an exception thrown by the PPL tasks layer in order to force the current task
+///     to cancel. It is also thrown by the <c>get()</c> method on <see cref="task Class">task</see>, for a
+///     canceled task.
+/// </summary>
+/// <seealso cref="task::get Method"/>
+/// <seealso cref="cancel_current_task Method"/>
+/**/
+class task_canceled : public std::exception
+{
+private:
+    std::string _message;
+
+public:
+    /// <summary>
+    ///     Constructs a <c>task_canceled</c> object.
+    /// </summary>
+    /// <param name="_Message">
+    ///     A descriptive message of the error.
+    /// </param>
+    /**/
+    explicit task_canceled(_In_z_ const char* _Message) throw() : _message(_Message) {}
+
+    /// <summary>
+    ///     Constructs a <c>task_canceled</c> object.
+    /// </summary>
+    /**/
+    task_canceled() throw() : exception() {}
+
+    ~task_canceled() throw() {}
+
+    const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); }
+};
+
+/// <summary>
+///     This class describes an exception thrown when an invalid operation is performed that is not more accurately
+///     described by another exception type thrown by the Concurrency Runtime.
+/// </summary>
+/// <remarks>
+///     The various methods which throw this exception will generally document under what circumstances they will throw
+///     it.
+/// </remarks>
+/**/
+class invalid_operation : public std::exception
+{
+private:
+    std::string _message;
+
+public:
+    /// <summary>
+    ///     Constructs an <c>invalid_operation</c> object.
+    /// </summary>
+    /// <param name="_Message">
+    ///     A descriptive message of the error.
+    /// </param>
+    /**/
+    invalid_operation(_In_z_ const char* _Message) throw() : _message(_Message) {}
+
+    /// <summary>
+    ///     Constructs an <c>invalid_operation</c> object.
+    /// </summary>
+    /**/
+    invalid_operation() throw() : exception() {}
+
+    ~invalid_operation() throw() {}
+
+    const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); }
+};
+
+namespace details
+{
+// Base class for all reference counted objects
+class _RefCounter
+{
+public:
+    virtual ~_RefCounter() { _ASSERTE(_M_refCount == 0); }
+
+    // Acquires a reference
+    // Returns the new reference count.
+    long _Reference()
+    {
+        long _Refcount = atomic_increment(_M_refCount);
+
+        // 0 - 1 transition is illegal
+        _ASSERTE(_Refcount > 1);
+        return _Refcount;
+    }
+
+    // Releases the reference
+    // Returns the new reference count
+    long _Release()
+    {
+        long _Refcount = atomic_decrement(_M_refCount);
+        _ASSERTE(_Refcount >= 0);
+
+        if (_Refcount == 0)
+        {
+            _Destroy();
+        }
+
+        return _Refcount;
+    }
+
+protected:
+    // Allow derived classes to provide their own deleter
+    virtual void _Destroy() { delete this; }
+
+    // Only allow instantiation through derived class
+    _RefCounter(long _InitialCount = 1) : _M_refCount(_InitialCount) { _ASSERTE(_M_refCount > 0); }
+
+    // Reference count
+    atomic_long _M_refCount;
+};
+
+class _CancellationTokenState;
+
+class _CancellationTokenRegistration : public _RefCounter
+{
+private:
+    static const long _STATE_CLEAR = 0;
+    static const long _STATE_DEFER_DELETE = 1;
+    static const long _STATE_SYNCHRONIZE = 2;
+    static const long _STATE_CALLED = 3;
+
+public:
+    _CancellationTokenRegistration(long _InitialRefs = 1)
+        : _RefCounter(_InitialRefs), _M_state(_STATE_CALLED), _M_pTokenState(NULL)
+    {
+    }
+
+    _CancellationTokenState* _GetToken() const { return _M_pTokenState; }
+
+protected:
+    virtual ~_CancellationTokenRegistration() { _ASSERTE(_M_state != _STATE_CLEAR); }
+
+    virtual void _Exec() = 0;
+
+private:
+    friend class _CancellationTokenState;
+
+    void _Invoke()
+    {
+        long tid = ::pplx::details::platform::GetCurrentThreadId();
+        _ASSERTE((tid & 0x3) == 0); // If this ever fires, we need a different encoding for this.
+
+        long result = atomic_compare_exchange(_M_state, tid, _STATE_CLEAR);
+
+        if (result == _STATE_CLEAR)
+        {
+            _Exec();
+
+            result = atomic_compare_exchange(_M_state, _STATE_CALLED, tid);
+
+            if (result == _STATE_SYNCHRONIZE)
+            {
+                _M_pSyncBlock->set();
+            }
+        }
+        _Release();
+    }
+
+    atomic_long _M_state;
+    extensibility::event_t* _M_pSyncBlock;
+    _CancellationTokenState* _M_pTokenState;
+};
+
+template<typename _Function>
+class _CancellationTokenCallback : public _CancellationTokenRegistration
+{
+public:
+    _CancellationTokenCallback(const _Function& _Func) : _M_function(_Func) {}
+
+protected:
+    virtual void _Exec() { _M_function(); }
+
+private:
+    _Function _M_function;
+};
+
+class CancellationTokenRegistration_TaskProc : public _CancellationTokenRegistration
+{
+public:
+    CancellationTokenRegistration_TaskProc(TaskProc_t proc, _In_ void* pData, int initialRefs)
+        : _CancellationTokenRegistration(initialRefs), m_proc(proc), m_pData(pData)
+    {
+    }
+
+protected:
+    virtual void _Exec() { m_proc(m_pData); }
+
+private:
+    TaskProc_t m_proc;
+    void* m_pData;
+};
+
+// The base implementation of a cancellation token.
+class _CancellationTokenState : public _RefCounter
+{
+protected:
+    class TokenRegistrationContainer
+    {
+    private:
+        typedef struct _Node
+        {
+            _CancellationTokenRegistration* _M_token;
+            _Node* _M_next;
+        } Node;
+
+    public:
+        TokenRegistrationContainer() : _M_begin(nullptr), _M_last(nullptr) {}
+
+        ~TokenRegistrationContainer()
+        {
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 6001)
+#endif
+            auto node = _M_begin;
+            while (node != nullptr)
+            {
+                Node* tmp = node;
+                node = node->_M_next;
+                ::free(tmp);
+            }
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+        }
+
+        void swap(TokenRegistrationContainer& list)
+        {
+            std::swap(list._M_begin, _M_begin);
+            std::swap(list._M_last, _M_last);
+        }
+
+        bool empty() { return _M_begin == nullptr; }
+
+        template<typename T>
+        void for_each(T lambda)
+        {
+            Node* node = _M_begin;
+
+            while (node != nullptr)
+            {
+                lambda(node->_M_token);
+                node = node->_M_next;
+            }
+        }
+
+        void push_back(_CancellationTokenRegistration* token)
+        {
+            Node* node = reinterpret_cast<Node*>(::malloc(sizeof(Node)));
+            if (node == nullptr)
+            {
+                throw ::std::bad_alloc();
+            }
+
+            node->_M_token = token;
+            node->_M_next = nullptr;
+
+            if (_M_begin == nullptr)
+            {
+                _M_begin = node;
+            }
+            else
+            {
+                _M_last->_M_next = node;
+            }
+
+            _M_last = node;
+        }
+
+        void remove(_CancellationTokenRegistration* token)
+        {
+            Node* node = _M_begin;
+            Node* prev = nullptr;
+
+            while (node != nullptr)
+            {
+                if (node->_M_token == token)
+                {
+                    if (prev == nullptr)
+                    {
+                        _M_begin = node->_M_next;
+                    }
+                    else
+                    {
+                        prev->_M_next = node->_M_next;
+                    }
+
+                    if (node->_M_next == nullptr)
+                    {
+                        _M_last = prev;
+                    }
+
+                    ::free(node);
+                    break;
+                }
+
+                prev = node;
+                node = node->_M_next;
+            }
+        }
+
+    private:
+        Node* _M_begin;
+        Node* _M_last;
+    };
+
+public:
+    static _CancellationTokenState* _NewTokenState() { return new _CancellationTokenState(); }
+
+    static _CancellationTokenState* _None() { return reinterpret_cast<_CancellationTokenState*>(2); }
+
+    static bool _IsValid(_In_opt_ _CancellationTokenState* _PToken) { return (_PToken != NULL && _PToken != _None()); }
+
+    _CancellationTokenState() : _M_stateFlag(0) {}
+
+    ~_CancellationTokenState()
+    {
+        TokenRegistrationContainer rundownList;
+        {
+            extensibility::scoped_critical_section_t _Lock(_M_listLock);
+            _M_registrations.swap(rundownList);
+        }
+
+        rundownList.for_each([](_CancellationTokenRegistration* pRegistration) {
+            pRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE;
+            pRegistration->_Release();
+        });
+    }
+
+    bool _IsCanceled() const { return (_M_stateFlag != 0); }
+
+    void _Cancel()
+    {
+        if (atomic_compare_exchange(_M_stateFlag, 1l, 0l) == 0)
+        {
+            TokenRegistrationContainer rundownList;
+            {
+                extensibility::scoped_critical_section_t _Lock(_M_listLock);
+                _M_registrations.swap(rundownList);
+            }
+
+            rundownList.for_each([](_CancellationTokenRegistration* pRegistration) { pRegistration->_Invoke(); });
+
+            _M_stateFlag = 2;
+            _M_cancelComplete.set();
+        }
+    }
+
+    _CancellationTokenRegistration* _RegisterCallback(TaskProc_t _PCallback, _In_ void* _PData, int _InitialRefs = 1)
+    {
+        _CancellationTokenRegistration* pRegistration =
+            new CancellationTokenRegistration_TaskProc(_PCallback, _PData, _InitialRefs);
+        _RegisterCallback(pRegistration);
+        return pRegistration;
+    }
+
+    void _RegisterCallback(_In_ _CancellationTokenRegistration* _PRegistration)
+    {
+        _PRegistration->_M_state = _CancellationTokenRegistration::_STATE_CLEAR;
+        _PRegistration->_Reference();
+        _PRegistration->_M_pTokenState = this;
+
+        bool invoke = true;
+
+        if (!_IsCanceled())
+        {
+            extensibility::scoped_critical_section_t _Lock(_M_listLock);
+
+            if (!_IsCanceled())
+            {
+                invoke = false;
+                _M_registrations.push_back(_PRegistration);
+            }
+        }
+
+        if (invoke)
+        {
+            _PRegistration->_Invoke();
+        }
+    }
+
+    void _DeregisterCallback(_In_ _CancellationTokenRegistration* _PRegistration)
+    {
+        bool synchronize = false;
+
+        {
+            extensibility::scoped_critical_section_t _Lock(_M_listLock);
+
+            //
+            // If a cancellation has occurred, the registration list is guaranteed to be empty if we've observed it
+            // under the auspices of the lock.  In this case, we must synchronize with the canceling thread to guarantee
+            // that the cancellation is finished by the time we return from this method.
+            //
+            if (!_M_registrations.empty())
+            {
+                _M_registrations.remove(_PRegistration);
+                _PRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE;
+                _PRegistration->_Release();
+            }
+            else
+            {
+                synchronize = true;
+            }
+        }
+
+        //
+        // If the list is empty, we are in one of several situations:
+        //
+        // - The callback has already been made         --> do nothing
+        // - The callback is about to be made           --> flag it so it doesn't happen and return
+        // - The callback is in progress elsewhere      --> synchronize with it
+        // - The callback is in progress on this thread --> do nothing
+        //
+        if (synchronize)
+        {
+            long result = atomic_compare_exchange(_PRegistration->_M_state,
+                                                  _CancellationTokenRegistration::_STATE_DEFER_DELETE,
+                                                  _CancellationTokenRegistration::_STATE_CLEAR);
+
+            switch (result)
+            {
+                case _CancellationTokenRegistration::_STATE_CLEAR:
+                case _CancellationTokenRegistration::_STATE_CALLED: break;
+                case _CancellationTokenRegistration::_STATE_DEFER_DELETE:
+                case _CancellationTokenRegistration::_STATE_SYNCHRONIZE: _ASSERTE(false); break;
+                default:
+                {
+                    long tid = result;
+                    if (tid == ::pplx::details::platform::GetCurrentThreadId())
+                    {
+                        //
+                        // It is entirely legal for a caller to Deregister during a callback instead of having to
+                        // provide their own synchronization mechanism between the two.  In this case, we do *NOT* need
+                        // to explicitly synchronize with the callback as doing so would deadlock.  If the call happens
+                        // during, skip any extra synchronization.
+                        //
+                        break;
+                    }
+
+                    extensibility::event_t ev;
+                    _PRegistration->_M_pSyncBlock = &ev;
+
+                    long result_1 =
+                        atomic_exchange(_PRegistration->_M_state, _CancellationTokenRegistration::_STATE_SYNCHRONIZE);
+
+                    if (result_1 != _CancellationTokenRegistration::_STATE_CALLED)
+                    {
+                        _PRegistration->_M_pSyncBlock->wait(::pplx::extensibility::event_t::timeout_infinite);
+                    }
+
+                    break;
+                }
+            }
+        }
+    }
+
+private:
+    // The flag for the token state (whether it is canceled or not)
+    atomic_long _M_stateFlag;
+
+    // Notification of completion of cancellation of this token.
+    extensibility::event_t _M_cancelComplete; // Hmm.. where do we wait for it??
+
+    // Lock to protect the registrations list
+    extensibility::critical_section_t _M_listLock;
+
+    // The protected list of registrations
+    TokenRegistrationContainer _M_registrations;
+};
+
+} // namespace details
+
+class cancellation_token_source;
+class cancellation_token;
+
+/// <summary>
+///     The <c>cancellation_token_registration</c> class represents a callback notification from a
+///     <c>cancellation_token</c>.  When the <c>register</c> method on a <c>cancellation_token</c> is used to receive
+///     notification of when cancellation occurs, a <c>cancellation_token_registration</c> object is returned as a
+///     handle to the callback so that the caller can request a specific callback no longer be made through use of the
+///     <c>deregister</c> method.
+/// </summary>
+class cancellation_token_registration
+{
+public:
+    cancellation_token_registration() : _M_pRegistration(NULL) {}
+
+    ~cancellation_token_registration() { _Clear(); }
+
+    cancellation_token_registration(const cancellation_token_registration& _Src) { _Assign(_Src._M_pRegistration); }
+
+    cancellation_token_registration(cancellation_token_registration&& _Src) { _Move(_Src._M_pRegistration); }
+
+    cancellation_token_registration& operator=(const cancellation_token_registration& _Src)
+    {
+        if (this != &_Src)
+        {
+            _Clear();
+            _Assign(_Src._M_pRegistration);
+        }
+        return *this;
+    }
+
+    cancellation_token_registration& operator=(cancellation_token_registration&& _Src)
+    {
+        if (this != &_Src)
+        {
+            _Clear();
+            _Move(_Src._M_pRegistration);
+        }
+        return *this;
+    }
+
+    bool operator==(const cancellation_token_registration& _Rhs) const
+    {
+        return _M_pRegistration == _Rhs._M_pRegistration;
+    }
+
+    bool operator!=(const cancellation_token_registration& _Rhs) const { return !(operator==(_Rhs)); }
+
+private:
+    friend class cancellation_token;
+
+    cancellation_token_registration(_In_ details::_CancellationTokenRegistration* _PRegistration)
+        : _M_pRegistration(_PRegistration)
+    {
+    }
+
+    void _Clear()
+    {
+        if (_M_pRegistration != NULL)
+        {
+            _M_pRegistration->_Release();
+        }
+        _M_pRegistration = NULL;
+    }
+
+    void _Assign(_In_ details::_CancellationTokenRegistration* _PRegistration)
+    {
+        if (_PRegistration != NULL)
+        {
+            _PRegistration->_Reference();
+        }
+        _M_pRegistration = _PRegistration;
+    }
+
+    void _Move(_In_ details::_CancellationTokenRegistration*& _PRegistration)
+    {
+        _M_pRegistration = _PRegistration;
+        _PRegistration = NULL;
+    }
+
+    details::_CancellationTokenRegistration* _M_pRegistration;
+};
+
+/// <summary>
+///     The <c>cancellation_token</c> class represents the ability to determine whether some operation has been
+///     requested to cancel.  A given token can be associated with a <c>task_group</c>, <c>structured_task_group</c>, or
+///     <c>task</c> to provide implicit cancellation.  It can also be polled for cancellation or have a callback
+///     registered for if and when the associated <c>cancellation_token_source</c> is canceled.
+/// </summary>
+class cancellation_token
+{
+public:
+    typedef details::_CancellationTokenState* _ImplType;
+
+    /// <summary>
+    ///     Returns a cancellation token which can never be subject to cancellation.
+    /// </summary>
+    /// <returns>
+    ///     A cancellation token that cannot be canceled.
+    /// </returns>
+    static cancellation_token none() { return cancellation_token(); }
+
+    cancellation_token(const cancellation_token& _Src) { _Assign(_Src._M_Impl); }
+
+    cancellation_token(cancellation_token&& _Src) { _Move(_Src._M_Impl); }
+
+    cancellation_token& operator=(const cancellation_token& _Src)
+    {
+        if (this != &_Src)
+        {
+            _Clear();
+            _Assign(_Src._M_Impl);
+        }
+        return *this;
+    }
+
+    cancellation_token& operator=(cancellation_token&& _Src)
+    {
+        if (this != &_Src)
+        {
+            _Clear();
+            _Move(_Src._M_Impl);
+        }
+        return *this;
+    }
+
+    bool operator==(const cancellation_token& _Src) const { return _M_Impl == _Src._M_Impl; }
+
+    bool operator!=(const cancellation_token& _Src) const { return !(operator==(_Src)); }
+
+    ~cancellation_token() { _Clear(); }
+
+    /// <summary>
+    ///     Returns an indication of whether this token can be canceled or not.
+    /// </summary>
+    /// <returns>
+    ///     An indication of whether this token can be canceled or not.
+    /// </returns>
+    bool is_cancelable() const { return (_M_Impl != NULL); }
+
+    /// <summary>
+    /// Returns <c>true</c> if the token has been canceled.
+    /// </summary>
+    /// <returns>
+    /// The value <c>true</c> if the token has been canceled; otherwise, the value <c>false</c>.
+    /// </returns>
+    bool is_canceled() const { return (_M_Impl != NULL && _M_Impl->_IsCanceled()); }
+
+    /// <summary>
+    ///     Registers a callback function with the token.  If and when the token is canceled, the callback will be made.
+    ///     Note that if the token is already canceled at the point where this method is called, the callback will be
+    ///     made immediately and synchronously.
+    /// </summary>
+    /// <typeparam name="_Function">
+    ///     The type of the function object that will be called back when this <c>cancellation_token</c> is canceled.
+    /// </typeparam>
+    /// <param name="_Func">
+    ///     The function object that will be called back when this <c>cancellation_token</c> is canceled.
+    /// </param>
+    /// <returns>
+    ///     A <c>cancellation_token_registration</c> object which can be utilized in the <c>deregister</c> method to
+    ///     deregister a previously registered callback and prevent it from being made. The method will throw an <see
+    ///     cref="invalid_operation Class">invalid_operation </see> exception if it is called on a
+    ///     <c>cancellation_token</c> object that was created using the <see cref="cancellation_token::none
+    ///     Method">cancellation_token::none </see> method.
+    /// </returns>
+    template<typename _Function>
+    ::pplx::cancellation_token_registration register_callback(const _Function& _Func) const
+    {
+        if (_M_Impl == NULL)
+        {
+            // A callback cannot be registered if the token does not have an associated source.
+            throw invalid_operation();
+        }
+#if defined(_MSC_VER)
+#pragma warning(suppress : 28197)
+#endif
+        details::_CancellationTokenCallback<_Function>* _PCallback =
+            new details::_CancellationTokenCallback<_Function>(_Func);
+        _M_Impl->_RegisterCallback(_PCallback);
+        return cancellation_token_registration(_PCallback);
+    }
+
+    /// <summary>
+    ///     Removes a callback previously registered via the <c>register</c> method based on the
+    ///     <c>cancellation_token_registration</c> object returned at the time of registration.
+    /// </summary>
+    /// <param name="_Registration">
+    ///     The <c>cancellation_token_registration</c> object corresponding to the callback to be deregistered.  This
+    ///     token must have been previously returned from a call to the <c>register</c> method.
+    /// </param>
+    void deregister_callback(const cancellation_token_registration& _Registration) const
+    {
+        _M_Impl->_DeregisterCallback(_Registration._M_pRegistration);
+    }
+
+    _ImplType _GetImpl() const { return _M_Impl; }
+
+    _ImplType _GetImplValue() const
+    {
+        return (_M_Impl == NULL) ? ::pplx::details::_CancellationTokenState::_None() : _M_Impl;
+    }
+
+    static cancellation_token _FromImpl(_ImplType _Impl) { return cancellation_token(_Impl); }
+
+private:
+    friend class cancellation_token_source;
+
+    _ImplType _M_Impl;
+
+    void _Clear()
+    {
+        if (_M_Impl != NULL)
+        {
+            _M_Impl->_Release();
+        }
+        _M_Impl = NULL;
+    }
+
+    void _Assign(_ImplType _Impl)
+    {
+        if (_Impl != NULL)
+        {
+            _Impl->_Reference();
+        }
+        _M_Impl = _Impl;
+    }
+
+    void _Move(_ImplType& _Impl)
+    {
+        _M_Impl = _Impl;
+        _Impl = NULL;
+    }
+
+    cancellation_token() : _M_Impl(NULL) {}
+
+    cancellation_token(_ImplType _Impl) : _M_Impl(_Impl)
+    {
+        if (_M_Impl == ::pplx::details::_CancellationTokenState::_None())
+        {
+            _M_Impl = NULL;
+        }
+
+        if (_M_Impl != NULL)
+        {
+            _M_Impl->_Reference();
+        }
+    }
+};
+
+/// <summary>
+///     The <c>cancellation_token_source</c> class represents the ability to cancel some cancelable operation.
+/// </summary>
+class cancellation_token_source
+{
+public:
+    typedef ::pplx::details::_CancellationTokenState* _ImplType;
+
+    /// <summary>
+    ///     Constructs a new <c>cancellation_token_source</c>.  The source can be used to flag cancellation of some
+    ///     cancelable operation.
+    /// </summary>
+    cancellation_token_source() { _M_Impl = new ::pplx::details::_CancellationTokenState; }
+
+    cancellation_token_source(const cancellation_token_source& _Src) { _Assign(_Src._M_Impl); }
+
+    cancellation_token_source(cancellation_token_source&& _Src) { _Move(_Src._M_Impl); }
+
+    cancellation_token_source& operator=(const cancellation_token_source& _Src)
+    {
+        if (this != &_Src)
+        {
+            _Clear();
+            _Assign(_Src._M_Impl);
+        }
+        return *this;
+    }
+
+    cancellation_token_source& operator=(cancellation_token_source&& _Src)
+    {
+        if (this != &_Src)
+        {
+            _Clear();
+            _Move(_Src._M_Impl);
+        }
+        return *this;
+    }
+
+    bool operator==(const cancellation_token_source& _Src) const { return _M_Impl == _Src._M_Impl; }
+
+    bool operator!=(const cancellation_token_source& _Src) const { return !(operator==(_Src)); }
+
+    ~cancellation_token_source()
+    {
+        if (_M_Impl != NULL)
+        {
+            _M_Impl->_Release();
+        }
+    }
+
+    /// <summary>
+    ///     Returns a cancellation token associated with this source.  The returned token can be polled for cancellation
+    ///     or provide a callback if and when cancellation occurs.
+    /// </summary>
+    /// <returns>
+    ///     A cancellation token associated with this source.
+    /// </returns>
+    cancellation_token get_token() const { return cancellation_token(_M_Impl); }
+
+    /// <summary>
+    ///     Creates a <c>cancellation_token_source</c> which is canceled when the provided token is canceled.
+    /// </summary>
+    /// <param name="_Src">
+    ///     A token whose cancellation will cause cancellation of the returned token source.  Note that the returned
+    ///     token source can also be canceled independently of the source contained in this parameter.
+    /// </param>
+    /// <returns>
+    ///     A <c>cancellation_token_source</c> which is canceled when the token provided by the <paramref name="_Src"/>
+    ///     parameter is canceled.
+    /// </returns>
+    static cancellation_token_source create_linked_source(cancellation_token& _Src)
+    {
+        cancellation_token_source newSource;
+        _Src.register_callback([newSource]() { newSource.cancel(); });
+        return newSource;
+    }
+
+    /// <summary>
+    ///     Creates a <c>cancellation_token_source</c> which is canceled when one of a series of tokens represented by
+    ///     an STL iterator pair is canceled.
+    /// </summary>
+    /// <param name="_Begin">
+    ///     The STL iterator corresponding to the beginning of the range of tokens to listen for cancellation of.
+    /// </param>
+    /// <param name="_End">
+    ///     The STL iterator corresponding to the ending of the range of tokens to listen for cancellation of.
+    /// </param>
+    /// <returns>
+    ///     A <c>cancellation_token_source</c> which is canceled when any of the tokens provided by the range described
+    ///     by the STL iterators contained in the <paramref name="_Begin"/> and <paramref name="_End"/> parameters is
+    ///     canceled.
+    /// </returns>
+    template<typename _Iter>
+    static cancellation_token_source create_linked_source(_Iter _Begin, _Iter _End)
+    {
+        cancellation_token_source newSource;
+        for (_Iter _It = _Begin; _It != _End; ++_It)
+        {
+            _It->register_callback([newSource]() { newSource.cancel(); });
+        }
+        return newSource;
+    }
+
+    /// <summary>
+    ///     Cancels the token.  Any <c>task_group</c>, <c>structured_task_group</c>, or <c>task</c> which utilizes the
+    ///     token will be canceled upon this call and throw an exception at the next interruption point.
+    /// </summary>
+    void cancel() const { _M_Impl->_Cancel(); }
+
+    _ImplType _GetImpl() const { return _M_Impl; }
+
+    static cancellation_token_source _FromImpl(_ImplType _Impl) { return cancellation_token_source(_Impl); }
+
+private:
+    _ImplType _M_Impl;
+
+    void _Clear()
+    {
+        if (_M_Impl != NULL)
+        {
+            _M_Impl->_Release();
+        }
+        _M_Impl = NULL;
+    }
+
+    void _Assign(_ImplType _Impl)
+    {
+        if (_Impl != NULL)
+        {
+            _Impl->_Reference();
+        }
+        _M_Impl = _Impl;
+    }
+
+    void _Move(_ImplType& _Impl)
+    {
+        _M_Impl = _Impl;
+        _Impl = NULL;
+    }
+
+    cancellation_token_source(_ImplType _Impl) : _M_Impl(_Impl)
+    {
+        if (_M_Impl == ::pplx::details::_CancellationTokenState::_None())
+        {
+            _M_Impl = NULL;
+        }
+
+        if (_M_Impl != NULL)
+        {
+            _M_Impl->_Reference();
+        }
+    }
+};
+
+} // namespace pplx
+
+#pragma pop_macro("new")
+#pragma pack(pop)
+
+#endif // _PPLXCANCELLATION_TOKEN_H
diff --git a/Release/include/pplx/pplxconv.h b/Release/include/pplx/pplxconv.h
new file mode 100644 (file)
index 0000000..723d42f
--- /dev/null
@@ -0,0 +1,83 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Utilities to convert between PPL tasks and PPLX tasks
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#ifndef _PPLXCONV_H
+#define _PPLXCONV_H
+
+#ifndef _WIN32
+#error This is only supported on Windows
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1700) && (_MSC_VER < 1800) && !CPPREST_FORCE_PPLX
+
+#include "pplx/pplxtasks.h"
+#include <ppltasks.h>
+
+namespace pplx
+{
+namespace _Ppl_conv_helpers
+{
+template<typename _Tc, typename _F>
+auto _Set_value(_Tc _Tcp, const _F& _Func) -> decltype(_Tcp.set(_Func()))
+{
+    return _Tcp.set(_Func());
+}
+
+template<typename _Tc, typename _F>
+auto _Set_value(_Tc _Tcp, const _F& _Func, ...) -> decltype(_Tcp.set())
+{
+    _Func();
+    return _Tcp.set();
+}
+
+template<typename _TaskType, typename _OtherTaskType, typename _OtherTCEType>
+_OtherTaskType _Convert_task(_TaskType _Task)
+{
+    _OtherTCEType _Tc;
+    _Task.then([_Tc](_TaskType _Task2) {
+        try
+        {
+            _Ppl_conv_helpers::_Set_value(_Tc, [=] { return _Task2.get(); });
+        }
+        catch (...)
+        {
+            _Tc.set_exception(std::current_exception());
+        }
+    });
+    _OtherTaskType _T_other(_Tc);
+    return _T_other;
+}
+} // namespace _Ppl_conv_helpers
+
+template<typename _TaskType>
+concurrency::task<_TaskType> pplx_task_to_concurrency_task(pplx::task<_TaskType> _Task)
+{
+    return _Ppl_conv_helpers::_Convert_task<typename pplx::task<_TaskType>,
+                                            concurrency::task<_TaskType>,
+                                            concurrency::task_completion_event<_TaskType>>(_Task);
+}
+
+template<typename _TaskType>
+pplx::task<_TaskType> concurrency_task_to_pplx_task(concurrency::task<_TaskType> _Task)
+{
+    return _Ppl_conv_helpers::_Convert_task<typename concurrency::task<_TaskType>,
+                                            pplx::task<_TaskType>,
+                                            pplx::task_completion_event<_TaskType>>(_Task);
+}
+} // namespace pplx
+
+#endif
+
+#endif // _PPLXCONV_H
diff --git a/Release/include/pplx/pplxinterface.h b/Release/include/pplx/pplxinterface.h
new file mode 100644 (file)
index 0000000..4e5ca5b
--- /dev/null
@@ -0,0 +1,227 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * PPL interfaces
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#ifndef _PPLXINTERFACE_H
+#define _PPLXINTERFACE_H
+
+#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
+#error This file must not be included for Visual Studio 12 or later
+#endif
+
+#if defined(_CRTBLD)
+#elif defined(_WIN32)
+#if (_MSC_VER >= 1700)
+#define _USE_REAL_ATOMICS
+#endif
+#else // GCC compiler
+#define _USE_REAL_ATOMICS
+#endif
+
+#include <memory>
+#ifdef _USE_REAL_ATOMICS
+#include <atomic>
+#endif
+
+#define _pplx_cdecl __cdecl
+
+namespace pplx
+{
+/// <summary>
+///     An elementary abstraction for a task, defined as <c>void (__cdecl * TaskProc_t)(void *)</c>. A <c>TaskProc</c>
+///     is called to invoke the body of a task.
+/// </summary>
+/**/
+typedef void(_pplx_cdecl* TaskProc_t)(void*);
+
+/// <summary>
+///     Scheduler Interface
+/// </summary>
+struct __declspec(novtable) scheduler_interface
+{
+    virtual void schedule(TaskProc_t, _In_ void*) = 0;
+};
+
+/// <summary>
+///     Represents a pointer to a scheduler. This class exists to allow the
+///     the specification of a shared lifetime by using shared_ptr or just
+///     a plain reference by using raw pointer.
+/// </summary>
+struct scheduler_ptr
+{
+    /// <summary>
+    /// Creates a scheduler pointer from shared_ptr to scheduler
+    /// </summary>
+    explicit scheduler_ptr(std::shared_ptr<scheduler_interface> scheduler) : m_sharedScheduler(std::move(scheduler))
+    {
+        m_scheduler = m_sharedScheduler.get();
+    }
+
+    /// <summary>
+    /// Creates a scheduler pointer from raw pointer to scheduler
+    /// </summary>
+    explicit scheduler_ptr(_In_opt_ scheduler_interface* pScheduler) : m_scheduler(pScheduler) {}
+
+    /// <summary>
+    /// Behave like a pointer
+    /// </summary>
+    scheduler_interface* operator->() const { return get(); }
+
+    /// <summary>
+    ///  Returns the raw pointer to the scheduler
+    /// </summary>
+    scheduler_interface* get() const { return m_scheduler; }
+
+    /// <summary>
+    /// Test whether the scheduler pointer is non-null
+    /// </summary>
+    operator bool() const { return get() != nullptr; }
+
+private:
+    std::shared_ptr<scheduler_interface> m_sharedScheduler;
+    scheduler_interface* m_scheduler;
+};
+
+/// <summary>
+///     Describes the execution status of a <c>task_group</c> or <c>structured_task_group</c> object.  A value of this
+///     type is returned by numerous methods that wait on tasks scheduled to a task group to complete.
+/// </summary>
+/// <seealso cref="task_group Class"/>
+/// <seealso cref="task_group::wait Method"/>
+/// <seealso cref="task_group::run_and_wait Method"/>
+/// <seealso cref="structured_task_group Class"/>
+/// <seealso cref="structured_task_group::wait Method"/>
+/// <seealso cref="structured_task_group::run_and_wait Method"/>
+/**/
+enum task_group_status
+{
+    /// <summary>
+    ///     The tasks queued to the <c>task_group</c> object have not completed.  Note that this value is not presently
+    ///     returned by the Concurrency Runtime.
+    /// </summary>
+    /**/
+    not_complete,
+
+    /// <summary>
+    ///     The tasks queued to the <c>task_group</c> or <c>structured_task_group</c> object completed successfully.
+    /// </summary>
+    /**/
+    completed,
+
+    /// <summary>
+    ///     The <c>task_group</c> or <c>structured_task_group</c> object was canceled.  One or more tasks may not have
+    ///     executed.
+    /// </summary>
+    /**/
+    canceled
+};
+
+namespace details
+{
+/// <summary>
+///     Atomics
+/// </summary>
+#ifdef _USE_REAL_ATOMICS
+typedef std::atomic<long> atomic_long;
+typedef std::atomic<size_t> atomic_size_t;
+
+template<typename _T>
+_T atomic_compare_exchange(std::atomic<_T>& _Target, _T _Exchange, _T _Comparand)
+{
+    _T _Result = _Comparand;
+    _Target.compare_exchange_strong(_Result, _Exchange);
+    return _Result;
+}
+
+template<typename _T>
+_T atomic_exchange(std::atomic<_T>& _Target, _T _Value)
+{
+    return _Target.exchange(_Value);
+}
+
+template<typename _T>
+_T atomic_increment(std::atomic<_T>& _Target)
+{
+    return _Target.fetch_add(1) + 1;
+}
+
+template<typename _T>
+_T atomic_decrement(std::atomic<_T>& _Target)
+{
+    return _Target.fetch_sub(1) - 1;
+}
+
+template<typename _T>
+_T atomic_add(std::atomic<_T>& _Target, _T value)
+{
+    return _Target.fetch_add(value) + value;
+}
+
+#else // not _USE_REAL_ATOMICS
+
+typedef long volatile atomic_long;
+typedef size_t volatile atomic_size_t;
+
+template<class T>
+inline T atomic_exchange(T volatile& _Target, T _Value)
+{
+    return _InterlockedExchange(&_Target, _Value);
+}
+
+inline long atomic_increment(long volatile& _Target) { return _InterlockedIncrement(&_Target); }
+
+inline long atomic_add(long volatile& _Target, long value) { return _InterlockedExchangeAdd(&_Target, value) + value; }
+
+inline size_t atomic_increment(size_t volatile& _Target)
+{
+#if (defined(_M_IX86) || defined(_M_ARM))
+    return static_cast<size_t>(_InterlockedIncrement(reinterpret_cast<long volatile*>(&_Target)));
+#else
+    return static_cast<size_t>(_InterlockedIncrement64(reinterpret_cast<__int64 volatile*>(&_Target)));
+#endif
+}
+
+inline long atomic_decrement(long volatile& _Target) { return _InterlockedDecrement(&_Target); }
+
+inline size_t atomic_decrement(size_t volatile& _Target)
+{
+#if (defined(_M_IX86) || defined(_M_ARM))
+    return static_cast<size_t>(_InterlockedDecrement(reinterpret_cast<long volatile*>(&_Target)));
+#else
+    return static_cast<size_t>(_InterlockedDecrement64(reinterpret_cast<__int64 volatile*>(&_Target)));
+#endif
+}
+
+inline long atomic_compare_exchange(long volatile& _Target, long _Exchange, long _Comparand)
+{
+    return _InterlockedCompareExchange(&_Target, _Exchange, _Comparand);
+}
+
+inline size_t atomic_compare_exchange(size_t volatile& _Target, size_t _Exchange, size_t _Comparand)
+{
+#if (defined(_M_IX86) || defined(_M_ARM))
+    return static_cast<size_t>(_InterlockedCompareExchange(
+        reinterpret_cast<long volatile*>(_Target), static_cast<long>(_Exchange), static_cast<long>(_Comparand)));
+#else
+    return static_cast<size_t>(_InterlockedCompareExchange64(reinterpret_cast<__int64 volatile*>(_Target),
+                                                             static_cast<__int64>(_Exchange),
+                                                             static_cast<__int64>(_Comparand)));
+#endif
+}
+#endif // _USE_REAL_ATOMICS
+
+} // namespace details
+} // namespace pplx
+
+#endif // _PPLXINTERFACE_H
diff --git a/Release/include/pplx/pplxlinux.h b/Release/include/pplx/pplxlinux.h
new file mode 100644 (file)
index 0000000..ab0c5c9
--- /dev/null
@@ -0,0 +1,277 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Linux specific pplx implementations
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if (defined(_MSC_VER))
+#error This file must not be included for Visual Studio
+#endif
+
+#ifndef _WIN32
+
+#include "cpprest/details/cpprest_compat.h"
+#include "pthread.h"
+#include <signal.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+
+#include "pplx/pplxinterface.h"
+
+namespace pplx
+{
+namespace details
+{
+namespace platform
+{
+/// <summary>
+/// Returns a unique identifier for the execution thread where this routine in invoked
+/// </summary>
+_PPLXIMP long _pplx_cdecl GetCurrentThreadId();
+
+/// <summary>
+/// Yields the execution of the current execution thread - typically when spin-waiting
+/// </summary>
+_PPLXIMP void _pplx_cdecl YieldExecution();
+
+/// <summary>
+/// Captures the callstack
+/// </summary>
+__declspec(noinline) inline static size_t CaptureCallstack(void**, size_t, size_t) { return 0; }
+} // namespace platform
+
+/// <summary>
+/// Manual reset event
+/// </summary>
+class event_impl
+{
+private:
+    std::mutex _lock;
+    std::condition_variable _condition;
+    bool _signaled;
+
+public:
+    static const unsigned int timeout_infinite = 0xFFFFFFFF;
+
+    event_impl() : _signaled(false) {}
+
+    void set()
+    {
+        std::lock_guard<std::mutex> lock(_lock);
+        _signaled = true;
+        _condition.notify_all();
+    }
+
+    void reset()
+    {
+        std::lock_guard<std::mutex> lock(_lock);
+        _signaled = false;
+    }
+
+    unsigned int wait(unsigned int timeout)
+    {
+        std::unique_lock<std::mutex> lock(_lock);
+        if (timeout == event_impl::timeout_infinite)
+        {
+            _condition.wait(lock, [this]() -> bool { return _signaled; });
+            return 0;
+        }
+        else
+        {
+            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
+            // Note: this must be consistent with the behavior of the Windows version, which is based on
+            // WaitForSingleObjectEx
+            return status ? 0 : event_impl::timeout_infinite;
+        }
+    }
+
+    unsigned int wait() { return wait(event_impl::timeout_infinite); }
+};
+
+/// <summary>
+/// Reader writer lock
+/// </summary>
+class reader_writer_lock_impl
+{
+private:
+    pthread_rwlock_t _M_reader_writer_lock;
+
+public:
+    class scoped_lock_read
+    {
+    public:
+        explicit scoped_lock_read(reader_writer_lock_impl& _Reader_writer_lock)
+            : _M_reader_writer_lock(_Reader_writer_lock)
+        {
+            _M_reader_writer_lock.lock_read();
+        }
+
+        ~scoped_lock_read() { _M_reader_writer_lock.unlock(); }
+
+    private:
+        reader_writer_lock_impl& _M_reader_writer_lock;
+        scoped_lock_read(const scoped_lock_read&);                  // no copy constructor
+        scoped_lock_read const& operator=(const scoped_lock_read&); // no assignment operator
+    };
+
+    reader_writer_lock_impl() { pthread_rwlock_init(&_M_reader_writer_lock, nullptr); }
+
+    ~reader_writer_lock_impl() { pthread_rwlock_destroy(&_M_reader_writer_lock); }
+
+    void lock() { pthread_rwlock_wrlock(&_M_reader_writer_lock); }
+
+    void lock_read() { pthread_rwlock_rdlock(&_M_reader_writer_lock); }
+
+    void unlock() { pthread_rwlock_unlock(&_M_reader_writer_lock); }
+};
+
+/// <summary>
+/// Recursive mutex
+/// </summary>
+class recursive_lock_impl
+{
+public:
+    recursive_lock_impl() : _M_owner(-1), _M_recursionCount(0) {}
+
+    ~recursive_lock_impl()
+    {
+        _ASSERTE(_M_owner == -1);
+        _ASSERTE(_M_recursionCount == 0);
+    }
+
+    void lock()
+    {
+        auto id = ::pplx::details::platform::GetCurrentThreadId();
+
+        if (_M_owner == id)
+        {
+            _M_recursionCount++;
+        }
+        else
+        {
+            _M_cs.lock();
+            _M_owner = id;
+            _M_recursionCount = 1;
+        }
+    }
+
+    void unlock()
+    {
+        _ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId());
+        _ASSERTE(_M_recursionCount >= 1);
+
+        _M_recursionCount--;
+
+        if (_M_recursionCount == 0)
+        {
+            _M_owner = -1;
+            _M_cs.unlock();
+        }
+    }
+
+private:
+    std::mutex _M_cs;
+    std::atomic<long> _M_owner;
+    long _M_recursionCount;
+};
+
+#if defined(__APPLE__)
+class apple_scheduler : public pplx::scheduler_interface
+#else
+class linux_scheduler : public pplx::scheduler_interface
+#endif
+{
+public:
+    _PPLXIMP virtual void schedule(TaskProc_t proc, _In_ void* param);
+#if defined(__APPLE__)
+    virtual ~apple_scheduler() {}
+#else
+    virtual ~linux_scheduler() {}
+#endif
+};
+
+} // namespace details
+
+/// <summary>
+///  A generic RAII wrapper for locks that implements the critical_section interface
+///  std::lock_guard
+/// </summary>
+template<class _Lock>
+class scoped_lock
+{
+public:
+    explicit scoped_lock(_Lock& _Critical_section) : _M_critical_section(_Critical_section)
+    {
+        _M_critical_section.lock();
+    }
+
+    ~scoped_lock() { _M_critical_section.unlock(); }
+
+private:
+    _Lock& _M_critical_section;
+
+    scoped_lock(const scoped_lock&);                  // no copy constructor
+    scoped_lock const& operator=(const scoped_lock&); // no assignment operator
+};
+
+// The extensibility namespace contains the type definitions that are used internally
+namespace extensibility
+{
+typedef ::pplx::details::event_impl event_t;
+
+typedef std::mutex critical_section_t;
+typedef scoped_lock<critical_section_t> scoped_critical_section_t;
+
+typedef ::pplx::details::reader_writer_lock_impl reader_writer_lock_t;
+typedef scoped_lock<reader_writer_lock_t> scoped_rw_lock_t;
+typedef ::pplx::extensibility::reader_writer_lock_t::scoped_lock_read scoped_read_lock_t;
+
+typedef ::pplx::details::recursive_lock_impl recursive_lock_t;
+typedef scoped_lock<recursive_lock_t> scoped_recursive_lock_t;
+} // namespace extensibility
+
+/// <summary>
+/// Default scheduler type
+/// </summary>
+#if defined(__APPLE__)
+typedef details::apple_scheduler default_scheduler_t;
+#else
+typedef details::linux_scheduler default_scheduler_t;
+#endif
+
+namespace details
+{
+/// <summary>
+/// Terminate the process due to unhandled exception
+/// </summary>
+#ifndef _REPORT_PPLTASK_UNOBSERVED_EXCEPTION
+#define _REPORT_PPLTASK_UNOBSERVED_EXCEPTION()                                                                         \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        raise(SIGTRAP);                                                                                                \
+        std::terminate();                                                                                              \
+    } while (false)
+#endif //_REPORT_PPLTASK_UNOBSERVED_EXCEPTION
+} // namespace details
+
+// see: http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
+// this is critical to inline
+__attribute__((always_inline)) inline void* _ReturnAddress() { return __builtin_return_address(0); }
+
+} // namespace pplx
+
+#endif // !_WIN32
diff --git a/Release/include/pplx/pplxtasks.h b/Release/include/pplx/pplxtasks.h
new file mode 100644 (file)
index 0000000..6868fc1
--- /dev/null
@@ -0,0 +1,7600 @@
+/***\r
+ * Copyright (C) Microsoft. All rights reserved.\r
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.\r
+ *\r
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\r
+ *\r
+ * Parallel Patterns Library - PPLx Tasks\r
+ *\r
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk\r
+ *\r
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\r
+ ****/\r
+\r
+#pragma once\r
+\r
+#ifndef PPLXTASKS_H\r
+#define PPLXTASKS_H\r
+\r
+#include "cpprest/details/cpprest_compat.h"\r
+\r
+#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX\r
+#include <ppltasks.h>\r
+namespace pplx = Concurrency;\r
+\r
+namespace Concurrency\r
+{\r
+/// <summary>\r
+/// Sets the ambient scheduler to be used by the PPL constructs.\r
+/// </summary>\r
+_ASYNCRTIMP void __cdecl set_cpprestsdk_ambient_scheduler(const std::shared_ptr<scheduler_interface>& _Scheduler);\r
+\r
+/// <summary>\r
+/// Gets the ambient scheduler to be used by the PPL constructs\r
+/// </summary>\r
+_ASYNCRTIMP const std::shared_ptr<scheduler_interface>& __cdecl get_cpprestsdk_ambient_scheduler();\r
+\r
+} // namespace Concurrency\r
+\r
+#if (_MSC_VER >= 1900)\r
+#include <concrt.h>\r
+namespace Concurrency\r
+{\r
+namespace extensibility\r
+{\r
+typedef ::std::condition_variable condition_variable_t;\r
+typedef ::std::mutex critical_section_t;\r
+typedef ::std::unique_lock<::std::mutex> scoped_critical_section_t;\r
+\r
+typedef ::Concurrency::event event_t;\r
+typedef ::Concurrency::reader_writer_lock reader_writer_lock_t;\r
+typedef ::Concurrency::reader_writer_lock::scoped_lock scoped_rw_lock_t;\r
+typedef ::Concurrency::reader_writer_lock::scoped_lock_read scoped_read_lock_t;\r
+\r
+typedef ::Concurrency::details::_ReentrantBlockingLock recursive_lock_t;\r
+typedef recursive_lock_t::_Scoped_lock scoped_recursive_lock_t;\r
+} // namespace extensibility\r
+} // namespace Concurrency\r
+#endif // _MSC_VER >= 1900\r
+#else\r
+\r
+#include "pplx/pplx.h"\r
+\r
+#if defined(__ANDROID__)\r
+#include <jni.h>\r
+void cpprest_init(JavaVM*);\r
+#endif\r
+\r
+// Cannot build using a compiler that is older than dev10 SP1\r
+#if defined(_MSC_VER)\r
+#if _MSC_FULL_VER < 160040219 /*IFSTRIP=IGN*/\r
+#error ERROR: Visual Studio 2010 SP1 or later is required to build ppltasks\r
+#endif /*IFSTRIP=IGN*/\r
+#endif /* defined(_MSC_VER) */\r
+\r
+#include <algorithm>\r
+#include <atomic>\r
+#include <exception>\r
+#include <functional>\r
+#include <utility>\r
+#include <vector>\r
+\r
+#if defined(_MSC_VER)\r
+#include <intrin.h>\r
+#if defined(__cplusplus_winrt)\r
+#include <agile.h>\r
+#include <ctxtcall.h>\r
+#include <winapifamily.h>\r
+\r
+#include <windows.h>\r
+#ifndef _UITHREADCTXT_SUPPORT\r
+\r
+#ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/\r
+\r
+// It is safe to include winapifamily as WINAPI_FAMILY was defined by the user\r
+#include <winapifamily.h>\r
+\r
+#if WINAPI_FAMILY == WINAPI_FAMILY_APP\r
+// UI thread context support is not required for desktop and Windows Store apps\r
+#define _UITHREADCTXT_SUPPORT 0\r
+#elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP\r
+// UI thread context support is not required for desktop and Windows Store apps\r
+#define _UITHREADCTXT_SUPPORT 0\r
+#else /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */\r
+#define _UITHREADCTXT_SUPPORT 1\r
+#endif /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */\r
+\r
+#else /* WINAPI_FAMILY */\r
+// Not supported without a WINAPI_FAMILY setting.\r
+#define _UITHREADCTXT_SUPPORT 0\r
+#endif /* WINAPI_FAMILY */\r
+\r
+#endif /* _UITHREADCTXT_SUPPORT */\r
+\r
+#if _UITHREADCTXT_SUPPORT\r
+#include <uithreadctxt.h>\r
+#endif /* _UITHREADCTXT_SUPPORT */\r
+\r
+#pragma detect_mismatch("PPLXTASKS_WITH_WINRT", "1")\r
+#else /* defined(__cplusplus_winrt) */\r
+#pragma detect_mismatch("PPLXTASKS_WITH_WINRT", "0")\r
+#endif /* defined(__cplusplus_winrt) */\r
+#endif /* defined(_MSC_VER) */\r
+\r
+#ifdef _DEBUG\r
+#define _DBG_ONLY(X) X\r
+#else\r
+#define _DBG_ONLY(X)\r
+#endif // #ifdef _DEBUG\r
+\r
+// std::copy_exception changed to std::make_exception_ptr from VS 2010 to VS 11.\r
+#ifdef _MSC_VER\r
+#if _MSC_VER < 1700 /*IFSTRIP=IGN*/\r
+namespace std\r
+{\r
+template<class _E>\r
+exception_ptr make_exception_ptr(_E _Except)\r
+{\r
+    return copy_exception(_Except);\r
+}\r
+} // namespace std\r
+#endif              /* _MSC_VER < 1700 */\r
+#ifndef PPLX_TASK_ASYNC_LOGGING\r
+#if _MSC_VER >= 1800 && defined(__cplusplus_winrt)\r
+#define PPLX_TASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt\r
+#else\r
+#define PPLX_TASK_ASYNC_LOGGING 0\r
+#endif\r
+#endif /* !PPLX_TASK_ASYNC_LOGGING */\r
+#endif /* _MSC_VER */\r
+\r
+#pragma pack(push, _CRT_PACKING)\r
+\r
+#if defined(_MSC_VER)\r
+#pragma warning(push)\r
+#pragma warning(disable : 28197)\r
+#pragma warning(disable : 4100) // Unreferenced formal parameter - needed for document generation\r
+#pragma warning(disable : 4127) // constant express in if condition - we use it for meta programming\r
+#endif                          /* defined(_MSC_VER) */\r
+\r
+// All CRT public header files are required to be protected from the macro new\r
+#pragma push_macro("new")\r
+#undef new\r
+\r
+// stuff ported from Dev11 CRT\r
+// NOTE: this doesn't actually match std::declval. it behaves differently for void!\r
+// so don't blindly change it to std::declval.\r
+namespace stdx\r
+{\r
+template<class _T>\r
+_T&& declval();\r
+}\r
+\r
+/// <summary>\r
+///     The <c>pplx</c> namespace provides classes and functions that give you access to the Concurrency Runtime,\r
+///     a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>.\r
+/// </summary>\r
+/**/\r
+namespace pplx\r
+{\r
+/// <summary>\r
+///     A type that represents the terminal state of a task. Valid values are <c>completed</c> and <c>canceled</c>.\r
+/// </summary>\r
+/// <seealso cref="task Class"/>\r
+/**/\r
+typedef task_group_status task_status;\r
+\r
+template<typename _Type>\r
+class task;\r
+template<>\r
+class task<void>;\r
+\r
+// In debug builds, default to 10 frames, unless this is overridden prior to #includ'ing ppltasks.h.  In retail builds,\r
+// default to only one frame.\r
+#ifndef PPLX_TASK_SAVE_FRAME_COUNT\r
+#ifdef _DEBUG\r
+#define PPLX_TASK_SAVE_FRAME_COUNT 10\r
+#else\r
+#define PPLX_TASK_SAVE_FRAME_COUNT 1\r
+#endif\r
+#endif\r
+\r
+/// <summary>\r
+/// Helper macro to determine how many stack frames need to be saved. When any number less or equal to 1 is specified,\r
+/// only one frame is captured and no stackwalk will be involved. Otherwise, the number of callstack frames will be\r
+/// captured.\r
+/// </summary>\r
+/// <ramarks>\r
+/// This needs to be defined as a macro rather than a function so that if we're only gathering one frame,\r
+/// _ReturnAddress() will evaluate to client code, rather than a helper function inside of _TaskCreationCallstack,\r
+/// itself.\r
+/// </remarks>\r
+#if PPLX_TASK_SAVE_FRAME_COUNT > 1\r
+#if defined(__cplusplus_winrt) && !defined(_DEBUG)\r
+#pragma message(                                                                                                       \\r
+    "WARNING: Redefining PPLX_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!")\r
+#define PPLX_CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress())\r
+#else\r
+#define PPLX_CAPTURE_CALLSTACK()                                                                                       \\r
+    ::pplx::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPLX_TASK_SAVE_FRAME_COUNT)\r
+#endif\r
+#else\r
+#define PPLX_CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress())\r
+#endif\r
+\r
+/// <summary>\r
+///     Returns an indication of whether the task that is currently executing has received a request to cancel its\r
+///     execution. Cancellation is requested on a task if the task was created with a cancellation token, and\r
+///     the token source associated with that token is canceled.\r
+/// </summary>\r
+/// <returns>\r
+///     <c>true</c> if the currently executing task has received a request for cancellation, <c>false</c> otherwise.\r
+/// </returns>\r
+/// <remarks>\r
+///     If you call this method in the body of a task and it returns <c>true</c>, you must respond with a call to\r
+///     <see cref="cancel_current_task Function">cancel_current_task</see> to acknowledge the cancellation request,\r
+///     after performing any cleanup you need. This will abort the execution of the task and cause it to enter into\r
+///     the <c>canceled</c> state. If you do not respond and continue execution, or return instead of calling\r
+///     <c>cancel_current_task</c>, the task will enter the <c>completed</c> state when it is done.\r
+///     state.\r
+///     <para>A task is not cancelable if it was created without a cancellation token.</para>\r
+/// </remarks>\r
+/// <seealso cref="task Class"/>\r
+/// <seealso cref="cancellation_token_source Class"/>\r
+/// <seealso cref="cancellation_token Class"/>\r
+/// <seealso cref="cancel_current_task Function"/>\r
+/**/\r
+inline bool _pplx_cdecl is_task_cancellation_requested()\r
+{\r
+    return ::pplx::details::_TaskCollection_t::_Is_cancellation_requested();\r
+}\r
+\r
+/// <summary>\r
+///     Cancels the currently executing task. This function can be called from within the body of a task to abort the\r
+///     task's execution and cause it to enter the <c>canceled</c> state. While it may be used in response to\r
+///     the <see cref="is_task_cancellation_requested Function">is_task_cancellation_requested</see> function, you may\r
+///     also use it by itself, to initiate cancellation of the task that is currently executing.\r
+///     <para>It is not a supported scenario to call this function if you are not within the body of a <c>task</c>.\r
+///     Doing so will result in undefined behavior such as a crash or a hang in your application.</para>\r
+/// </summary>\r
+/// <seealso cref="task Class"/>\r
+/**/\r
+inline __declspec(noreturn) void _pplx_cdecl cancel_current_task() { throw task_canceled(); }\r
+\r
+namespace details\r
+{\r
+/// <summary>\r
+///     Callstack container, which is used to capture and preserve callstacks in ppltasks.\r
+///     Members of this class is examined by vc debugger, thus there will be no public access methods.\r
+///     Please note that names of this class should be kept stable for debugger examining.\r
+/// </summary>\r
+class _TaskCreationCallstack\r
+{\r
+private:\r
+    // If _M_SingleFrame != nullptr, there will be only one frame of callstacks, which is stored in _M_SingleFrame;\r
+    // otherwise, _M_Frame will store all the callstack frames.\r
+    void* _M_SingleFrame;\r
+    std::vector<void*> _M_frames;\r
+\r
+public:\r
+    _TaskCreationCallstack() { _M_SingleFrame = nullptr; }\r
+\r
+    // Store one frame of callstack. This function works for both Debug / Release CRT.\r
+    static _TaskCreationCallstack _CaptureSingleFrameCallstack(void* _SingleFrame)\r
+    {\r
+        _TaskCreationCallstack _csc;\r
+        _csc._M_SingleFrame = _SingleFrame;\r
+        return _csc;\r
+    }\r
+\r
+    // Capture _CaptureFrames number of callstack frames. This function only work properly for Desktop or Debug CRT.\r
+    __declspec(noinline) static _TaskCreationCallstack _CaptureMultiFramesCallstack(size_t _CaptureFrames)\r
+    {\r
+        _TaskCreationCallstack _csc;\r
+        _csc._M_frames.resize(_CaptureFrames);\r
+        // skip 2 frames to make sure callstack starts from user code\r
+        _csc._M_frames.resize(::pplx::details::platform::CaptureCallstack(&_csc._M_frames[0], 2, _CaptureFrames));\r
+        return _csc;\r
+    }\r
+};\r
+typedef unsigned char _Unit_type;\r
+\r
+struct _TypeSelectorNoAsync\r
+{\r
+};\r
+struct _TypeSelectorAsyncOperationOrTask\r
+{\r
+};\r
+struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask\r
+{\r
+};\r
+struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask\r
+{\r
+};\r
+struct _TypeSelectorAsyncAction\r
+{\r
+};\r
+struct _TypeSelectorAsyncActionWithProgress\r
+{\r
+};\r
+struct _TypeSelectorAsyncOperationWithProgress\r
+{\r
+};\r
+\r
+template<typename _Ty>\r
+struct _NormalizeVoidToUnitType\r
+{\r
+    typedef _Ty _Type;\r
+};\r
+\r
+template<>\r
+struct _NormalizeVoidToUnitType<void>\r
+{\r
+    typedef _Unit_type _Type;\r
+};\r
+\r
+template<typename _T>\r
+struct _IsUnwrappedAsyncSelector\r
+{\r
+    static const bool _Value = true;\r
+};\r
+\r
+template<>\r
+struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync>\r
+{\r
+    static const bool _Value = false;\r
+};\r
+\r
+template<typename _Ty>\r
+struct _UnwrapTaskType\r
+{\r
+    typedef _Ty _Type;\r
+};\r
+\r
+template<typename _Ty>\r
+struct _UnwrapTaskType<task<_Ty>>\r
+{\r
+    typedef _Ty _Type;\r
+};\r
+\r
+template<typename _T>\r
+_TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>);\r
+\r
+_TypeSelectorNoAsync _AsyncOperationKindSelector(...);\r
+\r
+#if defined(__cplusplus_winrt)\r
+template<typename _Type>\r
+struct _Unhat\r
+{\r
+    typedef _Type _Value;\r
+};\r
+\r
+template<typename _Type>\r
+struct _Unhat<_Type ^>\r
+{\r
+    typedef _Type _Value;\r
+};\r
+\r
+value struct _NonUserType\r
+{\r
+public:\r
+    int _Dummy;\r
+};\r
+\r
+template<typename _Type, bool _IsValueTypeOrRefType = __is_valid_winrt_type(_Type)>\r
+struct _ValueTypeOrRefType\r
+{\r
+    typedef _NonUserType _Value;\r
+};\r
+\r
+template<typename _Type>\r
+struct _ValueTypeOrRefType<_Type, true>\r
+{\r
+    typedef _Type _Value;\r
+};\r
+\r
+template<typename _T1, typename _T2>\r
+_T2 _ProgressTypeSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2> ^);\r
+\r
+template<typename _T1>\r
+_T1 _ProgressTypeSelector(Windows::Foundation::IAsyncActionWithProgress<_T1> ^);\r
+\r
+template<typename _Type>\r
+struct _GetProgressType\r
+{\r
+    typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value;\r
+};\r
+\r
+template<typename _Type>\r
+struct _IsIAsyncInfo\r
+{\r
+    static const bool _Value = __is_base_of(Windows::Foundation::IAsyncInfo, typename _Unhat<_Type>::_Value);\r
+};\r
+\r
+template<typename _T>\r
+_TypeSelectorAsyncOperation _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperation<_T> ^);\r
+\r
+_TypeSelectorAsyncAction _AsyncOperationKindSelector(Windows::Foundation::IAsyncAction ^);\r
+\r
+template<typename _T1, typename _T2>\r
+_TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(\r
+    Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2> ^);\r
+\r
+template<typename _T>\r
+_TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncActionWithProgress<_T> ^);\r
+\r
+template<typename _Type, bool _IsAsync = _IsIAsyncInfo<_Type>::_Value>\r
+struct _TaskTypeTraits\r
+{\r
+    typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType;\r
+    typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind;\r
+    typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType;\r
+\r
+    static const bool _IsAsyncTask = _IsAsync;\r
+    static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value;\r
+};\r
+\r
+template<typename _Type>\r
+struct _TaskTypeTraits<_Type, true>\r
+{\r
+    typedef decltype(((_Type) nullptr)->GetResults()) _TaskRetType;\r
+    typedef _TaskRetType _NormalizedTaskRetType;\r
+    typedef decltype(_AsyncOperationKindSelector((_Type) nullptr)) _AsyncKind;\r
+\r
+    static const bool _IsAsyncTask = true;\r
+    static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value;\r
+};\r
+\r
+#else  /* defined (__cplusplus_winrt) */\r
+template<typename _Type>\r
+struct _IsIAsyncInfo\r
+{\r
+    static const bool _Value = false;\r
+};\r
+\r
+template<typename _Type, bool _IsAsync = false>\r
+struct _TaskTypeTraits\r
+{\r
+    typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType;\r
+    typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind;\r
+    typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType;\r
+\r
+    static const bool _IsAsyncTask = false;\r
+    static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value;\r
+};\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+template<typename _Function>\r
+auto _IsCallable(_Function _Func, int) -> decltype(_Func(), std::true_type())\r
+{\r
+    (void)(_Func);\r
+    return std::true_type();\r
+}\r
+template<typename _Function>\r
+std::false_type _IsCallable(_Function, ...)\r
+{\r
+    return std::false_type();\r
+}\r
+\r
+template<>\r
+struct _TaskTypeTraits<void>\r
+{\r
+    typedef void _TaskRetType;\r
+    typedef _TypeSelectorNoAsync _AsyncKind;\r
+    typedef _Unit_type _NormalizedTaskRetType;\r
+\r
+    static const bool _IsAsyncTask = false;\r
+    static const bool _IsUnwrappedTaskOrAsync = false;\r
+};\r
+\r
+template<typename _Type>\r
+task<_Type> _To_task(_Type t);\r
+\r
+template<typename _Func>\r
+task<void> _To_task_void(_Func f);\r
+\r
+struct _BadContinuationParamType\r
+{\r
+};\r
+\r
+template<typename _Function, typename _Type>\r
+auto _ReturnTypeHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t)));\r
+template<typename _Function, typename _Type>\r
+auto _ReturnTypeHelper(_Type t, _Function _Func, int, ...) -> decltype(_Func(t));\r
+template<typename _Function, typename _Type>\r
+auto _ReturnTypeHelper(_Type t, _Function _Func, ...) -> _BadContinuationParamType;\r
+\r
+template<typename _Function, typename _Type>\r
+auto _IsTaskHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t)), std::true_type());\r
+template<typename _Function, typename _Type>\r
+std::false_type _IsTaskHelper(_Type t, _Function _Func, int, ...);\r
+\r
+template<typename _Function>\r
+auto _VoidReturnTypeHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func)));\r
+template<typename _Function>\r
+auto _VoidReturnTypeHelper(_Function _Func, int, ...) -> decltype(_Func());\r
+\r
+template<typename _Function>\r
+auto _VoidIsTaskHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func)), std::true_type());\r
+template<typename _Function>\r
+std::false_type _VoidIsTaskHelper(_Function _Func, int, ...);\r
+\r
+template<typename _Function, typename _ExpectedParameterType>\r
+struct _FunctionTypeTraits\r
+{\r
+    typedef decltype(\r
+        _ReturnTypeHelper(stdx::declval<_ExpectedParameterType>(), stdx::declval<_Function>(), 0, 0)) _FuncRetType;\r
+    static_assert(!std::is_same<_FuncRetType, _BadContinuationParamType>::value,\r
+                  "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or "\r
+                  "task<_ExpectedParameterType> (see below)");\r
+\r
+    typedef decltype(\r
+        _IsTaskHelper(stdx::declval<_ExpectedParameterType>(), stdx::declval<_Function>(), 0, 0)) _Takes_task;\r
+};\r
+\r
+template<typename _Function>\r
+struct _FunctionTypeTraits<_Function, void>\r
+{\r
+    typedef decltype(_VoidReturnTypeHelper(stdx::declval<_Function>(), 0, 0)) _FuncRetType;\r
+    typedef decltype(_VoidIsTaskHelper(stdx::declval<_Function>(), 0, 0)) _Takes_task;\r
+};\r
+\r
+template<typename _Function, typename _ReturnType>\r
+struct _ContinuationTypeTraits\r
+{\r
+    typedef task<\r
+        typename _TaskTypeTraits<typename _FunctionTypeTraits<_Function, _ReturnType>::_FuncRetType>::_TaskRetType>\r
+        _TaskOfType;\r
+};\r
+\r
+// _InitFunctorTypeTraits is used to decide whether a task constructed with a lambda should be unwrapped. Depending on\r
+// how the variable is declared, the constructor may or may not perform unwrapping. For eg.\r
+//\r
+//  This declaration SHOULD NOT cause unwrapping\r
+//    task<task<void>> t1([]() -> task<void> {\r
+//        task<void> t2([]() {});\r
+//        return t2;\r
+//    });\r
+//\r
+// This declaration SHOULD cause unwrapping\r
+//    task<void>> t1([]() -> task<void> {\r
+//        task<void> t2([]() {});\r
+//        return t2;\r
+//    });\r
+// If the type of the task is the same as the return type of the function, no unwrapping should take place. Else normal\r
+// rules apply.\r
+template<typename _TaskType, typename _FuncRetType>\r
+struct _InitFunctorTypeTraits\r
+{\r
+    typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind;\r
+    static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask;\r
+    static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync;\r
+};\r
+\r
+template<typename T>\r
+struct _InitFunctorTypeTraits<T, T>\r
+{\r
+    typedef _TypeSelectorNoAsync _AsyncKind;\r
+    static const bool _IsAsyncTask = false;\r
+    static const bool _IsUnwrappedTaskOrAsync = false;\r
+};\r
+\r
+/// <summary>\r
+///     Helper object used for LWT invocation.\r
+/// </summary>\r
+struct _TaskProcThunk\r
+{\r
+    _TaskProcThunk(const std::function<void()>& _Callback) : _M_func(_Callback) {}\r
+\r
+    static void _pplx_cdecl _Bridge(void* _PData)\r
+    {\r
+        _TaskProcThunk* _PThunk = reinterpret_cast<_TaskProcThunk*>(_PData);\r
+        _Holder _ThunkHolder(_PThunk);\r
+        _PThunk->_M_func();\r
+    }\r
+\r
+private:\r
+    // RAII holder\r
+    struct _Holder\r
+    {\r
+        _Holder(_TaskProcThunk* _PThunk) : _M_pThunk(_PThunk) {}\r
+\r
+        ~_Holder() { delete _M_pThunk; }\r
+\r
+        _TaskProcThunk* _M_pThunk;\r
+\r
+    private:\r
+        _Holder& operator=(const _Holder&);\r
+    };\r
+\r
+    std::function<void()> _M_func;\r
+    _TaskProcThunk& operator=(const _TaskProcThunk&);\r
+};\r
+\r
+/// <summary>\r
+///     Schedule a functor with automatic inlining. Note that this is "fire and forget" scheduling, which cannot be\r
+///     waited on or canceled after scheduling.\r
+///     This schedule method will perform automatic inlining base on <paramref value="_InliningMode"/>.\r
+/// </summary>\r
+/// <param name="_Func">\r
+///     The user functor need to be scheduled.\r
+/// </param>\r
+/// <param name="_InliningMode">\r
+///     The inlining scheduling policy for current functor.\r
+/// </param>\r
+static void _ScheduleFuncWithAutoInline(const std::function<void()>& _Func, _TaskInliningMode_t _InliningMode)\r
+{\r
+    _TaskCollection_t::_RunTask(&_TaskProcThunk::_Bridge, new _TaskProcThunk(_Func), _InliningMode);\r
+}\r
+\r
+class _ContextCallback\r
+{\r
+    typedef std::function<void(void)> _CallbackFunction;\r
+\r
+#if defined(__cplusplus_winrt)\r
+\r
+public:\r
+    static _ContextCallback _CaptureCurrent()\r
+    {\r
+        _ContextCallback _Context;\r
+        _Context._Capture();\r
+        return _Context;\r
+    }\r
+\r
+    ~_ContextCallback() { _Reset(); }\r
+\r
+    _ContextCallback(bool _DeferCapture = false)\r
+    {\r
+        if (_DeferCapture)\r
+        {\r
+            _M_context._M_captureMethod = _S_captureDeferred;\r
+        }\r
+        else\r
+        {\r
+            _M_context._M_pContextCallback = nullptr;\r
+        }\r
+    }\r
+\r
+    // Resolves a context that was created as _S_captureDeferred based on the environment (ancestor, current context).\r
+    void _Resolve(bool _CaptureCurrent)\r
+    {\r
+        if (_M_context._M_captureMethod == _S_captureDeferred)\r
+        {\r
+            _M_context._M_pContextCallback = nullptr;\r
+\r
+            if (_CaptureCurrent)\r
+            {\r
+                if (_IsCurrentOriginSTA())\r
+                {\r
+                    _Capture();\r
+                }\r
+#if _UITHREADCTXT_SUPPORT\r
+                else\r
+                {\r
+                    // This method will fail if not called from the UI thread.\r
+                    HRESULT _Hr = CaptureUiThreadContext(&_M_context._M_pContextCallback);\r
+                    if (FAILED(_Hr))\r
+                    {\r
+                        _M_context._M_pContextCallback = nullptr;\r
+                    }\r
+                }\r
+#endif /* _UITHREADCTXT_SUPPORT */\r
+            }\r
+        }\r
+    }\r
+\r
+    void _Capture()\r
+    {\r
+        HRESULT _Hr =\r
+            CoGetObjectContext(IID_IContextCallback, reinterpret_cast<void**>(&_M_context._M_pContextCallback));\r
+        if (FAILED(_Hr))\r
+        {\r
+            _M_context._M_pContextCallback = nullptr;\r
+        }\r
+    }\r
+\r
+    _ContextCallback(const _ContextCallback& _Src) { _Assign(_Src._M_context._M_pContextCallback); }\r
+\r
+    _ContextCallback(_ContextCallback&& _Src)\r
+    {\r
+        _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback;\r
+        _Src._M_context._M_pContextCallback = nullptr;\r
+    }\r
+\r
+    _ContextCallback& operator=(const _ContextCallback& _Src)\r
+    {\r
+        if (this != &_Src)\r
+        {\r
+            _Reset();\r
+            _Assign(_Src._M_context._M_pContextCallback);\r
+        }\r
+        return *this;\r
+    }\r
+\r
+    _ContextCallback& operator=(_ContextCallback&& _Src)\r
+    {\r
+        if (this != &_Src)\r
+        {\r
+            _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback;\r
+            _Src._M_context._M_pContextCallback = nullptr;\r
+        }\r
+        return *this;\r
+    }\r
+\r
+    bool _HasCapturedContext() const\r
+    {\r
+        _ASSERTE(_M_context._M_captureMethod != _S_captureDeferred);\r
+        return (_M_context._M_pContextCallback != nullptr);\r
+    }\r
+\r
+    void _CallInContext(_CallbackFunction _Func) const\r
+    {\r
+        if (!_HasCapturedContext())\r
+        {\r
+            _Func();\r
+        }\r
+        else\r
+        {\r
+            ComCallData callData;\r
+            ZeroMemory(&callData, sizeof(callData));\r
+            callData.pUserDefined = reinterpret_cast<void*>(&_Func);\r
+\r
+            HRESULT _Hr = _M_context._M_pContextCallback->ContextCallback(\r
+                &_Bridge, &callData, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr);\r
+            if (FAILED(_Hr))\r
+            {\r
+                throw ::Platform::Exception::CreateException(_Hr);\r
+            }\r
+        }\r
+    }\r
+\r
+    bool operator==(const _ContextCallback& _Rhs) const\r
+    {\r
+        return (_M_context._M_pContextCallback == _Rhs._M_context._M_pContextCallback);\r
+    }\r
+\r
+    bool operator!=(const _ContextCallback& _Rhs) const { return !(operator==(_Rhs)); }\r
+\r
+private:\r
+    void _Reset()\r
+    {\r
+        if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr)\r
+        {\r
+            _M_context._M_pContextCallback->Release();\r
+        }\r
+    }\r
+\r
+    void _Assign(IContextCallback* _PContextCallback)\r
+    {\r
+        _M_context._M_pContextCallback = _PContextCallback;\r
+        if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr)\r
+        {\r
+            _M_context._M_pContextCallback->AddRef();\r
+        }\r
+    }\r
+\r
+    static HRESULT __stdcall _Bridge(ComCallData* _PParam)\r
+    {\r
+        _CallbackFunction* pFunc = reinterpret_cast<_CallbackFunction*>(_PParam->pUserDefined);\r
+        (*pFunc)();\r
+        return S_OK;\r
+    }\r
+\r
+    // Returns the origin information for the caller (runtime / Windows Runtime apartment as far as task continuations\r
+    // need know)\r
+    static bool _IsCurrentOriginSTA()\r
+    {\r
+        APTTYPE _AptType;\r
+        APTTYPEQUALIFIER _AptTypeQualifier;\r
+\r
+        HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier);\r
+        if (SUCCEEDED(hr))\r
+        {\r
+            // We determine the origin of a task continuation by looking at where .then is called, so we can tell\r
+            // whether to need to marshal the continuation back to the originating apartment. If an STA thread is in\r
+            // executing in a neutral apartment when it schedules a continuation, we will not marshal continuations back\r
+            // to the STA, since variables used within a neutral apartment are expected to be apartment neutral.\r
+            switch (_AptType)\r
+            {\r
+                case APTTYPE_MAINSTA:\r
+                case APTTYPE_STA: return true;\r
+                default: break;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    union {\r
+        IContextCallback* _M_pContextCallback;\r
+        size_t _M_captureMethod;\r
+    } _M_context;\r
+\r
+    static const size_t _S_captureDeferred = 1;\r
+#else  /* defined (__cplusplus_winrt) */\r
+public:\r
+    static _ContextCallback _CaptureCurrent() { return _ContextCallback(); }\r
+\r
+    _ContextCallback(bool = false) {}\r
+\r
+    _ContextCallback(const _ContextCallback&) {}\r
+\r
+    _ContextCallback(_ContextCallback&&) {}\r
+\r
+    _ContextCallback& operator=(const _ContextCallback&) { return *this; }\r
+\r
+    _ContextCallback& operator=(_ContextCallback&&) { return *this; }\r
+\r
+    bool _HasCapturedContext() const { return false; }\r
+\r
+    void _Resolve(bool) const {}\r
+\r
+    void _CallInContext(_CallbackFunction _Func) const { _Func(); }\r
+\r
+    bool operator==(const _ContextCallback&) const { return true; }\r
+\r
+    bool operator!=(const _ContextCallback&) const { return false; }\r
+\r
+#endif /* defined (__cplusplus_winrt) */\r
+};\r
+\r
+template<typename _Type>\r
+struct _ResultHolder\r
+{\r
+    void Set(const _Type& _type) { _Result = _type; }\r
+\r
+    _Type Get() { return _Result; }\r
+\r
+    _Type _Result;\r
+};\r
+\r
+#if defined(__cplusplus_winrt)\r
+\r
+template<typename _Type>\r
+struct _ResultHolder<_Type ^>\r
+{\r
+    void Set(_Type ^ const& _type) { _M_Result = _type; }\r
+\r
+    _Type ^ Get() { return _M_Result.Get(); } private :\r
+        // ::Platform::Agile handle specialization of all hats\r
+        // including ::Platform::String and ::Platform::Array\r
+        ::Platform::Agile<_Type ^> _M_Result;\r
+};\r
+\r
+//\r
+// The below are for composability with tasks auto-created from when_any / when_all / && / || constructs.\r
+//\r
+template<typename _Type>\r
+struct _ResultHolder<std::vector<_Type ^>>\r
+{\r
+    void Set(const std::vector<_Type ^>& _type)\r
+    {\r
+        _Result.reserve(_type.size());\r
+\r
+        for (auto _PTask = _type.begin(); _PTask != _type.end(); ++_PTask)\r
+        {\r
+            _Result.emplace_back(*_PTask);\r
+        }\r
+    }\r
+\r
+    std::vector<_Type ^> Get()\r
+    {\r
+        // Return vectory<T^> with the objects that are marshaled in the proper apartment\r
+        std::vector<_Type ^> _Return;\r
+        _Return.reserve(_Result.size());\r
+\r
+        for (auto _PTask = _Result.begin(); _PTask != _Result.end(); ++_PTask)\r
+        {\r
+            _Return.push_back(\r
+                _PTask->Get()); // Platform::Agile will marshal the object to appropriate apartment if necessary\r
+        }\r
+\r
+        return _Return;\r
+    }\r
+\r
+    std::vector<::Platform::Agile<_Type ^>> _Result;\r
+};\r
+\r
+template<typename _Type>\r
+struct _ResultHolder<std::pair<_Type ^, void*>>\r
+{\r
+    void Set(const std::pair<_Type ^, size_t>& _type) { _M_Result = _type; }\r
+\r
+    std::pair<_Type ^, size_t> Get() { return std::make_pair(_M_Result.first.Get(), _M_Result.second); }\r
+\r
+private:\r
+    std::pair<::Platform::Agile<_Type ^>, size_t> _M_Result;\r
+};\r
+\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+// An exception thrown by the task body is captured in an exception holder and it is shared with all value based\r
+// continuations rooted at the task. The exception is 'observed' if the user invokes get()/wait() on any of the tasks\r
+// that are sharing this exception holder. If the exception is not observed by the time the internal object owned by the\r
+// shared pointer destructs, the process will fail fast.\r
+struct _ExceptionHolder\r
+{\r
+private:\r
+    void ReportUnhandledError()\r
+    {\r
+#if _MSC_VER >= 1800 && defined(__cplusplus_winrt)\r
+        if (_M_winRTException != nullptr)\r
+        {\r
+            ::Platform::Details::ReportUnhandledError(_M_winRTException);\r
+        }\r
+#endif /* defined (__cplusplus_winrt) */\r
+    }\r
+\r
+public:\r
+    explicit _ExceptionHolder(const std::exception_ptr& _E, const _TaskCreationCallstack& _stackTrace)\r
+        : _M_exceptionObserved(0)\r
+        , _M_stdException(_E)\r
+        , _M_stackTrace(_stackTrace)\r
+#if defined(__cplusplus_winrt)\r
+        , _M_winRTException(nullptr)\r
+#endif /* defined (__cplusplus_winrt) */\r
+    {\r
+    }\r
+\r
+#if defined(__cplusplus_winrt)\r
+    explicit _ExceptionHolder(::Platform::Exception ^ _E, const _TaskCreationCallstack& _stackTrace)\r
+        : _M_exceptionObserved(0), _M_winRTException(_E), _M_stackTrace(_stackTrace)\r
+    {\r
+    }\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+    __declspec(noinline) ~_ExceptionHolder()\r
+    {\r
+        if (_M_exceptionObserved == 0)\r
+        {\r
+            // If you are trapped here, it means an exception thrown in task chain didn't get handled.\r
+            // Please add task-based continuation to handle all exceptions coming from tasks.\r
+            // this->_M_stackTrace keeps the creation callstack of the task generates this exception.\r
+            _REPORT_PPLTASK_UNOBSERVED_EXCEPTION();\r
+        }\r
+    }\r
+\r
+    void _RethrowUserException()\r
+    {\r
+        if (_M_exceptionObserved == 0)\r
+        {\r
+            atomic_exchange(_M_exceptionObserved, 1l);\r
+        }\r
+\r
+#if defined(__cplusplus_winrt)\r
+        if (_M_winRTException != nullptr)\r
+        {\r
+            throw _M_winRTException;\r
+        }\r
+#endif /* defined (__cplusplus_winrt) */\r
+        std::rethrow_exception(_M_stdException);\r
+    }\r
+\r
+    // A variable that remembers if this exception was every rethrown into user code (and hence handled by the user).\r
+    // Exceptions that are unobserved when the exception holder is destructed will terminate the process.\r
+    atomic_long _M_exceptionObserved;\r
+\r
+    // Either _M_stdException or _M_winRTException is populated based on the type of exception encountered.\r
+    std::exception_ptr _M_stdException;\r
+#if defined(__cplusplus_winrt)\r
+    ::Platform::Exception ^ _M_winRTException;\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+    // Disassembling this value will point to a source instruction right after a call instruction. If the call is to\r
+    // create_task, a task constructor or the then method, the task created by that method is the one that encountered\r
+    // this exception. If the call is to task_completion_event::set_exception, the set_exception method was the source\r
+    // of the exception. DO NOT REMOVE THIS VARIABLE. It is extremely helpful for debugging.\r
+    _TaskCreationCallstack _M_stackTrace;\r
+};\r
+\r
+#if defined(__cplusplus_winrt)\r
+/// <summary>\r
+///     Base converter class for converting asynchronous interfaces to IAsyncOperation\r
+/// </summary>\r
+template<typename _AsyncOperationType, typename _CompletionHandlerType, typename _Result>\r
+ref struct _AsyncInfoImpl abstract : Windows::Foundation::IAsyncOperation<_Result>\r
+{\r
+    internal :\r
+        // The async action, action with progress or operation with progress that this stub forwards to.\r
+        ::Platform::Agile<_AsyncOperationType>\r
+            _M_asyncInfo;\r
+\r
+    Windows::Foundation::AsyncOperationCompletedHandler<_Result> ^ _M_CompletedHandler;\r
+\r
+    _AsyncInfoImpl(_AsyncOperationType _AsyncInfo) : _M_asyncInfo(_AsyncInfo) {}\r
+\r
+public:\r
+    virtual void Cancel() { _M_asyncInfo.Get()->Cancel(); }\r
+    virtual void Close() { _M_asyncInfo.Get()->Close(); }\r
+\r
+    virtual property Windows::Foundation::HResult ErrorCode\r
+    {\r
+        Windows::Foundation::HResult get() { return _M_asyncInfo.Get()->ErrorCode; }\r
+    }\r
+\r
+    virtual property UINT Id\r
+    {\r
+        UINT get() { return _M_asyncInfo.Get()->Id; }\r
+    }\r
+\r
+    virtual property Windows::Foundation::AsyncStatus Status\r
+    {\r
+        Windows::Foundation::AsyncStatus get() { return _M_asyncInfo.Get()->Status; }\r
+    }\r
+\r
+    virtual _Result GetResults() { throw std::runtime_error("derived class must implement"); }\r
+\r
+    virtual property Windows::Foundation::AsyncOperationCompletedHandler<_Result> ^ Completed {\r
+        Windows::Foundation::AsyncOperationCompletedHandler<_Result> ^ get() { return _M_CompletedHandler; }\r
+\r
+            void set(Windows::Foundation::AsyncOperationCompletedHandler<_Result> ^ value)\r
+        {\r
+            _M_CompletedHandler = value;\r
+            _M_asyncInfo.Get()->Completed =\r
+                ref new _CompletionHandlerType([&](_AsyncOperationType, Windows::Foundation::AsyncStatus status) {\r
+                    _M_CompletedHandler->Invoke(this, status);\r
+                });\r
+        }\r
+    }\r
+};\r
+\r
+/// <summary>\r
+///     Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of\r
+///     IAsyncOperationWithProgress<T> into IAsyncOperation<T>\r
+/// </summary>\r
+template<typename _Result, typename _Progress>\r
+ref struct _IAsyncOperationWithProgressToAsyncOperationConverter sealed\r
+    : _AsyncInfoImpl<Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress> ^\r
+                     , Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result, _Progress>, _Result>\r
+{\r
+    internal : _IAsyncOperationWithProgressToAsyncOperationConverter(\r
+                   Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress> ^ _Operation)\r
+        : _AsyncInfoImpl<Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress> ^,\r
+                         Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result, _Progress>,\r
+                         _Result>(_Operation)\r
+    {\r
+    }\r
+\r
+public:\r
+    virtual _Result GetResults() override { return _M_asyncInfo.Get()->GetResults(); }\r
+};\r
+\r
+/// <summary>\r
+///     Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into\r
+///     IAsyncOperation<_Unit_type>\r
+/// </summary>\r
+ref struct _IAsyncActionToAsyncOperationConverter sealed\r
+    : _AsyncInfoImpl<Windows::Foundation::IAsyncAction ^\r
+                     , Windows::Foundation::AsyncActionCompletedHandler, details::_Unit_type>\r
+{\r
+    internal : _IAsyncActionToAsyncOperationConverter(Windows::Foundation::IAsyncAction ^ _Operation)\r
+        : _AsyncInfoImpl<Windows::Foundation::IAsyncAction ^\r
+                         , Windows::Foundation::AsyncActionCompletedHandler, details::_Unit_type>(_Operation)\r
+    {\r
+    }\r
+\r
+public:\r
+    virtual details::_Unit_type GetResults() override\r
+    {\r
+        // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a\r
+        // dummy value.\r
+        _M_asyncInfo.Get()->GetResults();\r
+        return details::_Unit_type();\r
+    }\r
+};\r
+\r
+/// <summary>\r
+///     Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of\r
+///     IAsyncActionWithProgress into IAsyncOperation<_Unit_type>\r
+/// </summary>\r
+template<typename _Progress>\r
+ref struct _IAsyncActionWithProgressToAsyncOperationConverter sealed\r
+    : _AsyncInfoImpl<Windows::Foundation::IAsyncActionWithProgress<_Progress> ^\r
+                     , Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, details::_Unit_type>\r
+{\r
+    internal\r
+        : _IAsyncActionWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncActionWithProgress<_Progress> ^\r
+                                                             _Action)\r
+        : _AsyncInfoImpl<Windows::Foundation::IAsyncActionWithProgress<_Progress> ^,\r
+                         Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>,\r
+                         details::_Unit_type>(_Action)\r
+    {\r
+    }\r
+\r
+public:\r
+    virtual details::_Unit_type GetResults() override\r
+    {\r
+        // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy\r
+        // value.\r
+        _M_asyncInfo.Get()->GetResults();\r
+        return details::_Unit_type();\r
+    }\r
+};\r
+#endif /* defined (__cplusplus_winrt) */\r
+} // namespace details\r
+\r
+/// <summary>\r
+///     The <c>task_continuation_context</c> class allows you to specify where you would like a continuation to be\r
+///     executed. It is only useful to use this class from a Windows Store app. For non-Windows Store apps, the task\r
+///     continuation's execution context is determined by the runtime, and not configurable.\r
+/// </summary>\r
+/// <seealso cref="task Class"/>\r
+/**/\r
+class task_continuation_context : public details::_ContextCallback\r
+{\r
+public:\r
+    /// <summary>\r
+    ///     Creates the default task continuation context.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     The default continuation context.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     The default context is used if you don't specify a continuation context when you call the <c>then</c>\r
+    ///     method. In Windows applications for Windows 7 and below, as well as desktop applications on Windows 8 and\r
+    ///     higher, the runtime determines where task continuations will execute. However, in a Windows Store app, the\r
+    ///     default continuation context for a continuation on an apartment aware task is the apartment where\r
+    ///     <c>then</c> is invoked. <para>An apartment aware task is a task that unwraps a Windows Runtime\r
+    ///     <c>IAsyncInfo</c> interface, or a task that is descended from such a task. Therefore, if you schedule a\r
+    ///     continuation on an apartment aware task in a Windows Runtime STA, the continuation will execute in that\r
+    ///     STA.</para> <para>A continuation on a non-apartment aware task will execute in a context the Runtime\r
+    ///     chooses.</para>\r
+    /// </remarks>\r
+    /**/\r
+    static task_continuation_context use_default()\r
+    {\r
+#if defined(__cplusplus_winrt)\r
+        // The callback context is created with the context set to CaptureDeferred and resolved when it is used in\r
+        // .then()\r
+        return task_continuation_context(\r
+            true); // sets it to deferred, is resolved in the constructor of _ContinuationTaskHandle\r
+#else  /* defined (__cplusplus_winrt) */\r
+        return task_continuation_context();\r
+#endif /* defined (__cplusplus_winrt) */\r
+    }\r
+\r
+#if defined(__cplusplus_winrt)\r
+    /// <summary>\r
+    ///     Creates a task continuation context which allows the Runtime to choose the execution context for a\r
+    ///     continuation.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     A task continuation context that represents an arbitrary location.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     When this continuation context is used the continuation will execute in a context the runtime chooses even\r
+    ///     if the antecedent task is apartment aware. <para><c>use_arbitrary</c> can be used to turn off the default\r
+    ///     behavior for a continuation on an apartment aware task created in an STA. </para> <para>This method is only\r
+    ///     available to Windows Store apps.</para>\r
+    /// </remarks>\r
+    /**/\r
+    static task_continuation_context use_arbitrary()\r
+    {\r
+        task_continuation_context _Arbitrary(true);\r
+        _Arbitrary._Resolve(false);\r
+        return _Arbitrary;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Returns a task continuation context object that represents the current execution context.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     The current execution context.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     This method captures the caller's Windows Runtime context so that continuations can be executed in the right\r
+    ///     apartment. <para>The value returned by <c>use_current</c> can be used to indicate to the Runtime that the\r
+    ///     continuation should execute in the captured context (STA vs MTA) regardless of whether or not the antecedent\r
+    ///     task is apartment aware. An apartment aware task is a task that unwraps a Windows Runtime <c>IAsyncInfo</c>\r
+    ///     interface, or a task that is descended from such a task. </para> <para>This method is only available to\r
+    ///     Windows Store apps.</para>\r
+    /// </remarks>\r
+    /**/\r
+    static task_continuation_context use_current()\r
+    {\r
+        task_continuation_context _Current(true);\r
+        _Current._Resolve(true);\r
+        return _Current;\r
+    }\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+private:\r
+    task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) {}\r
+};\r
+\r
+class task_options;\r
+namespace details\r
+{\r
+struct _Internal_task_options\r
+{\r
+    bool _M_hasPresetCreationCallstack;\r
+    _TaskCreationCallstack _M_presetCreationCallstack;\r
+\r
+    void _set_creation_callstack(const _TaskCreationCallstack& _callstack)\r
+    {\r
+        _M_hasPresetCreationCallstack = true;\r
+        _M_presetCreationCallstack = _callstack;\r
+    }\r
+    _Internal_task_options() { _M_hasPresetCreationCallstack = false; }\r
+};\r
+\r
+inline _Internal_task_options& _get_internal_task_options(task_options& options);\r
+inline const _Internal_task_options& _get_internal_task_options(const task_options& options);\r
+} // namespace details\r
+/// <summary>\r
+///     Represents the allowed options for creating a task\r
+/// </summary>\r
+class task_options\r
+{\r
+public:\r
+    /// <summary>\r
+    ///     Default list of task creation options\r
+    /// </summary>\r
+    task_options()\r
+        : _M_Scheduler(get_ambient_scheduler())\r
+        , _M_CancellationToken(cancellation_token::none())\r
+        , _M_ContinuationContext(task_continuation_context::use_default())\r
+        , _M_HasCancellationToken(false)\r
+        , _M_HasScheduler(false)\r
+    {\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Task option that specify a cancellation token\r
+    /// </summary>\r
+    task_options(cancellation_token _Token)\r
+        : _M_Scheduler(get_ambient_scheduler())\r
+        , _M_CancellationToken(_Token)\r
+        , _M_ContinuationContext(task_continuation_context::use_default())\r
+        , _M_HasCancellationToken(true)\r
+        , _M_HasScheduler(false)\r
+    {\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Task option that specify a continuation context. This is valid only for continuations (then)\r
+    /// </summary>\r
+    task_options(task_continuation_context _ContinuationContext)\r
+        : _M_Scheduler(get_ambient_scheduler())\r
+        , _M_CancellationToken(cancellation_token::none())\r
+        , _M_ContinuationContext(_ContinuationContext)\r
+        , _M_HasCancellationToken(false)\r
+        , _M_HasScheduler(false)\r
+    {\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Task option that specify a cancellation token and a continuation context. This is valid only for\r
+    ///     continuations (then)\r
+    /// </summary>\r
+    task_options(cancellation_token _Token, task_continuation_context _ContinuationContext)\r
+        : _M_Scheduler(get_ambient_scheduler())\r
+        , _M_CancellationToken(_Token)\r
+        , _M_ContinuationContext(_ContinuationContext)\r
+        , _M_HasCancellationToken(false)\r
+        , _M_HasScheduler(false)\r
+    {\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Task option that specify a scheduler with shared lifetime\r
+    /// </summary>\r
+    template<typename _SchedType>\r
+    task_options(std::shared_ptr<_SchedType> _Scheduler)\r
+        : _M_Scheduler(std::move(_Scheduler))\r
+        , _M_CancellationToken(cancellation_token::none())\r
+        , _M_ContinuationContext(task_continuation_context::use_default())\r
+        , _M_HasCancellationToken(false)\r
+        , _M_HasScheduler(true)\r
+    {\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Task option that specify a scheduler reference\r
+    /// </summary>\r
+    task_options(scheduler_interface& _Scheduler)\r
+        : _M_Scheduler(&_Scheduler)\r
+        , _M_CancellationToken(cancellation_token::none())\r
+        , _M_ContinuationContext(task_continuation_context::use_default())\r
+        , _M_HasCancellationToken(false)\r
+        , _M_HasScheduler(true)\r
+    {\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Task option that specify a scheduler\r
+    /// </summary>\r
+    task_options(scheduler_ptr _Scheduler)\r
+        : _M_Scheduler(std::move(_Scheduler))\r
+        , _M_CancellationToken(cancellation_token::none())\r
+        , _M_ContinuationContext(task_continuation_context::use_default())\r
+        , _M_HasCancellationToken(false)\r
+        , _M_HasScheduler(true)\r
+    {\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Task option copy constructor\r
+    /// </summary>\r
+    task_options(const task_options& _TaskOptions)\r
+        : _M_Scheduler(_TaskOptions.get_scheduler())\r
+        , _M_CancellationToken(_TaskOptions.get_cancellation_token())\r
+        , _M_ContinuationContext(_TaskOptions.get_continuation_context())\r
+        , _M_HasCancellationToken(_TaskOptions.has_cancellation_token())\r
+        , _M_HasScheduler(_TaskOptions.has_scheduler())\r
+    {\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Sets the given token in the options\r
+    /// </summary>\r
+    void set_cancellation_token(cancellation_token _Token)\r
+    {\r
+        _M_CancellationToken = _Token;\r
+        _M_HasCancellationToken = true;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Sets the given continuation context in the options\r
+    /// </summary>\r
+    void set_continuation_context(task_continuation_context _ContinuationContext)\r
+    {\r
+        _M_ContinuationContext = _ContinuationContext;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Indicates whether a cancellation token was specified by the user\r
+    /// </summary>\r
+    bool has_cancellation_token() const { return _M_HasCancellationToken; }\r
+\r
+    /// <summary>\r
+    ///     Returns the cancellation token\r
+    /// </summary>\r
+    cancellation_token get_cancellation_token() const { return _M_CancellationToken; }\r
+\r
+    /// <summary>\r
+    ///     Returns the continuation context\r
+    /// </summary>\r
+    task_continuation_context get_continuation_context() const { return _M_ContinuationContext; }\r
+\r
+    /// <summary>\r
+    ///     Indicates whether a scheduler n was specified by the user\r
+    /// </summary>\r
+    bool has_scheduler() const { return _M_HasScheduler; }\r
+\r
+    /// <summary>\r
+    ///     Returns the scheduler\r
+    /// </summary>\r
+    scheduler_ptr get_scheduler() const { return _M_Scheduler; }\r
+\r
+private:\r
+    task_options const& operator=(task_options const& _Right);\r
+    friend details::_Internal_task_options& details::_get_internal_task_options(task_options&);\r
+    friend const details::_Internal_task_options& details::_get_internal_task_options(const task_options&);\r
+\r
+    scheduler_ptr _M_Scheduler;\r
+    cancellation_token _M_CancellationToken;\r
+    task_continuation_context _M_ContinuationContext;\r
+    details::_Internal_task_options _M_InternalTaskOptions;\r
+    bool _M_HasCancellationToken;\r
+    bool _M_HasScheduler;\r
+};\r
+\r
+namespace details\r
+{\r
+inline _Internal_task_options& _get_internal_task_options(task_options& options)\r
+{\r
+    return options._M_InternalTaskOptions;\r
+}\r
+inline const _Internal_task_options& _get_internal_task_options(const task_options& options)\r
+{\r
+    return options._M_InternalTaskOptions;\r
+}\r
+\r
+struct _Task_impl_base;\r
+template<typename _ReturnType>\r
+struct _Task_impl;\r
+\r
+template<typename _ReturnType>\r
+struct _Task_ptr\r
+{\r
+    typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type;\r
+    static _Type _Make(_CancellationTokenState* _Ct, scheduler_ptr _Scheduler_arg)\r
+    {\r
+        return std::make_shared<_Task_impl<_ReturnType>>(_Ct, _Scheduler_arg);\r
+    }\r
+};\r
+\r
+typedef _TaskCollection_t::_TaskProcHandle_t _UnrealizedChore_t;\r
+typedef std::shared_ptr<_Task_impl_base> _Task_ptr_base;\r
+\r
+// The weak-typed base task handler for continuation tasks.\r
+struct _ContinuationTaskHandleBase : _UnrealizedChore_t\r
+{\r
+    _ContinuationTaskHandleBase* _M_next;\r
+    task_continuation_context _M_continuationContext;\r
+    bool _M_isTaskBasedContinuation;\r
+\r
+    // This field gives inlining scheduling policy for current chore.\r
+    _TaskInliningMode_t _M_inliningMode;\r
+\r
+    virtual _Task_ptr_base _GetTaskImplBase() const = 0;\r
+\r
+    _ContinuationTaskHandleBase()\r
+        : _M_next(nullptr)\r
+        , _M_continuationContext(task_continuation_context::use_default())\r
+        , _M_isTaskBasedContinuation(false)\r
+        , _M_inliningMode(details::_NoInline)\r
+    {\r
+    }\r
+\r
+    virtual ~_ContinuationTaskHandleBase() {}\r
+};\r
+\r
+#if PPLX_TASK_ASYNC_LOGGING\r
+// GUID used for identifying causality logs from PPLTask\r
+const ::Platform::Guid _PPLTaskCausalityPlatformID(\r
+    0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE);\r
+\r
+__declspec(selectany) volatile long _isCausalitySupported = 0;\r
+\r
+inline bool _IsCausalitySupported()\r
+{\r
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)\r
+    if (_isCausalitySupported == 0)\r
+    {\r
+        long _causality = 1;\r
+        OSVERSIONINFOEX _osvi = {};\r
+        _osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
+\r
+        // The Causality is supported on Windows version higher than Windows 8\r
+        _osvi.dwMajorVersion = 6;\r
+        _osvi.dwMinorVersion = 3;\r
+\r
+        DWORDLONG _conditionMask = 0;\r
+        VER_SET_CONDITION(_conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);\r
+        VER_SET_CONDITION(_conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);\r
+\r
+        if (::VerifyVersionInfo(&_osvi, VER_MAJORVERSION | VER_MINORVERSION, _conditionMask))\r
+        {\r
+            _causality = 2;\r
+        }\r
+\r
+        _isCausalitySupported = _causality;\r
+        return _causality == 2;\r
+    }\r
+\r
+    return _isCausalitySupported == 2 ? true : false;\r
+#else\r
+    return true;\r
+#endif\r
+}\r
+\r
+// Stateful logger rests inside task_impl_base.\r
+struct _TaskEventLogger\r
+{\r
+    _Task_impl_base* _M_task;\r
+    bool _M_scheduled;\r
+    bool _M_taskPostEventStarted;\r
+\r
+    // Log before scheduling task\r
+    void _LogScheduleTask(bool _isContinuation)\r
+    {\r
+        if (details::_IsCausalitySupported())\r
+        {\r
+            ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCreation(\r
+                ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,\r
+                ::Windows::Foundation::Diagnostics::CausalitySource::Library,\r
+                _PPLTaskCausalityPlatformID,\r
+                reinterpret_cast<unsigned long long>(_M_task),\r
+                _isContinuation ? "pplx::PPLTask::ScheduleContinuationTask" : "pplx::PPLTask::ScheduleTask",\r
+                0);\r
+            _M_scheduled = true;\r
+        }\r
+    }\r
+\r
+    // It will log the cancel event but not canceled state. _LogTaskCompleted will log the terminal state, which\r
+    // includes cancel state.\r
+    void _LogCancelTask()\r
+    {\r
+        if (details::_IsCausalitySupported())\r
+        {\r
+            ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationRelation(\r
+                ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Important,\r
+                ::Windows::Foundation::Diagnostics::CausalitySource::Library,\r
+                _PPLTaskCausalityPlatformID,\r
+                reinterpret_cast<unsigned long long>(_M_task),\r
+                ::Windows::Foundation::Diagnostics::CausalityRelation::Cancel);\r
+        }\r
+    }\r
+\r
+    // Log when task reaches terminal state. Note: the task can reach a terminal state (by cancellation or exception)\r
+    // without having run\r
+    void _LogTaskCompleted();\r
+\r
+    // Log when task body (which includes user lambda and other scheduling code) begin to run\r
+    void _LogTaskExecutionStarted() {}\r
+\r
+    // Log when task body finish executing\r
+    void _LogTaskExecutionCompleted()\r
+    {\r
+        if (_M_taskPostEventStarted && details::_IsCausalitySupported())\r
+        {\r
+            ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(\r
+                ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,\r
+                ::Windows::Foundation::Diagnostics::CausalitySource::Library,\r
+                ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification);\r
+        }\r
+    }\r
+\r
+    // Log right before user lambda being invoked\r
+    void _LogWorkItemStarted()\r
+    {\r
+        if (details::_IsCausalitySupported())\r
+        {\r
+            ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(\r
+                ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,\r
+                ::Windows::Foundation::Diagnostics::CausalitySource::Library,\r
+                _PPLTaskCausalityPlatformID,\r
+                reinterpret_cast<unsigned long long>(_M_task),\r
+                ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution);\r
+        }\r
+    }\r
+\r
+    // Log right after user lambda being invoked\r
+    void _LogWorkItemCompleted()\r
+    {\r
+        if (details::_IsCausalitySupported())\r
+        {\r
+            ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(\r
+                ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,\r
+                ::Windows::Foundation::Diagnostics::CausalitySource::Library,\r
+                ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution);\r
+\r
+            ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(\r
+                ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,\r
+                ::Windows::Foundation::Diagnostics::CausalitySource::Library,\r
+                _PPLTaskCausalityPlatformID,\r
+                reinterpret_cast<unsigned long long>(_M_task),\r
+                ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification);\r
+            _M_taskPostEventStarted = true;\r
+        }\r
+    }\r
+\r
+    _TaskEventLogger(_Task_impl_base* _task) : _M_task(_task)\r
+    {\r
+        _M_scheduled = false;\r
+        _M_taskPostEventStarted = false;\r
+    }\r
+};\r
+\r
+// Exception safe logger for user lambda\r
+struct _TaskWorkItemRAIILogger\r
+{\r
+    _TaskEventLogger& _M_logger;\r
+    _TaskWorkItemRAIILogger(_TaskEventLogger& _taskHandleLogger) : _M_logger(_taskHandleLogger)\r
+    {\r
+        _M_logger._LogWorkItemStarted();\r
+    }\r
+\r
+    ~_TaskWorkItemRAIILogger() { _M_logger._LogWorkItemCompleted(); }\r
+    _TaskWorkItemRAIILogger& operator=(const _TaskWorkItemRAIILogger&); // cannot be assigned\r
+};\r
+\r
+#else\r
+inline void _LogCancelTask(_Task_impl_base*) {}\r
+struct _TaskEventLogger\r
+{\r
+    void _LogScheduleTask(bool) {}\r
+    void _LogCancelTask() {}\r
+    void _LogWorkItemStarted() {}\r
+    void _LogWorkItemCompleted() {}\r
+    void _LogTaskExecutionStarted() {}\r
+    void _LogTaskExecutionCompleted() {}\r
+    void _LogTaskCompleted() {}\r
+    _TaskEventLogger(_Task_impl_base*) {}\r
+};\r
+struct _TaskWorkItemRAIILogger\r
+{\r
+    _TaskWorkItemRAIILogger(_TaskEventLogger&) {}\r
+};\r
+#endif\r
+\r
+/// <summary>\r
+///     The _PPLTaskHandle is the strong-typed task handle base. All user task functions need to be wrapped in this task\r
+///     handler to be executable by PPL. By deriving from a different _BaseTaskHandle, it can be used for both initial\r
+///     tasks and continuation tasks. For initial tasks, _PPLTaskHandle will be derived from _UnrealizedChore_t, and for\r
+///     continuation tasks, it will be derived from _ContinuationTaskHandleBase. The life time of the _PPLTaskHandle\r
+///     object is be managed by runtime if task handle is scheduled.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The result type of the _Task_impl.\r
+/// </typeparam>\r
+/// <typeparam name="_DerivedTaskHandle">\r
+///     The derived task handle class. The <c>operator ()</c> needs to be implemented.\r
+/// </typeparam>\r
+/// <typeparam name="_BaseTaskHandle">\r
+///     The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore_t or\r
+///     _ContinuationTaskHandleBase.\r
+/// </typeparam>\r
+template<typename _ReturnType, typename _DerivedTaskHandle, typename _BaseTaskHandle>\r
+struct _PPLTaskHandle : _BaseTaskHandle\r
+{\r
+    _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type& _PTask) : _M_pTask(_PTask) {}\r
+\r
+    virtual ~_PPLTaskHandle()\r
+    {\r
+        // Here is the sink of all task completion code paths\r
+        _M_pTask->_M_taskEventLogger._LogTaskCompleted();\r
+    }\r
+\r
+    virtual void invoke() const\r
+    {\r
+        // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled\r
+        // by the runtime.\r
+        _ASSERTE((bool)_M_pTask);\r
+        if (!_M_pTask->_TransitionedToStarted())\r
+        {\r
+            static_cast<const _DerivedTaskHandle*>(this)->_SyncCancelAndPropagateException();\r
+            return;\r
+        }\r
+\r
+        _M_pTask->_M_taskEventLogger._LogTaskExecutionStarted();\r
+        try\r
+        {\r
+            // All derived task handle must implement this contract function.\r
+            static_cast<const _DerivedTaskHandle*>(this)->_Perform();\r
+        }\r
+        catch (const task_canceled&)\r
+        {\r
+            _M_pTask->_Cancel(true);\r
+        }\r
+        catch (const _Interruption_exception&)\r
+        {\r
+            _M_pTask->_Cancel(true);\r
+        }\r
+#if defined(__cplusplus_winrt)\r
+        catch (::Platform::Exception ^ _E)\r
+        {\r
+            _M_pTask->_CancelWithException(_E);\r
+        }\r
+#endif /* defined (__cplusplus_winrt) */\r
+        catch (...)\r
+        {\r
+            _M_pTask->_CancelWithException(std::current_exception());\r
+        }\r
+        _M_pTask->_M_taskEventLogger._LogTaskExecutionCompleted();\r
+    }\r
+\r
+    // Cast _M_pTask pointer to "type-less" _Task_impl_base pointer, which can be used in _ContinuationTaskHandleBase.\r
+    // The return value should be automatically optimized by R-value ref.\r
+    _Task_ptr_base _GetTaskImplBase() const { return _M_pTask; }\r
+\r
+    typename _Task_ptr<_ReturnType>::_Type _M_pTask;\r
+\r
+private:\r
+    _PPLTaskHandle const& operator=(_PPLTaskHandle const&); // no assignment operator\r
+};\r
+\r
+/// <summary>\r
+///     The base implementation of a first-class task. This class contains all the non-type specific\r
+///     implementation details of the task.\r
+/// </summary>\r
+/**/\r
+struct _Task_impl_base\r
+{\r
+    enum _TaskInternalState\r
+    {\r
+        // Tracks the state of the task, rather than the task collection on which the task is scheduled\r
+        _Created,\r
+        _Started,\r
+        _PendingCancel,\r
+        _Completed,\r
+        _Canceled\r
+    };\r
+// _M_taskEventLogger - 'this' : used in base member initializer list\r
+#if defined(_MSC_VER)\r
+#pragma warning(push)\r
+#pragma warning(disable : 4355)\r
+#endif\r
+    _Task_impl_base(_CancellationTokenState* _PTokenState, scheduler_ptr _Scheduler_arg)\r
+        : _M_TaskState(_Created)\r
+        , _M_fFromAsync(false)\r
+        , _M_fUnwrappedTask(false)\r
+        , _M_pRegistration(nullptr)\r
+        , _M_Continuations(nullptr)\r
+        , _M_TaskCollection(_Scheduler_arg)\r
+        , _M_taskEventLogger(this)\r
+    {\r
+        // Set cancellation token\r
+        _M_pTokenState = _PTokenState;\r
+        _ASSERTE(_M_pTokenState != nullptr);\r
+        if (_M_pTokenState != _CancellationTokenState::_None()) _M_pTokenState->_Reference();\r
+    }\r
+#if defined(_MSC_VER)\r
+#pragma warning(pop)\r
+#endif\r
+\r
+    virtual ~_Task_impl_base()\r
+    {\r
+        _ASSERTE(_M_pTokenState != nullptr);\r
+        if (_M_pTokenState != _CancellationTokenState::_None())\r
+        {\r
+            _M_pTokenState->_Release();\r
+        }\r
+    }\r
+\r
+    task_status _Wait()\r
+    {\r
+        bool _DoWait = true;\r
+\r
+#if defined(__cplusplus_winrt)\r
+        if (_IsNonBlockingThread())\r
+        {\r
+            // In order to prevent Windows Runtime STA threads from blocking the UI, calling task.wait() task.get() is\r
+            // illegal if task has not been completed.\r
+            if (!_IsCompleted() && !_IsCanceled())\r
+            {\r
+                throw invalid_operation("Illegal to wait on a task in a Windows Runtime STA");\r
+            }\r
+            else\r
+            {\r
+                // Task Continuations are 'scheduled' *inside* the chore that is executing on the ancestors's task\r
+                // group. If a continuation needs to be marshaled to a different apartment, instead of scheduling, we\r
+                // make a synchronous cross apartment COM call to execute the continuation. If it then happens to do\r
+                // something which waits on the ancestor (say it calls .get(), which task based continuations are wont\r
+                // to do), waiting on the task group results in on the chore that is making this synchronous callback,\r
+                // which causes a deadlock. To avoid this, we test the state ancestor's event , and we will NOT wait on\r
+                // if it has finished execution (which means now we are on the inline synchronous callback).\r
+                _DoWait = false;\r
+            }\r
+        }\r
+#endif /* defined (__cplusplus_winrt) */\r
+        if (_DoWait)\r
+        {\r
+            // If this task was created from a Windows Runtime async operation, do not attempt to inline it. The\r
+            // async operation will take place on a thread in the appropriate apartment Simply wait for the completed\r
+            // event to be set.\r
+            if (_M_fFromAsync)\r
+            {\r
+                _M_TaskCollection._Wait();\r
+            }\r
+            else\r
+            {\r
+                // Wait on the task collection to complete. The task collection is guaranteed to still be\r
+                // valid since the task must be still within scope so that the _Task_impl_base destructor\r
+                // has not yet been called. This call to _Wait potentially inlines execution of work.\r
+                try\r
+                {\r
+                    // Invoking wait on a task collection resets the state of the task collection. This means that\r
+                    // if the task collection itself were canceled, or had encountered an exception, only the first\r
+                    // call to wait will receive this status. However, both cancellation and exceptions flowing through\r
+                    // tasks set state in the task impl itself.\r
+\r
+                    // When it returns canceled, either work chore or the cancel thread should already have set task's\r
+                    // state properly -- canceled state or completed state (because there was no interruption point).\r
+                    // For tasks with unwrapped tasks, we should not change the state of current task, since the\r
+                    // unwrapped task are still running.\r
+                    _M_TaskCollection._RunAndWait();\r
+                }\r
+                catch (details::_Interruption_exception&)\r
+                {\r
+                    // The _TaskCollection will never be an interruption point since it has a none token.\r
+                    _ASSERTE(false);\r
+                }\r
+                catch (task_canceled&)\r
+                {\r
+                    // task_canceled is a special exception thrown by cancel_current_task. The spec states that\r
+                    // cancel_current_task must be called from code that is executed within the task (throwing it from\r
+                    // parallel work created by and waited upon by the task is acceptable). We can safely assume that\r
+                    // the task wrapper _PPLTaskHandle::operator() has seen the exception and canceled the task. Swallow\r
+                    // the exception here.\r
+                    _ASSERTE(_IsCanceled());\r
+                }\r
+#if defined(__cplusplus_winrt)\r
+                catch (::Platform::Exception ^ _E)\r
+                {\r
+                    // Its possible the task body hasn't seen the exception, if so we need to cancel with exception\r
+                    // here.\r
+                    if (!_HasUserException())\r
+                    {\r
+                        _CancelWithException(_E);\r
+                    }\r
+                    // Rethrow will mark the exception as observed.\r
+                    _M_exceptionHolder->_RethrowUserException();\r
+                }\r
+#endif /* defined (__cplusplus_winrt) */\r
+                catch (...)\r
+                {\r
+                    // Its possible the task body hasn't seen the exception, if so we need to cancel with exception\r
+                    // here.\r
+                    if (!_HasUserException())\r
+                    {\r
+                        _CancelWithException(std::current_exception());\r
+                    }\r
+                    // Rethrow will mark the exception as observed.\r
+                    _M_exceptionHolder->_RethrowUserException();\r
+                }\r
+\r
+                // If the lambda body for this task (executed or waited upon in _RunAndWait above) happened to return a\r
+                // task which is to be unwrapped and plumbed to the output of this task, we must not only wait on the\r
+                // lambda body, we must wait on the **INNER** body. It is in theory possible that we could inline such\r
+                // if we plumb a series of things through; however, this takes the tact of simply waiting upon the\r
+                // completion signal.\r
+                if (_M_fUnwrappedTask)\r
+                {\r
+                    _M_TaskCollection._Wait();\r
+                }\r
+            }\r
+        }\r
+\r
+        if (_HasUserException())\r
+        {\r
+            _M_exceptionHolder->_RethrowUserException();\r
+        }\r
+        else if (_IsCanceled())\r
+        {\r
+            return canceled;\r
+        }\r
+        _ASSERTE(_IsCompleted());\r
+        return completed;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal\r
+    ///     state.\r
+    /// </summary>\r
+    /// <param name="_SynchronousCancel">\r
+    ///     Set to true if the cancel takes place as a result of the task body encountering an exception, or because an\r
+    ///     ancestor or task_completion_event the task was registered with were canceled with an exception. A\r
+    ///     synchronous cancel is one that assures the task could not be running on a different thread at the time the\r
+    ///     cancellation is in progress. An asynchronous cancel is one where the thread performing the cancel has no\r
+    ///     control over the thread that could be executing the task, that is the task could execute concurrently while\r
+    ///     the cancellation is in progress.\r
+    /// </param>\r
+    /// <param name="_UserException">\r
+    ///     Whether an exception other than the internal runtime cancellation exceptions caused this cancellation.\r
+    /// </param>\r
+    /// <param name="_PropagatedFromAncestor">\r
+    ///     Whether this exception came from an ancestor task or a task_completion_event as opposed to an exception that\r
+    ///     was encountered by the task itself. Only valid when _UserException is set to true.\r
+    /// </param>\r
+    /// <param name="_ExHolder">\r
+    ///     The exception holder that represents the exception. Only valid when _UserException is set to true.\r
+    /// </param>\r
+    virtual bool _CancelAndRunContinuations(bool _SynchronousCancel,\r
+                                            bool _UserException,\r
+                                            bool _PropagatedFromAncestor,\r
+                                            const std::shared_ptr<_ExceptionHolder>& _ExHolder) = 0;\r
+\r
+    bool _Cancel(bool _SynchronousCancel)\r
+    {\r
+        // Send in a dummy value for exception. It is not used when the first parameter is false.\r
+        return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder);\r
+    }\r
+\r
+    bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor)\r
+    {\r
+        // This task was canceled because an ancestor task encountered an exception.\r
+        return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder);\r
+    }\r
+\r
+#if defined(__cplusplus_winrt)\r
+    bool _CancelWithException(::Platform::Exception ^ _Exception)\r
+    {\r
+        // This task was canceled because the task body encountered an exception.\r
+        _ASSERTE(!_HasUserException());\r
+        return _CancelAndRunContinuations(\r
+            true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack()));\r
+    }\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+    bool _CancelWithException(const std::exception_ptr& _Exception)\r
+    {\r
+        // This task was canceled because the task body encountered an exception.\r
+        _ASSERTE(!_HasUserException());\r
+        return _CancelAndRunContinuations(\r
+            true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack()));\r
+    }\r
+\r
+    void _RegisterCancellation(std::weak_ptr<_Task_impl_base> _WeakPtr)\r
+    {\r
+        _ASSERTE(details::_CancellationTokenState::_IsValid(_M_pTokenState));\r
+\r
+        auto _CancellationCallback = [_WeakPtr]() {\r
+            // Taking ownership of the task prevents dead lock during destruction\r
+            // if the destructor waits for the cancellations to be finished\r
+            auto _task = _WeakPtr.lock();\r
+            if (_task != nullptr) _task->_Cancel(false);\r
+        };\r
+\r
+        _M_pRegistration =\r
+            new details::_CancellationTokenCallback<decltype(_CancellationCallback)>(_CancellationCallback);\r
+        _M_pTokenState->_RegisterCallback(_M_pRegistration);\r
+    }\r
+\r
+    void _DeregisterCancellation()\r
+    {\r
+        if (_M_pRegistration != nullptr)\r
+        {\r
+            _M_pTokenState->_DeregisterCallback(_M_pRegistration);\r
+            _M_pRegistration->_Release();\r
+            _M_pRegistration = nullptr;\r
+        }\r
+    }\r
+\r
+    bool _IsCreated() { return (_M_TaskState == _Created); }\r
+\r
+    bool _IsStarted() { return (_M_TaskState == _Started); }\r
+\r
+    bool _IsPendingCancel() { return (_M_TaskState == _PendingCancel); }\r
+\r
+    bool _IsCompleted() { return (_M_TaskState == _Completed); }\r
+\r
+    bool _IsCanceled() { return (_M_TaskState == _Canceled); }\r
+\r
+    bool _HasUserException() { return static_cast<bool>(_M_exceptionHolder); }\r
+\r
+    const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder()\r
+    {\r
+        _ASSERTE(_HasUserException());\r
+        return _M_exceptionHolder;\r
+    }\r
+\r
+    bool _IsApartmentAware() { return _M_fFromAsync; }\r
+\r
+    void _SetAsync(bool _Async = true) { _M_fFromAsync = _Async; }\r
+\r
+    _TaskCreationCallstack _GetTaskCreationCallstack() { return _M_pTaskCreationCallstack; }\r
+\r
+    void _SetTaskCreationCallstack(const _TaskCreationCallstack& _Callstack) { _M_pTaskCreationCallstack = _Callstack; }\r
+\r
+    /// <summary>\r
+    ///     Helper function to schedule the task on the Task Collection.\r
+    /// </summary>\r
+    /// <param name="_PTaskHandle">\r
+    ///     The task chore handle that need to be executed.\r
+    /// </param>\r
+    /// <param name="_InliningMode">\r
+    ///     The inlining scheduling policy for current _PTaskHandle.\r
+    /// </param>\r
+    void _ScheduleTask(_UnrealizedChore_t* _PTaskHandle, _TaskInliningMode_t _InliningMode)\r
+    {\r
+        try\r
+        {\r
+            _M_TaskCollection._ScheduleTask(_PTaskHandle, _InliningMode);\r
+        }\r
+        catch (const task_canceled&)\r
+        {\r
+            // task_canceled is a special exception thrown by cancel_current_task. The spec states that\r
+            // cancel_current_task must be called from code that is executed within the task (throwing it from parallel\r
+            // work created by and waited upon by the task is acceptable). We can safely assume that the task wrapper\r
+            // _PPLTaskHandle::operator() has seen the exception and canceled the task. Swallow the exception here.\r
+            _ASSERTE(_IsCanceled());\r
+        }\r
+        catch (const _Interruption_exception&)\r
+        {\r
+            // The _TaskCollection will never be an interruption point since it has a none token.\r
+            _ASSERTE(false);\r
+        }\r
+        catch (...)\r
+        {\r
+            // The exception could have come from two places:\r
+            //   1. From the chore body, so it already should have been caught and canceled.\r
+            //      In this case swallow the exception.\r
+            //   2. From trying to actually schedule the task on the scheduler.\r
+            //      In this case cancel the task with the current exception, otherwise the\r
+            //      task will never be signaled leading to deadlock when waiting on the task.\r
+            if (!_HasUserException())\r
+            {\r
+                _CancelWithException(std::current_exception());\r
+            }\r
+        }\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Function executes a continuation. This function is recorded by a parent task implementation\r
+    ///     when a continuation is created in order to execute later.\r
+    /// </summary>\r
+    /// <param name="_PTaskHandle">\r
+    ///     The continuation task chore handle that need to be executed.\r
+    /// </param>\r
+    /**/\r
+    void _RunContinuation(_ContinuationTaskHandleBase* _PTaskHandle)\r
+    {\r
+        _Task_ptr_base _ImplBase = _PTaskHandle->_GetTaskImplBase();\r
+        if (_IsCanceled() && !_PTaskHandle->_M_isTaskBasedContinuation)\r
+        {\r
+            if (_HasUserException())\r
+            {\r
+                // If the ancestor encountered an exception, transfer the exception to the continuation\r
+                // This traverses down the tree to propagate the exception.\r
+                _ImplBase->_CancelWithExceptionHolder(_GetExceptionHolder(), true);\r
+            }\r
+            else\r
+            {\r
+                // If the ancestor was canceled, then your own execution should be canceled.\r
+                // This traverses down the tree to cancel it.\r
+                _ImplBase->_Cancel(true);\r
+            }\r
+        }\r
+        else\r
+        {\r
+            // This can only run when the ancestor has completed or it's a task based continuation that fires when a\r
+            // task is canceled (with or without a user exception).\r
+            _ASSERTE(_IsCompleted() || _PTaskHandle->_M_isTaskBasedContinuation);\r
+            _ASSERTE(!_ImplBase->_IsCanceled());\r
+            return _ImplBase->_ScheduleContinuationTask(_PTaskHandle);\r
+        }\r
+\r
+        // If the handle is not scheduled, we need to manually delete it.\r
+        delete _PTaskHandle;\r
+    }\r
+\r
+    // Schedule a continuation to run\r
+    void _ScheduleContinuationTask(_ContinuationTaskHandleBase* _PTaskHandle)\r
+    {\r
+        _M_taskEventLogger._LogScheduleTask(true);\r
+        // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a\r
+        // different Windows Runtime apartment)\r
+        if (_PTaskHandle->_M_continuationContext._HasCapturedContext())\r
+        {\r
+            // For those continuations need to be scheduled inside captured context, we will try to apply automatic\r
+            // inlining to their inline modes, if they haven't been specified as _ForceInline yet. This change will\r
+            // encourage those continuations to be executed inline so that reduce the cost of marshaling. For normal\r
+            // continuations we won't do any change here, and their inline policies are completely decided by ._ThenImpl\r
+            // method.\r
+            if (_PTaskHandle->_M_inliningMode != details::_ForceInline)\r
+            {\r
+                _PTaskHandle->_M_inliningMode = details::_DefaultAutoInline;\r
+            }\r
+            _ScheduleFuncWithAutoInline(\r
+                [_PTaskHandle]() {\r
+                    // Note that we cannot directly capture "this" pointer, instead, we should use _TaskImplPtr, a\r
+                    // shared_ptr to the _Task_impl_base. Because "this" pointer will be invalid as soon as _PTaskHandle\r
+                    // get deleted. _PTaskHandle will be deleted after being scheduled.\r
+                    auto _TaskImplPtr = _PTaskHandle->_GetTaskImplBase();\r
+                    if (details::_ContextCallback::_CaptureCurrent() == _PTaskHandle->_M_continuationContext)\r
+                    {\r
+                        _TaskImplPtr->_ScheduleTask(_PTaskHandle, details::_ForceInline);\r
+                    }\r
+                    else\r
+                    {\r
+                        //\r
+                        // It's entirely possible that the attempt to marshal the call into a differing context will\r
+                        // fail. In this case, we need to handle the exception and mark the continuation as canceled\r
+                        // with the appropriate exception. There is one slight hitch to this:\r
+                        //\r
+                        // NOTE: COM's legacy behavior is to swallow SEH exceptions and marshal them back as HRESULTS.\r
+                        // This will in effect turn an SEH into a C++ exception that gets tagged on the task. One\r
+                        // unfortunate result of this is that various pieces of the task infrastructure will not be in a\r
+                        // valid state after this in /EHsc (due to the lack of destructors running, etc...).\r
+                        //\r
+                        try\r
+                        {\r
+                            // Dev10 compiler needs this!\r
+                            auto _PTaskHandle1 = _PTaskHandle;\r
+                            _PTaskHandle->_M_continuationContext._CallInContext([_PTaskHandle1, _TaskImplPtr]() {\r
+                                _TaskImplPtr->_ScheduleTask(_PTaskHandle1, details::_ForceInline);\r
+                            });\r
+                        }\r
+#if defined(__cplusplus_winrt)\r
+                        catch (::Platform::Exception ^ _E)\r
+                        {\r
+                            _TaskImplPtr->_CancelWithException(_E);\r
+                        }\r
+#endif /* defined (__cplusplus_winrt) */\r
+                        catch (...)\r
+                        {\r
+                            _TaskImplPtr->_CancelWithException(std::current_exception());\r
+                        }\r
+                    }\r
+                },\r
+                _PTaskHandle->_M_inliningMode);\r
+        }\r
+        else\r
+        {\r
+            _ScheduleTask(_PTaskHandle, _PTaskHandle->_M_inliningMode);\r
+        }\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Schedule the actual continuation. This will either schedule the function on the continuation task's\r
+    ///     implementation if the task has completed or append it to a list of functions to execute when the task\r
+    ///     actually does complete.\r
+    /// </summary>\r
+    /// <typeparam name="_FuncInputType">\r
+    ///     The input type of the task.\r
+    /// </typeparam>\r
+    /// <typeparam name="_FuncOutputType">\r
+    ///     The output type of the task.\r
+    /// </typeparam>\r
+    /**/\r
+    void _ScheduleContinuation(_ContinuationTaskHandleBase* _PTaskHandle)\r
+    {\r
+        enum\r
+        {\r
+            _Nothing,\r
+            _Schedule,\r
+            _Cancel,\r
+            _CancelWithException\r
+        } _Do = _Nothing;\r
+\r
+        // If the task has canceled, cancel the continuation. If the task has completed, execute the continuation right\r
+        // away. Otherwise, add it to the list of pending continuations\r
+        {\r
+            ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec);\r
+            if (_IsCompleted() || (_IsCanceled() && _PTaskHandle->_M_isTaskBasedContinuation))\r
+            {\r
+                _Do = _Schedule;\r
+            }\r
+            else if (_IsCanceled())\r
+            {\r
+                if (_HasUserException())\r
+                {\r
+                    _Do = _CancelWithException;\r
+                }\r
+                else\r
+                {\r
+                    _Do = _Cancel;\r
+                }\r
+            }\r
+            else\r
+            {\r
+                // chain itself on the continuation chain.\r
+                _PTaskHandle->_M_next = _M_Continuations;\r
+                _M_Continuations = _PTaskHandle;\r
+            }\r
+        }\r
+\r
+        // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off\r
+        // of async tasks may execute inline.\r
+        switch (_Do)\r
+        {\r
+            case _Schedule:\r
+            {\r
+                _PTaskHandle->_GetTaskImplBase()->_ScheduleContinuationTask(_PTaskHandle);\r
+                break;\r
+            }\r
+            case _Cancel:\r
+            {\r
+                // If the ancestor was canceled, then your own execution should be canceled.\r
+                // This traverses down the tree to cancel it.\r
+                _PTaskHandle->_GetTaskImplBase()->_Cancel(true);\r
+\r
+                delete _PTaskHandle;\r
+                break;\r
+            }\r
+            case _CancelWithException:\r
+            {\r
+                // If the ancestor encountered an exception, transfer the exception to the continuation\r
+                // This traverses down the tree to propagate the exception.\r
+                _PTaskHandle->_GetTaskImplBase()->_CancelWithExceptionHolder(_GetExceptionHolder(), true);\r
+\r
+                delete _PTaskHandle;\r
+                break;\r
+            }\r
+            case _Nothing:\r
+            default:\r
+                // In this case, we have inserted continuation to continuation chain,\r
+                // nothing more need to be done, just leave.\r
+                break;\r
+        }\r
+    }\r
+\r
+    void _RunTaskContinuations()\r
+    {\r
+        // The link list can no longer be modified at this point,\r
+        // since all following up continuations will be scheduled by themselves.\r
+        _ContinuationList _Cur = _M_Continuations, _Next;\r
+        _M_Continuations = nullptr;\r
+        while (_Cur)\r
+        {\r
+            // Current node might be deleted after running,\r
+            // so we must fetch the next first.\r
+            _Next = _Cur->_M_next;\r
+            _RunContinuation(_Cur);\r
+            _Cur = _Next;\r
+        }\r
+    }\r
+\r
+#if defined(__cplusplus_winrt)\r
+    static bool _IsNonBlockingThread()\r
+    {\r
+        APTTYPE _AptType;\r
+        APTTYPEQUALIFIER _AptTypeQualifier;\r
+\r
+        HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier);\r
+        //\r
+        // If it failed, it's not a Windows Runtime/COM initialized thread. This is not a failure.\r
+        //\r
+        if (SUCCEEDED(hr))\r
+        {\r
+            switch (_AptType)\r
+            {\r
+                case APTTYPE_STA:\r
+                case APTTYPE_MAINSTA: return true; break;\r
+                case APTTYPE_NA:\r
+                    switch (_AptTypeQualifier)\r
+                    {\r
+                        // A thread executing in a neutral apartment is either STA or MTA. To find out if this thread is\r
+                        // allowed to wait, we check the app qualifier. If it is an STA thread executing in a neutral\r
+                        // apartment, waiting is illegal, because the thread is responsible for pumping messages and\r
+                        // waiting on a task could take the thread out of circulation for a while.\r
+                        case APTTYPEQUALIFIER_NA_ON_STA:\r
+                        case APTTYPEQUALIFIER_NA_ON_MAINSTA: return true; break;\r
+                    }\r
+                    break;\r
+            }\r
+        }\r
+\r
+#if _UITHREADCTXT_SUPPORT\r
+        // This method is used to throw an exception in _Wait() if called within STA.  We\r
+        // want the same behavior if _Wait is called on the UI thread.\r
+        if (SUCCEEDED(CaptureUiThreadContext(nullptr)))\r
+        {\r
+            return true;\r
+        }\r
+#endif /* _UITHREADCTXT_SUPPORT */\r
+\r
+        return false;\r
+    }\r
+\r
+    template<typename _ReturnType, typename>\r
+    static void _AsyncInit(\r
+        const typename _Task_ptr<_ReturnType>::_Type& _OuterTask,\r
+        Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value> ^ _AsyncOp)\r
+    {\r
+        // This method is invoked either when a task is created from an existing async operation or\r
+        // when a lambda that creates an async operation executes.\r
+\r
+        // If the outer task is pending cancel, cancel the async operation before setting the completed handler. The COM\r
+        // reference on the IAsyncInfo object will be released when all ^references to the operation go out of scope.\r
+\r
+        // This assertion uses the existence of taskcollection to determine if the task was created from an event.\r
+        // That is no longer valid as even tasks created from a user lambda could have no underlying taskcollection\r
+        // when a custom scheduler is used.\r
+        // _ASSERTE((!_OuterTask->_M_TaskCollection._IsCreated() || _OuterTask->_M_fUnwrappedTask) &&\r
+        // !_OuterTask->_IsCanceled());\r
+\r
+        // Pass the shared_ptr by value into the lambda instead of using 'this'.\r
+        _AsyncOp->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType>(\r
+            [_OuterTask](\r
+                Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value> ^\r
+                    _Operation,\r
+                Windows::Foundation::AsyncStatus _Status) mutable {\r
+                if (_Status == Windows::Foundation::AsyncStatus::Canceled)\r
+                {\r
+                    _OuterTask->_Cancel(true);\r
+                }\r
+                else if (_Status == Windows::Foundation::AsyncStatus::Error)\r
+                {\r
+                    _OuterTask->_CancelWithException(\r
+                        ::Platform::Exception::ReCreateException(static_cast<int>(_Operation->ErrorCode.Value)));\r
+                }\r
+                else\r
+                {\r
+                    _ASSERTE(_Status == Windows::Foundation::AsyncStatus::Completed);\r
+                    _OuterTask->_FinalizeAndRunContinuations(_Operation->GetResults());\r
+                }\r
+\r
+                // Take away this shared pointers reference on the task instead of waiting for the delegate to be\r
+                // released. It could be released on a different thread after a delay, and not releasing the reference\r
+                // here could cause the tasks to hold on to resources longer than they should. As an example, without\r
+                // this reset, writing to a file followed by reading from it using the Windows Runtime Async APIs causes\r
+                // a sharing violation. Using const_cast is the workaround for failed mutable keywords\r
+                const_cast<_Task_ptr<_ReturnType>::_Type&>(_OuterTask).reset();\r
+            });\r
+        _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp);\r
+    }\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+    template<typename _ReturnType, typename _InternalReturnType>\r
+    static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type& _OuterTask,\r
+                           const task<_InternalReturnType>& _UnwrappedTask)\r
+    {\r
+        _ASSERTE(_OuterTask->_M_fUnwrappedTask && !_OuterTask->_IsCanceled());\r
+\r
+        //\r
+        // We must ensure that continuations off _OuterTask (especially exception handling ones) continue to function in\r
+        // the presence of an exception flowing out of the inner task _UnwrappedTask. This requires an exception\r
+        // handling continuation off the inner task which does the appropriate funneling to the outer one. We use _Then\r
+        // instead of then to prevent the exception from being marked as observed by our internal continuation. This\r
+        // continuation must be scheduled regardless of whether or not the _OuterTask task is canceled.\r
+        //\r
+        _UnwrappedTask._Then(\r
+            [_OuterTask](task<_InternalReturnType> _AncestorTask) {\r
+                if (_AncestorTask._GetImpl()->_IsCompleted())\r
+                {\r
+                    _OuterTask->_FinalizeAndRunContinuations(_AncestorTask._GetImpl()->_GetResult());\r
+                }\r
+                else\r
+                {\r
+                    _ASSERTE(_AncestorTask._GetImpl()->_IsCanceled());\r
+                    if (_AncestorTask._GetImpl()->_HasUserException())\r
+                    {\r
+                        // Set _PropagatedFromAncestor to false, since _AncestorTask is not an ancestor of\r
+                        // _UnwrappedTask. Instead, it is the enclosing task.\r
+                        _OuterTask->_CancelWithExceptionHolder(_AncestorTask._GetImpl()->_GetExceptionHolder(), false);\r
+                    }\r
+                    else\r
+                    {\r
+                        _OuterTask->_Cancel(true);\r
+                    }\r
+                }\r
+            },\r
+            nullptr,\r
+            details::_DefaultAutoInline);\r
+    }\r
+\r
+    scheduler_ptr _GetScheduler() const { return _M_TaskCollection._GetScheduler(); }\r
+\r
+    // Tracks the internal state of the task\r
+    std::atomic<_TaskInternalState> _M_TaskState;\r
+    // Set to true either if the ancestor task had the flag set to true, or if the lambda that does the work of this\r
+    // task returns an async operation or async action that is unwrapped by the runtime.\r
+    bool _M_fFromAsync;\r
+    // Set to true when a continuation unwraps a task or async operation.\r
+    bool _M_fUnwrappedTask;\r
+\r
+    // An exception thrown by the task body is captured in an exception holder and it is shared with all value based\r
+    // continuations rooted at the task. The exception is 'observed' if the user invokes get()/wait() on any of the\r
+    // tasks that are sharing this exception holder. If the exception is not observed by the time the internal object\r
+    // owned by the shared pointer destructs, the process will fail fast.\r
+    std::shared_ptr<_ExceptionHolder> _M_exceptionHolder;\r
+\r
+    ::pplx::extensibility::critical_section_t _M_ContinuationsCritSec;\r
+\r
+    // The cancellation token state.\r
+    _CancellationTokenState* _M_pTokenState;\r
+\r
+    // The registration on the token.\r
+    _CancellationTokenRegistration* _M_pRegistration;\r
+\r
+    typedef _ContinuationTaskHandleBase* _ContinuationList;\r
+    _ContinuationList _M_Continuations;\r
+\r
+    // The async task collection wrapper\r
+    ::pplx::details::_TaskCollection_t _M_TaskCollection;\r
+\r
+    // Callstack for function call (constructor or .then) that created this task impl.\r
+    _TaskCreationCallstack _M_pTaskCreationCallstack;\r
+\r
+    _TaskEventLogger _M_taskEventLogger;\r
+\r
+private:\r
+    // Must not be copied by value:\r
+    _Task_impl_base(const _Task_impl_base&);\r
+    _Task_impl_base const& operator=(_Task_impl_base const&);\r
+};\r
+\r
+#if PPLX_TASK_ASYNC_LOGGING\r
+inline void _TaskEventLogger::_LogTaskCompleted()\r
+{\r
+    if (_M_scheduled)\r
+    {\r
+        ::Windows::Foundation::AsyncStatus _State;\r
+        if (_M_task->_IsCompleted())\r
+            _State = ::Windows::Foundation::AsyncStatus::Completed;\r
+        else if (_M_task->_HasUserException())\r
+            _State = ::Windows::Foundation::AsyncStatus::Error;\r
+        else\r
+            _State = ::Windows::Foundation::AsyncStatus::Canceled;\r
+\r
+        if (details::_IsCausalitySupported())\r
+        {\r
+            ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCompletion(\r
+                ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,\r
+                ::Windows::Foundation::Diagnostics::CausalitySource::Library,\r
+                _PPLTaskCausalityPlatformID,\r
+                reinterpret_cast<unsigned long long>(_M_task),\r
+                _State);\r
+        }\r
+    }\r
+}\r
+#endif\r
+\r
+/// <summary>\r
+///     The implementation of a first-class task. This structure contains the task group used to execute\r
+///     the task function and handles the scheduling. The _Task_impl is created as a shared_ptr\r
+///     member of the the public task class, so its destruction is handled automatically.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The result type of this task.\r
+/// </typeparam>\r
+/**/\r
+template<typename _ReturnType>\r
+struct _Task_impl : public _Task_impl_base\r
+{\r
+#if defined(__cplusplus_winrt)\r
+    typedef Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value>\r
+        _AsyncOperationType;\r
+#endif // defined(__cplusplus_winrt)\r
+    _Task_impl(_CancellationTokenState* _Ct, scheduler_ptr _Scheduler_arg) : _Task_impl_base(_Ct, _Scheduler_arg)\r
+    {\r
+#if defined(__cplusplus_winrt)\r
+        _M_unwrapped_async_op = nullptr;\r
+#endif /* defined (__cplusplus_winrt) */\r
+    }\r
+\r
+    virtual ~_Task_impl()\r
+    {\r
+        // We must invoke _DeregisterCancellation in the derived class destructor. Calling it in the base class\r
+        // destructor could cause a partially initialized _Task_impl to be in the list of registrations for a\r
+        // cancellation token.\r
+        _DeregisterCancellation();\r
+    }\r
+\r
+    virtual bool _CancelAndRunContinuations(bool _SynchronousCancel,\r
+                                            bool _UserException,\r
+                                            bool _PropagatedFromAncestor,\r
+                                            const std::shared_ptr<_ExceptionHolder>& _ExceptionHolder_arg)\r
+    {\r
+        bool _RunContinuations = false;\r
+        {\r
+            ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec);\r
+            if (_UserException)\r
+            {\r
+                _ASSERTE(_SynchronousCancel && !_IsCompleted());\r
+                // If the state is _Canceled, the exception has to be coming from an ancestor.\r
+                _ASSERTE(!_IsCanceled() || _PropagatedFromAncestor);\r
+\r
+                // We should not be canceled with an exception more than once.\r
+                _ASSERTE(!_HasUserException());\r
+\r
+                // Mark _PropagatedFromAncestor as used.\r
+                (void)_PropagatedFromAncestor;\r
+\r
+                if (_M_TaskState == _Canceled)\r
+                {\r
+                    // If the task has finished canceling there should not be any continuation records in the array.\r
+                    return false;\r
+                }\r
+                else\r
+                {\r
+                    _ASSERTE(_M_TaskState != _Completed);\r
+                    _M_exceptionHolder = _ExceptionHolder_arg;\r
+                }\r
+            }\r
+            else\r
+            {\r
+                // Completed is a non-cancellable state, and if this is an asynchronous cancel, we're unable to do\r
+                // better than the last async cancel which is to say, cancellation is already initiated, so return\r
+                // early.\r
+                if (_IsCompleted() || _IsCanceled() || (_IsPendingCancel() && !_SynchronousCancel))\r
+                {\r
+                    _ASSERTE(!_IsCompleted() || !_HasUserException());\r
+                    return false;\r
+                }\r
+                _ASSERTE(!_SynchronousCancel || !_HasUserException());\r
+            }\r
+\r
+            if (_SynchronousCancel)\r
+            {\r
+                // Be aware that this set must be done BEFORE _M_Scheduled being set, or race will happen between this\r
+                // and wait()\r
+                _M_TaskState = _Canceled;\r
+                // Cancellation completes the task, so all dependent tasks must be run to cancel them\r
+                // They are canceled when they begin running (see _RunContinuation) and see that their\r
+                // ancestor has been canceled.\r
+                _RunContinuations = true;\r
+            }\r
+            else\r
+            {\r
+                _ASSERTE(!_UserException);\r
+\r
+                if (_IsStarted())\r
+                {\r
+#if defined(__cplusplus_winrt)\r
+                    if (_M_unwrapped_async_op != nullptr)\r
+                    {\r
+                        // We will only try to cancel async operation but not unwrapped tasks, since unwrapped tasks\r
+                        // cannot be canceled without its token.\r
+                        _M_unwrapped_async_op->Cancel();\r
+                    }\r
+#endif /* defined (__cplusplus_winrt) */\r
+                    _M_TaskCollection._Cancel();\r
+                }\r
+\r
+                // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not\r
+                // executing user code anymore). In the case of a synchronous cancel, this can happen immediately,\r
+                // whereas with an asynchronous cancel, the task has to move from _Started to _PendingCancel before it\r
+                // can move to _Canceled when it is finished executing.\r
+                _M_TaskState = _PendingCancel;\r
+\r
+                _M_taskEventLogger._LogCancelTask();\r
+            }\r
+        }\r
+\r
+        // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled\r
+        // state.\r
+        if (_RunContinuations)\r
+        {\r
+            _M_TaskCollection._Complete();\r
+\r
+            if (_M_Continuations)\r
+            {\r
+                // Scheduling cancellation with automatic inlining.\r
+                _ScheduleFuncWithAutoInline([=]() { _RunTaskContinuations(); }, details::_DefaultAutoInline);\r
+            }\r
+        }\r
+        return true;\r
+    }\r
+\r
+    void _FinalizeAndRunContinuations(_ReturnType _Result)\r
+    {\r
+        _M_Result.Set(_Result);\r
+\r
+        {\r
+            //\r
+            // Hold this lock to ensure continuations being concurrently either get added\r
+            // to the _M_Continuations vector or wait for the result\r
+            //\r
+            ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec);\r
+\r
+            // A task could still be in the _Created state if it was created with a task_completion_event.\r
+            // It could also be in the _Canceled state for the same reason.\r
+            _ASSERTE(!_HasUserException() && !_IsCompleted());\r
+            if (_IsCanceled())\r
+            {\r
+                return;\r
+            }\r
+\r
+            // Always transition to "completed" state, even in the face of unacknowledged pending cancellation\r
+            _M_TaskState = _Completed;\r
+        }\r
+        _M_TaskCollection._Complete();\r
+        _RunTaskContinuations();\r
+    }\r
+\r
+    //\r
+    // This method is invoked when the starts executing. The task returns early if this method returns true.\r
+    //\r
+    bool _TransitionedToStarted()\r
+    {\r
+        ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec);\r
+        // Canceled state could only result from antecedent task's canceled state, but that code path will not reach\r
+        // here.\r
+        _ASSERTE(!_IsCanceled());\r
+        if (_IsPendingCancel()) return false;\r
+\r
+        _ASSERTE(_IsCreated());\r
+        _M_TaskState = _Started;\r
+        return true;\r
+    }\r
+\r
+#if defined(__cplusplus_winrt)\r
+    void _SetUnwrappedAsyncOp(_AsyncOperationType ^ _AsyncOp)\r
+    {\r
+        ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec);\r
+        // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it.\r
+        if (_IsPendingCancel())\r
+        {\r
+            _ASSERTE(!_IsCanceled());\r
+            _AsyncOp->Cancel();\r
+        }\r
+        else\r
+        {\r
+            _M_unwrapped_async_op = _AsyncOp;\r
+        }\r
+    }\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+    // Return true if the task has reached a terminal state\r
+    bool _IsDone() { return _IsCompleted() || _IsCanceled(); }\r
+\r
+    _ReturnType _GetResult() { return _M_Result.Get(); }\r
+\r
+    _ResultHolder<_ReturnType> _M_Result; // this means that the result type must have a public default ctor.\r
+#if defined(__cplusplus_winrt)\r
+    _AsyncOperationType ^ _M_unwrapped_async_op;\r
+#endif /* defined (__cplusplus_winrt) */\r
+};\r
+\r
+template<typename _ResultType>\r
+struct _Task_completion_event_impl\r
+{\r
+private:\r
+    _Task_completion_event_impl(const _Task_completion_event_impl&);\r
+    _Task_completion_event_impl& operator=(const _Task_completion_event_impl&);\r
+\r
+public:\r
+    typedef std::vector<typename _Task_ptr<_ResultType>::_Type> _TaskList;\r
+\r
+    _Task_completion_event_impl() : _M_fHasValue(false), _M_fIsCanceled(false) {}\r
+\r
+    bool _HasUserException() { return _M_exceptionHolder != nullptr; }\r
+\r
+    ~_Task_completion_event_impl()\r
+    {\r
+        for (auto _TaskIt = _M_tasks.begin(); _TaskIt != _M_tasks.end(); ++_TaskIt)\r
+        {\r
+            _ASSERTE(!_M_fHasValue && !_M_fIsCanceled);\r
+            // Cancel the tasks since the event was never signaled or canceled.\r
+            (*_TaskIt)->_Cancel(true);\r
+        }\r
+    }\r
+\r
+    // We need to protect the loop over the array, so concurrent_vector would not have helped\r
+    _TaskList _M_tasks;\r
+    ::pplx::extensibility::critical_section_t _M_taskListCritSec;\r
+    _ResultHolder<_ResultType> _M_value;\r
+    std::shared_ptr<_ExceptionHolder> _M_exceptionHolder;\r
+    std::atomic<bool> _M_fHasValue;\r
+    std::atomic<bool> _M_fIsCanceled;\r
+};\r
+\r
+// Utility method for dealing with void functions\r
+inline std::function<_Unit_type(void)> _MakeVoidToUnitFunc(const std::function<void(void)>& _Func)\r
+{\r
+    return [=]() -> _Unit_type {\r
+        _Func();\r
+        return _Unit_type();\r
+    };\r
+}\r
+\r
+template<typename _Type>\r
+std::function<_Type(_Unit_type)> _MakeUnitToTFunc(const std::function<_Type(void)>& _Func)\r
+{\r
+    return [=](_Unit_type) -> _Type { return _Func(); };\r
+}\r
+\r
+template<typename _Type>\r
+std::function<_Unit_type(_Type)> _MakeTToUnitFunc(const std::function<void(_Type)>& _Func)\r
+{\r
+    return [=](_Type t) -> _Unit_type {\r
+        _Func(t);\r
+        return _Unit_type();\r
+    };\r
+}\r
+\r
+inline std::function<_Unit_type(_Unit_type)> _MakeUnitToUnitFunc(const std::function<void(void)>& _Func)\r
+{\r
+    return [=](_Unit_type) -> _Unit_type {\r
+        _Func();\r
+        return _Unit_type();\r
+    };\r
+}\r
+} // namespace details\r
+\r
+/// <summary>\r
+///     The <c>task_completion_event</c> class allows you to delay the execution of a task until a condition is\r
+///     satisfied, or start a task in response to an external event.\r
+/// </summary>\r
+/// <typeparam name="_ResultType">\r
+///     The result type of this <c>task_completion_event</c> class.\r
+/// </typeparam>\r
+/// <remarks>\r
+///     Use a task created from a task completion event when your scenario requires you to create a task that will\r
+///     complete, and thereby have its continuations scheduled for execution, at some point in the future. The\r
+///     <c>task_completion_event</c> must have the same type as the task you create, and calling the set method on the\r
+///     task completion event with a value of that type will cause the associated task to complete, and provide that\r
+///     value as a result to its continuations. <para>If the task completion event is never signaled, any tasks created\r
+///     from it will be canceled when it is destructed.</para> <para><c>task_completion_event</c> behaves like a smart\r
+///     pointer, and should be passed by value.</para>\r
+/// </remarks>\r
+/// <seealso cref="task Class"/>\r
+/**/\r
+template<typename _ResultType>\r
+class task_completion_event\r
+{\r
+public:\r
+    /// <summary>\r
+    ///     Constructs a <c>task_completion_event</c> object.\r
+    /// </summary>\r
+    /**/\r
+    task_completion_event() : _M_Impl(std::make_shared<details::_Task_completion_event_impl<_ResultType>>()) {}\r
+\r
+    /// <summary>\r
+    ///     Sets the task completion event.\r
+    /// </summary>\r
+    /// <param name="_Result">\r
+    ///     The result to set this event with.\r
+    /// </param>\r
+    /// <returns>\r
+    ///     The method returns <c>true</c> if it was successful in setting the event. It returns <c>false</c> if the\r
+    ///     event is already set.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     In the presence of multiple or concurrent calls to <c>set</c>, only the first call will succeed and its\r
+    ///     result (if any) will be stored in the task completion event. The remaining sets are ignored and the method\r
+    ///     will return false. When you set a task completion event, all the tasks created from that event will\r
+    ///     immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have a\r
+    ///     <typeparamref name="_ResultType"/> other than <c>void</c> will pass the value <paramref value="_Result"/> to\r
+    ///     their continuations.\r
+    /// </remarks>\r
+    /**/\r
+    bool set(_ResultType _Result)\r
+        const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas\r
+    {\r
+        // Subsequent sets are ignored. This makes races to set benign: the first setter wins and all others are\r
+        // ignored.\r
+        if (_IsTriggered())\r
+        {\r
+            return false;\r
+        }\r
+\r
+        _TaskList _Tasks;\r
+        bool _RunContinuations = false;\r
+        {\r
+            ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec);\r
+\r
+            if (!_IsTriggered())\r
+            {\r
+                _M_Impl->_M_value.Set(_Result);\r
+                _M_Impl->_M_fHasValue = true;\r
+\r
+                _Tasks.swap(_M_Impl->_M_tasks);\r
+                _RunContinuations = true;\r
+            }\r
+        }\r
+\r
+        if (_RunContinuations)\r
+        {\r
+            for (auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt)\r
+            {\r
+                // If current task was canceled by a cancellation_token, it would be in cancel pending state.\r
+                if ((*_TaskIt)->_IsPendingCancel())\r
+                    (*_TaskIt)->_Cancel(true);\r
+                else\r
+                {\r
+                    // Tasks created with task_completion_events can be marked as async, (we do this in when_any and\r
+                    // when_all if one of the tasks involved is an async task). Since continuations of async tasks can\r
+                    // execute inline, we need to run continuations after the lock is released.\r
+                    (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get());\r
+                }\r
+            }\r
+            if (_M_Impl->_HasUserException())\r
+            {\r
+                _M_Impl->_M_exceptionHolder.reset();\r
+            }\r
+            return true;\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    template<typename _E>\r
+    __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result\r
+        bool set_exception(\r
+            _E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas\r
+    {\r
+        // It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for\r
+        // set_exception.\r
+        return _Cancel(std::make_exception_ptr(_Except), PPLX_CAPTURE_CALLSTACK());\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Propagates an exception to all tasks associated with this event.\r
+    /// </summary>\r
+    /// <param>\r
+    ///     The exception_ptr that indicates the exception to set this event with.\r
+    /// </param>\r
+    /**/\r
+    __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result\r
+        bool set_exception(std::exception_ptr _ExceptionPtr)\r
+            const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas\r
+    {\r
+        // It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for\r
+        // set_exception.\r
+        return _Cancel(_ExceptionPtr, PPLX_CAPTURE_CALLSTACK());\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Internal method to cancel the task_completion_event. Any task created using this event will be marked as\r
+    ///     canceled if it has not already been set.\r
+    /// </summary>\r
+    bool _Cancel() const\r
+    {\r
+        // Cancel with the stored exception if one exists.\r
+        return _CancelInternal();\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Internal method to cancel the task_completion_event with the exception provided. Any task created using this\r
+    ///     event will be canceled with the same exception.\r
+    /// </summary>\r
+    template<typename _ExHolderType>\r
+    bool _Cancel(\r
+        _ExHolderType _ExHolder,\r
+        const details::_TaskCreationCallstack& _SetExceptionAddressHint = details::_TaskCreationCallstack()) const\r
+    {\r
+        bool _Canceled;\r
+        if (_StoreException(_ExHolder, _SetExceptionAddressHint))\r
+        {\r
+            _Canceled = _CancelInternal();\r
+            _ASSERTE(_Canceled);\r
+        }\r
+        else\r
+        {\r
+            _Canceled = false;\r
+        }\r
+        return _Canceled;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Internal method that stores an exception in the task completion event. This is used internally by when_any.\r
+    ///     Note, this does not cancel the task completion event. A task completion event with a stored exception\r
+    ///     can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present.\r
+    /// </summary>\r
+    template<typename _ExHolderType>\r
+    bool _StoreException(\r
+        _ExHolderType _ExHolder,\r
+        const details::_TaskCreationCallstack& _SetExceptionAddressHint = details::_TaskCreationCallstack()) const\r
+    {\r
+        ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec);\r
+        if (!_IsTriggered() && !_M_Impl->_HasUserException())\r
+        {\r
+            // Create the exception holder only if we have ensured there we will be successful in setting it onto the\r
+            // task completion event. Failing to do so will result in an unobserved task exception.\r
+            _M_Impl->_M_exceptionHolder = _ToExceptionHolder(_ExHolder, _SetExceptionAddressHint);\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Tests whether current event has been either Set, or Canceled.\r
+    /// </summary>\r
+    bool _IsTriggered() const { return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled; }\r
+\r
+private:\r
+    static std::shared_ptr<details::_ExceptionHolder> _ToExceptionHolder(\r
+        const std::shared_ptr<details::_ExceptionHolder>& _ExHolder, const details::_TaskCreationCallstack&)\r
+    {\r
+        return _ExHolder;\r
+    }\r
+\r
+    static std::shared_ptr<details::_ExceptionHolder> _ToExceptionHolder(\r
+        std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack& _SetExceptionAddressHint)\r
+    {\r
+        return std::make_shared<details::_ExceptionHolder>(_ExceptionPtr, _SetExceptionAddressHint);\r
+    }\r
+\r
+    template<typename T>\r
+    friend class task; // task can register itself with the event by calling the private _RegisterTask\r
+    template<typename T>\r
+    friend class task_completion_event;\r
+\r
+    typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList;\r
+\r
+    /// <summary>\r
+    ///    Cancels the task_completion_event.\r
+    /// </summary>\r
+    bool _CancelInternal() const\r
+    {\r
+        // Cancellation of task completion events is an internal only utility. Our usage is such that _CancelInternal\r
+        // will never be invoked if the task completion event has been set.\r
+        _ASSERTE(!_M_Impl->_M_fHasValue);\r
+        if (_M_Impl->_M_fIsCanceled)\r
+        {\r
+            return false;\r
+        }\r
+\r
+        _TaskList _Tasks;\r
+        bool _Cancel = false;\r
+        {\r
+            ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec);\r
+            _ASSERTE(!_M_Impl->_M_fHasValue);\r
+            if (!_M_Impl->_M_fIsCanceled)\r
+            {\r
+                _M_Impl->_M_fIsCanceled = true;\r
+                _Tasks.swap(_M_Impl->_M_tasks);\r
+                _Cancel = true;\r
+            }\r
+        }\r
+\r
+        bool _UserException = _M_Impl->_HasUserException();\r
+\r
+        if (_Cancel)\r
+        {\r
+            for (auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt)\r
+            {\r
+                // Need to call this after the lock is released. See comments in set().\r
+                if (_UserException)\r
+                {\r
+                    (*_TaskIt)->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true);\r
+                }\r
+                else\r
+                {\r
+                    (*_TaskIt)->_Cancel(true);\r
+                }\r
+            }\r
+        }\r
+        return _Cancel;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Register a task with this event. This function is called when a task is constructed using\r
+    ///     a task_completion_event.\r
+    /// </summary>\r
+    void _RegisterTask(const typename details::_Task_ptr<_ResultType>::_Type& _TaskParam)\r
+    {\r
+        ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec);\r
+\r
+        // If an exception was already set on this event, then cancel the task with the stored exception.\r
+        if (_M_Impl->_HasUserException())\r
+        {\r
+            _TaskParam->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true);\r
+        }\r
+        else if (_M_Impl->_M_fHasValue)\r
+        {\r
+            _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get());\r
+        }\r
+        else\r
+        {\r
+            _M_Impl->_M_tasks.push_back(_TaskParam);\r
+        }\r
+    }\r
+\r
+    std::shared_ptr<details::_Task_completion_event_impl<_ResultType>> _M_Impl;\r
+};\r
+\r
+/// <summary>\r
+///     The <c>task_completion_event</c> class allows you to delay the execution of a task until a condition is\r
+///     satisfied, or start a task in response to an external event.\r
+/// </summary>\r
+/// <remarks>\r
+///     Use a task created from a task completion event when your scenario requires you to create a task that will\r
+///     complete, and thereby have its continuations scheduled for execution, at some point in the future. The\r
+///     <c>task_completion_event</c> must have the same type as the task you create, and calling the set method on the\r
+///     task completion event with a value of that type will cause the associated task to complete, and provide that\r
+///     value as a result to its continuations. <para>If the task completion event is never signaled, any tasks created\r
+///     from it will be canceled when it is destructed.</para> <para><c>task_completion_event</c> behaves like a smart\r
+///     pointer, and should be passed by value.</para>\r
+/// </remarks>\r
+/// <seealso cref="task Class"/>\r
+/**/\r
+template<>\r
+class task_completion_event<void>\r
+{\r
+public:\r
+    /// <summary>\r
+    ///     Sets the task completion event.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     The method returns <c>true</c> if it was successful in setting the event. It returns <c>false</c> if the\r
+    ///     event is already set.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     In the presence of multiple or concurrent calls to <c>set</c>, only the first call will succeed and its\r
+    ///     result (if any) will be stored in the task completion event. The remaining sets are ignored and the method\r
+    ///     will return false. When you set a task completion event, all the tasks created from that event will\r
+    ///     immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have a\r
+    ///     <typeparamref name="_ResultType"/> other than <c>void</c> will pass the value <paramref value="_Result"/> to\r
+    ///     their continuations.\r
+    /// </remarks>\r
+    /**/\r
+    bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas\r
+    {\r
+        return _M_unitEvent.set(details::_Unit_type());\r
+    }\r
+\r
+    template<typename _E>\r
+    __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result\r
+        bool set_exception(\r
+            _E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas\r
+    {\r
+        return _M_unitEvent._Cancel(std::make_exception_ptr(_Except), PPLX_CAPTURE_CALLSTACK());\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Propagates an exception to all tasks associated with this event.\r
+    /// </summary>\r
+    /// <param>\r
+    ///     The exception_ptr that indicates the exception to set this event with.\r
+    /// </param>\r
+    /**/\r
+    __declspec(\r
+        noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK intrinsic gives us the expected result\r
+        bool set_exception(std::exception_ptr _ExceptionPtr)\r
+            const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas\r
+    {\r
+        // It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for\r
+        // set_exception.\r
+        return _M_unitEvent._Cancel(_ExceptionPtr, PPLX_CAPTURE_CALLSTACK());\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has\r
+    ///     not already been set.\r
+    /// </summary>\r
+    void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas\r
+    {\r
+        _M_unitEvent._Cancel();\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Cancel the task_completion_event with the exception holder provided. Any task created using this event will\r
+    ///     be canceled with the same exception.\r
+    /// </summary>\r
+    void _Cancel(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const { _M_unitEvent._Cancel(_ExHolder); }\r
+\r
+    /// <summary>\r
+    ///     Method that stores an exception in the task completion event. This is used internally by when_any.\r
+    ///     Note, this does not cancel the task completion event. A task completion event with a stored exception\r
+    ///     can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present.\r
+    /// </summary>\r
+    bool _StoreException(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const\r
+    {\r
+        return _M_unitEvent._StoreException(_ExHolder);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Test whether current event has been either Set, or Canceled.\r
+    /// </summary>\r
+    bool _IsTriggered() const { return _M_unitEvent._IsTriggered(); }\r
+\r
+private:\r
+    template<typename T>\r
+    friend class task; // task can register itself with the event by calling the private _RegisterTask\r
+\r
+    /// <summary>\r
+    ///     Register a task with this event. This function is called when a task is constructed using\r
+    ///     a task_completion_event.\r
+    /// </summary>\r
+    void _RegisterTask(details::_Task_ptr<details::_Unit_type>::_Type _TaskParam)\r
+    {\r
+        _M_unitEvent._RegisterTask(_TaskParam);\r
+    }\r
+\r
+    // The void event contains an event a dummy type so common code can be used for events with void and non-void\r
+    // results.\r
+    task_completion_event<details::_Unit_type> _M_unitEvent;\r
+};\r
+\r
+namespace details\r
+{\r
+//\r
+// Compile-time validation helpers\r
+//\r
+\r
+// Task constructor validation: issue helpful diagnostics for common user errors. Do not attempt full validation here.\r
+//\r
+// Anything callable is fine\r
+template<typename _ReturnType, typename _Ty>\r
+auto _IsValidTaskCtor(_Ty _Param, int, int, int, int) -> decltype(_Param(), std::true_type());\r
+\r
+#if defined(__cplusplus_winrt)\r
+// Anything that has GetResults is fine: this covers all async operations\r
+template<typename _ReturnType, typename _Ty>\r
+auto _IsValidTaskCtor(_Ty _Param, int, int, int, ...) -> decltype(_Param->GetResults(), std::true_type());\r
+#endif\r
+\r
+// Allow parameters with set: this covers task_completion_event\r
+template<typename _ReturnType, typename _Ty>\r
+auto _IsValidTaskCtor(_Ty _Param, int, int, ...)\r
+    -> decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type());\r
+\r
+template<typename _ReturnType, typename _Ty>\r
+auto _IsValidTaskCtor(_Ty _Param, int, ...) -> decltype(_Param.set(), std::true_type());\r
+\r
+// All else is invalid\r
+template<typename _ReturnType, typename _Ty>\r
+std::false_type _IsValidTaskCtor(_Ty _Param, ...);\r
+\r
+template<typename _ReturnType, typename _Ty>\r
+void _ValidateTaskConstructorArgs(_Ty _Param)\r
+{\r
+    static_assert(std::is_same<decltype(_IsValidTaskCtor<_ReturnType>(_Param, 0, 0, 0, 0)), std::true_type>::value,\r
+#if defined(__cplusplus_winrt)\r
+                  "incorrect argument for task constructor; can be a callable object, an asynchronous operation, or a "\r
+                  "task_completion_event"\r
+#else  /* defined (__cplusplus_winrt) */\r
+                  "incorrect argument for task constructor; can be a callable object or a task_completion_event"\r
+#endif /* defined (__cplusplus_winrt) */\r
+    );\r
+#if defined(__cplusplus_winrt)\r
+    static_assert(!(std::is_same<_Ty, _ReturnType>::value && details::_IsIAsyncInfo<_Ty>::_Value),\r
+                  "incorrect template argument for task; consider using the return type of the async operation");\r
+#endif /* defined (__cplusplus_winrt) */\r
+}\r
+\r
+#if defined(__cplusplus_winrt)\r
+// Helpers for create_async validation\r
+//\r
+// A parameter lambda taking no arguments is valid\r
+template<typename _Ty>\r
+static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int) -> decltype(_Param(), std::true_type());\r
+\r
+// A parameter lambda taking an cancellation_token argument is valid\r
+template<typename _Ty>\r
+static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...)\r
+    -> decltype(_Param(cancellation_token::none()), std::true_type());\r
+\r
+// A parameter lambda taking a progress report argument is valid\r
+template<typename _Ty>\r
+static auto _IsValidCreateAsync(_Ty _Param, int, int, ...)\r
+    -> decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type());\r
+\r
+// A parameter lambda taking a progress report and a cancellation_token argument is valid\r
+template<typename _Ty>\r
+static auto _IsValidCreateAsync(_Ty _Param, int, ...)\r
+    -> decltype(_Param(details::_ProgressReporterCtorArgType(), cancellation_token::none()), std::true_type());\r
+\r
+// All else is invalid\r
+template<typename _Ty>\r
+static std::false_type _IsValidCreateAsync(_Ty _Param, ...);\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+/// <summary>\r
+///     A helper class template that makes only movable functions be able to be passed to std::function\r
+/// </summary>\r
+template<typename _Ty>\r
+struct _NonCopyableFunctorWrapper\r
+{\r
+    template<typename _Tx,\r
+             typename = typename std::enable_if<\r
+                 !std::is_base_of<_NonCopyableFunctorWrapper<_Ty>, typename std::decay<_Tx>::type>::value>::type>\r
+    explicit _NonCopyableFunctorWrapper(_Tx&& f) : _M_functor {std::make_shared<_Ty>(std::forward<_Tx>(f))}\r
+    {\r
+    }\r
+\r
+    template<class... _Args>\r
+    auto operator()(_Args&&... args) -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...))\r
+    {\r
+        return _M_functor->operator()(std::forward<_Args>(args)...);\r
+    }\r
+\r
+    template<class... _Args>\r
+    auto operator()(_Args&&... args) const -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...))\r
+    {\r
+        return _M_functor->operator()(std::forward<_Args>(args)...);\r
+    }\r
+\r
+    std::shared_ptr<_Ty> _M_functor;\r
+};\r
+\r
+template<typename _Ty, typename Enable = void>\r
+struct _CopyableFunctor\r
+{\r
+    typedef _Ty _Type;\r
+};\r
+\r
+template<typename _Ty>\r
+struct _CopyableFunctor<\r
+    _Ty,\r
+    typename std::enable_if<std::is_move_constructible<_Ty>::value && !std::is_copy_constructible<_Ty>::value>::type>\r
+{\r
+    typedef _NonCopyableFunctorWrapper<_Ty> _Type;\r
+};\r
+} // namespace details\r
+/// <summary>\r
+///     A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a\r
+///     lambda that takes and returns a non-void type (details::_Unit_type is used to substitute for void). This is to\r
+///     minimize the special handling required for 'void'.\r
+/// </summary>\r
+template<typename _InpType, typename _OutType>\r
+class _Continuation_func_transformer\r
+{\r
+public:\r
+    static auto _Perform(std::function<_OutType(_InpType)> _Func) -> decltype(_Func) { return _Func; }\r
+};\r
+\r
+template<typename _OutType>\r
+class _Continuation_func_transformer<void, _OutType>\r
+{\r
+public:\r
+    static auto _Perform(std::function<_OutType(void)> _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func))\r
+    {\r
+        return details::_MakeUnitToTFunc<_OutType>(_Func);\r
+    }\r
+};\r
+\r
+template<typename _InType>\r
+class _Continuation_func_transformer<_InType, void>\r
+{\r
+public:\r
+    static auto _Perform(std::function<void(_InType)> _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func))\r
+    {\r
+        return details::_MakeTToUnitFunc<_InType>(_Func);\r
+    }\r
+};\r
+\r
+template<>\r
+class _Continuation_func_transformer<void, void>\r
+{\r
+public:\r
+    static auto _Perform(std::function<void(void)> _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func))\r
+    {\r
+        return details::_MakeUnitToUnitFunc(_Func);\r
+    }\r
+};\r
+\r
+// A helper class template that transforms an intial task lambda returns void into a lambda that returns a non-void type\r
+// (details::_Unit_type is used to substitute for void). This is to minimize the special handling required for 'void'.\r
+template<typename _RetType>\r
+class _Init_func_transformer\r
+{\r
+public:\r
+    static auto _Perform(std::function<_RetType(void)> _Func) -> decltype(_Func) { return _Func; }\r
+};\r
+\r
+template<>\r
+class _Init_func_transformer<void>\r
+{\r
+public:\r
+    static auto _Perform(std::function<void(void)> _Func) -> decltype(details::_MakeVoidToUnitFunc(_Func))\r
+    {\r
+        return details::_MakeVoidToUnitFunc(_Func);\r
+    }\r
+};\r
+\r
+/// <summary>\r
+///     The Parallel Patterns Library (PPL) <c>task</c> class. A <c>task</c> object represents work that can be executed\r
+///     asynchronously, and concurrently with other tasks and parallel work produced by parallel algorithms in the\r
+///     Concurrency Runtime. It produces a result of type <typeparamref name="_ResultType"/> on successful completion.\r
+///     Tasks of type <c>task&lt;void&gt;</c> produce no result. A task can be waited upon and canceled independently of\r
+///     other tasks. It can also be composed with other tasks using continuations(<c>then</c>), and\r
+///     join(<c>when_all</c>) and choice(<c>when_any</c>) patterns.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The result type of this task.\r
+/// </typeparam>\r
+/// <remarks>\r
+///     For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.\r
+/// </remarks>\r
+/**/\r
+template<typename _ReturnType>\r
+class task\r
+{\r
+public:\r
+    /// <summary>\r
+    ///     The type of the result an object of this class produces.\r
+    /// </summary>\r
+    /**/\r
+    typedef _ReturnType result_type;\r
+\r
+    /// <summary>\r
+    ///     Constructs a <c>task</c> object.\r
+    /// </summary>\r
+    /// <remarks>\r
+    ///     The default constructor for a <c>task</c> is only present in order to allow tasks to be used within\r
+    ///     containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as\r
+    ///     <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument\r
+    ///     Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is\r
+    ///     created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the\r
+    ///     task completion event is set.</para> <para>The version of the constructor that takes a cancellation token\r
+    ///     creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.\r
+    ///     Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a\r
+    ///     <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface\r
+    ///     reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.\r
+    ///     Similarly, tasks created from a lambda that returns a <c>task&lt;result_type&gt;</c> reach their terminal\r
+    ///     state when the inner task reaches its terminal state, and not when the lambda returns.</para>\r
+    ///     <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by\r
+    ///     multiple threads without the need for locks.</para> <para>The constructor overloads that take a\r
+    ///     Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to\r
+    ///     Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency\r
+    ///     Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    task() : _M_Impl(nullptr)\r
+    {\r
+        // The default constructor should create a task with a nullptr impl. This is a signal that the\r
+        // task is not usable and should throw if any wait(), get() or then() APIs are used.\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Constructs a <c>task</c> object.\r
+    /// </summary>\r
+    /// <typeparam name="_Ty">\r
+    ///     The type of the parameter from which the task is to be constructed.\r
+    /// </typeparam>\r
+    /// <param name="_Param">\r
+    ///     The parameter from which the task is to be constructed. This could be a lambda, a function object, a\r
+    ///     <c>task_completion_event&lt;result_type&gt;</c> object, or a Windows::Foundation::IAsyncInfo if you are\r
+    ///     using tasks in your Windows Store app. The lambda or function object should be a type equivalent to\r
+    ///     <c>std::function&lt;X(void)&gt;</c>, where X can be a variable of type <c>result_type</c>,\r
+    ///     <c>task&lt;result_type&gt;</c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps.\r
+    /// </param>\r
+    /// <param name="_Token">\r
+    ///     The cancellation token to associate with this task. A task created without a cancellation token cannot be\r
+    ///     canceled. It implicitly receives the token <c>cancellation_token::none()</c>.\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     The default constructor for a <c>task</c> is only present in order to allow tasks to be used within\r
+    ///     containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as\r
+    ///     <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument\r
+    ///     Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is\r
+    ///     created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the\r
+    ///     task completion event is set.</para> <para>The version of the constructor that takes a cancellation token\r
+    ///     creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.\r
+    ///     Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a\r
+    ///     <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface\r
+    ///     reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.\r
+    ///     Similarly, tasks created from a lambda that returns a <c>task&lt;result_type&gt;</c> reach their terminal\r
+    ///     state when the inner task reaches its terminal state, and not when the lambda returns.</para>\r
+    ///     <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by\r
+    ///     multiple threads without the need for locks.</para> <para>The constructor overloads that take a\r
+    ///     Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to\r
+    ///     Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency\r
+    ///     Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    template<typename _Ty>\r
+    __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result\r
+        explicit task(_Ty _Param)\r
+    {\r
+        task_options _TaskOptions;\r
+        details::_ValidateTaskConstructorArgs<_ReturnType, _Ty>(_Param);\r
+\r
+        _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler());\r
+        // Do not move the next line out of this function. It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the\r
+        // the call site of the task constructor.\r
+        _SetTaskCreationCallstack(PPLX_CAPTURE_CALLSTACK());\r
+\r
+        _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param, 0));\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Constructs a <c>task</c> object.\r
+    /// </summary>\r
+    /// <typeparam name="_Ty">\r
+    ///     The type of the parameter from which the task is to be constructed.\r
+    /// </typeparam>\r
+    /// <param name="_Param">\r
+    ///     The parameter from which the task is to be constructed. This could be a lambda, a function object, a\r
+    ///     <c>task_completion_event&lt;result_type&gt;</c> object, or a Windows::Foundation::IAsyncInfo if you are\r
+    ///     using tasks in your Windows Store app. The lambda or function object should be a type equivalent to\r
+    ///     <c>std::function&lt;X(void)&gt;</c>, where X can be a variable of type <c>result_type</c>,\r
+    ///     <c>task&lt;result_type&gt;</c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps.\r
+    /// </param>\r
+    /// <param name="_TaskOptions">\r
+    ///     The task options include cancellation token, scheduler etc\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     The default constructor for a <c>task</c> is only present in order to allow tasks to be used within\r
+    ///     containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as\r
+    ///     <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument\r
+    ///     Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is\r
+    ///     created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the\r
+    ///     task completion event is set.</para> <para>The version of the constructor that takes a cancellation token\r
+    ///     creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.\r
+    ///     Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a\r
+    ///     <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface\r
+    ///     reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.\r
+    ///     Similarly, tasks created from a lambda that returns a <c>task&lt;result_type&gt;</c> reach their terminal\r
+    ///     state when the inner task reaches its terminal state, and not when the lambda returns.</para>\r
+    ///     <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by\r
+    ///     multiple threads without the need for locks.</para> <para>The constructor overloads that take a\r
+    ///     Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to\r
+    ///     Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency\r
+    ///     Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    template<typename _Ty>\r
+    __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result\r
+        explicit task(_Ty _Param, const task_options& _TaskOptions)\r
+    {\r
+        details::_ValidateTaskConstructorArgs<_ReturnType, _Ty>(_Param);\r
+\r
+        _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler());\r
+        // Do not move the next line out of this function. It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the\r
+        // the call site of the task constructor.\r
+        _SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack\r
+                                      ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack\r
+                                      : PPLX_CAPTURE_CALLSTACK());\r
+\r
+        _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param, 0));\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Constructs a <c>task</c> object.\r
+    /// </summary>\r
+    /// <param name="_Other">\r
+    ///     The source <c>task</c> object.\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     The default constructor for a <c>task</c> is only present in order to allow tasks to be used within\r
+    ///     containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as\r
+    ///     <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument\r
+    ///     Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is\r
+    ///     created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the\r
+    ///     task completion event is set.</para> <para>The version of the constructor that takes a cancellation token\r
+    ///     creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.\r
+    ///     Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a\r
+    ///     <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface\r
+    ///     reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.\r
+    ///     Similarly, tasks created from a lambda that returns a <c>task&lt;result_type&gt;</c> reach their terminal\r
+    ///     state when the inner task reaches its terminal state, and not when the lambda returns.</para>\r
+    ///     <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by\r
+    ///     multiple threads without the need for locks.</para> <para>The constructor overloads that take a\r
+    ///     Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to\r
+    ///     Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency\r
+    ///     Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    task(const task& _Other) : _M_Impl(_Other._M_Impl) {}\r
+\r
+    /// <summary>\r
+    ///     Constructs a <c>task</c> object.\r
+    /// </summary>\r
+    /// <param name="_Other">\r
+    ///     The source <c>task</c> object.\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     The default constructor for a <c>task</c> is only present in order to allow tasks to be used within\r
+    ///     containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as\r
+    ///     <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument\r
+    ///     Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is\r
+    ///     created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the\r
+    ///     task completion event is set.</para> <para>The version of the constructor that takes a cancellation token\r
+    ///     creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.\r
+    ///     Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a\r
+    ///     <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface\r
+    ///     reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.\r
+    ///     Similarly, tasks created from a lambda that returns a <c>task&lt;result_type&gt;</c> reach their terminal\r
+    ///     state when the inner task reaches its terminal state, and not when the lambda returns.</para>\r
+    ///     <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by\r
+    ///     multiple threads without the need for locks.</para> <para>The constructor overloads that take a\r
+    ///     Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to\r
+    ///     Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency\r
+    ///     Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    task(task&& _Other) : _M_Impl(std::move(_Other._M_Impl)) {}\r
+\r
+    /// <summary>\r
+    ///     Replaces the contents of one <c>task</c> object with another.\r
+    /// </summary>\r
+    /// <param name="_Other">\r
+    ///     The source <c>task</c> object.\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents\r
+    ///     the same actual task as <paramref name="_Other"/> does.\r
+    /// </remarks>\r
+    /**/\r
+    task& operator=(const task& _Other)\r
+    {\r
+        if (this != &_Other)\r
+        {\r
+            _M_Impl = _Other._M_Impl;\r
+        }\r
+        return *this;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Replaces the contents of one <c>task</c> object with another.\r
+    /// </summary>\r
+    /// <param name="_Other">\r
+    ///     The source <c>task</c> object.\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents\r
+    ///     the same actual task as <paramref name="_Other"/> does.\r
+    /// </remarks>\r
+    /**/\r
+    task& operator=(task&& _Other)\r
+    {\r
+        if (this != &_Other)\r
+        {\r
+            _M_Impl = std::move(_Other._M_Impl);\r
+        }\r
+        return *this;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Adds a continuation task to this task.\r
+    /// </summary>\r
+    /// <typeparam name="_Function">\r
+    ///     The type of the function object that will be invoked by this task.\r
+    /// </typeparam>\r
+    /// <param name="_Func">\r
+    ///     The continuation function to execute when this task completes. This continuation function must take as input\r
+    ///     a variable of either <c>result_type</c> or <c>task&lt;result_type&gt;</c>, where <c>result_type</c> is the\r
+    ///     type of the result this task produces.\r
+    /// </param>\r
+    /// <returns>\r
+    ///     The newly created continuation task. The result type of the returned task is determined by what <paramref\r
+    ///     name="_Func"/> returns.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo\r
+    ///     interface, are only available to Windows Store apps. <para>For more information on how to use task\r
+    ///     continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    template<typename _Function>\r
+    __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result\r
+        auto then(_Function&& _Func) const ->\r
+        typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType\r
+    {\r
+        task_options _TaskOptions;\r
+        details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());\r
+        return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Adds a continuation task to this task.\r
+    /// </summary>\r
+    /// <typeparam name="_Function">\r
+    ///     The type of the function object that will be invoked by this task.\r
+    /// </typeparam>\r
+    /// <param name="_Func">\r
+    ///     The continuation function to execute when this task completes. This continuation function must take as input\r
+    ///     a variable of either <c>result_type</c> or <c>task&lt;result_type&gt;</c>, where <c>result_type</c> is the\r
+    ///     type of the result this task produces.\r
+    /// </param>\r
+    /// <param name="_TaskOptions">\r
+    ///     The task options include cancellation token, scheduler and continuation context. By default the former 3\r
+    ///     options are inherited from the antecedent task\r
+    /// </param>\r
+    /// <returns>\r
+    ///     The newly created continuation task. The result type of the returned task is determined by what <paramref\r
+    ///     name="_Func"/> returns.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo\r
+    ///     interface, are only available to Windows Store apps. <para>For more information on how to use task\r
+    ///     continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    template<typename _Function>\r
+    __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result\r
+        auto then(_Function&& _Func, task_options _TaskOptions) const ->\r
+        typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType\r
+    {\r
+        details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());\r
+        return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Adds a continuation task to this task.\r
+    /// </summary>\r
+    /// <typeparam name="_Function">\r
+    ///     The type of the function object that will be invoked by this task.\r
+    /// </typeparam>\r
+    /// <param name="_Func">\r
+    ///     The continuation function to execute when this task completes. This continuation function must take as input\r
+    ///     a variable of either <c>result_type</c> or <c>task&lt;result_type&gt;</c>, where <c>result_type</c> is the\r
+    ///     type of the result this task produces.\r
+    /// </param>\r
+    /// <param name="_CancellationToken">\r
+    ///     The cancellation token to associate with the continuation task. A continuation task that is created without\r
+    ///     a cancellation token will inherit the token of its antecedent task.\r
+    /// </param>\r
+    /// <param name="_ContinuationContext">\r
+    ///     A variable that specifies where the continuation should execute. This variable is only useful when used in a\r
+    ///     Windows Store style app. For more information, see <see cref="task_continuation_context\r
+    ///     Class">task_continuation_context</see>\r
+    /// </param>\r
+    /// <returns>\r
+    ///     The newly created continuation task. The result type of the returned task is determined by what <paramref\r
+    ///     name="_Func"/> returns.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo\r
+    ///     interface, are only available to Windows Store apps. <para>For more information on how to use task\r
+    ///     continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    template<typename _Function>\r
+    __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result\r
+        auto then(_Function&& _Func,\r
+                  cancellation_token _CancellationToken,\r
+                  task_continuation_context _ContinuationContext) const ->\r
+        typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType\r
+    {\r
+        task_options _TaskOptions(_CancellationToken, _ContinuationContext);\r
+        details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());\r
+        return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Waits for this task to reach a terminal state. It is possible for <c>wait</c> to execute the task inline, if\r
+    ///     all of the tasks dependencies are satisfied, and it has not already been picked up for execution by a\r
+    ///     background worker.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     A <c>task_status</c> value which could be either <c>completed</c> or <c>canceled</c>. If the task\r
+    ///     encountered an exception during execution, or an exception was propagated to it from an antecedent task,\r
+    ///     <c>wait</c> will throw that exception.\r
+    /// </returns>\r
+    /**/\r
+    task_status wait() const\r
+    {\r
+        if (!_M_Impl)\r
+        {\r
+            throw invalid_operation("wait() cannot be called on a default constructed task.");\r
+        }\r
+\r
+        return _M_Impl->_Wait();\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Returns the result this task produced. If the task is not in a terminal state, a call to <c>get</c> will\r
+    ///     wait for the task to finish. This method does not return a value when called on a task with a\r
+    ///     <c>result_type</c> of <c>void</c>.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     The result of the task.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     If the task is canceled, a call to <c>get</c> will throw a <see cref="task_canceled\r
+    ///     Class">task_canceled</see> exception. If the task encountered an different exception or an exception was\r
+    ///     propagated to it from an antecedent task, a call to <c>get</c> will throw that exception.\r
+    /// </remarks>\r
+    /**/\r
+    _ReturnType get() const\r
+    {\r
+        if (!_M_Impl)\r
+        {\r
+            throw invalid_operation("get() cannot be called on a default constructed task.");\r
+        }\r
+\r
+        if (_M_Impl->_Wait() == canceled)\r
+        {\r
+            throw task_canceled();\r
+        }\r
+\r
+        return _M_Impl->_GetResult();\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Determines if the task is completed.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     True if the task has completed, false otherwise.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     The function returns true if the task is completed or canceled (with or without user exception).\r
+    /// </remarks>\r
+    bool is_done() const\r
+    {\r
+        if (!_M_Impl)\r
+        {\r
+            throw invalid_operation("is_done() cannot be called on a default constructed task.");\r
+        }\r
+\r
+        return _M_Impl->_IsDone();\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Returns the scheduler for this task\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     A pointer to the scheduler\r
+    /// </returns>\r
+    scheduler_ptr scheduler() const\r
+    {\r
+        if (!_M_Impl)\r
+        {\r
+            throw invalid_operation("scheduler() cannot be called on a default constructed task.");\r
+        }\r
+\r
+        return _M_Impl->_GetScheduler();\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Determines whether the task unwraps a Windows Runtime <c>IAsyncInfo</c> interface or is descended from such\r
+    ///     a task.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     <c>true</c> if the task unwraps an <c>IAsyncInfo</c> interface or is descended from such a task,\r
+    ///     <c>false</c> otherwise.\r
+    /// </returns>\r
+    /**/\r
+    bool is_apartment_aware() const\r
+    {\r
+        if (!_M_Impl)\r
+        {\r
+            throw invalid_operation("is_apartment_aware() cannot be called on a default constructed task.");\r
+        }\r
+        return _M_Impl->_IsApartmentAware();\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Determines whether two <c>task</c> objects represent the same internal task.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     <c>true</c> if the objects refer to the same underlying task, and <c>false</c> otherwise.\r
+    /// </returns>\r
+    /**/\r
+    bool operator==(const task<_ReturnType>& _Rhs) const { return (_M_Impl == _Rhs._M_Impl); }\r
+\r
+    /// <summary>\r
+    ///     Determines whether two <c>task</c> objects represent different internal tasks.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     <c>true</c> if the objects refer to different underlying tasks, and <c>false</c> otherwise.\r
+    /// </returns>\r
+    /**/\r
+    bool operator!=(const task<_ReturnType>& _Rhs) const { return !operator==(_Rhs); }\r
+\r
+    /// <summary>\r
+    ///     Create an underlying task implementation.\r
+    /// </summary>\r
+    void _CreateImpl(details::_CancellationTokenState* _Ct, scheduler_ptr _Scheduler)\r
+    {\r
+        _ASSERTE(_Ct != nullptr);\r
+        _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct, _Scheduler);\r
+        if (_Ct != details::_CancellationTokenState::_None())\r
+        {\r
+            _M_Impl->_RegisterCancellation(_M_Impl);\r
+        }\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Return the underlying implementation for this task.\r
+    /// </summary>\r
+    const typename details::_Task_ptr<_ReturnType>::_Type& _GetImpl() const { return _M_Impl; }\r
+\r
+    /// <summary>\r
+    ///     Set the implementation of the task to be the supplied implementation.\r
+    /// </summary>\r
+    void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type& _Impl)\r
+    {\r
+        _ASSERTE(!_M_Impl);\r
+        _M_Impl = _Impl;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Set the implementation of the task to be the supplied implementation using a move instead of a copy.\r
+    /// </summary>\r
+    void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type&& _Impl)\r
+    {\r
+        _ASSERTE(!_M_Impl);\r
+        _M_Impl = std::move(_Impl);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Sets a property determining whether the task is apartment aware.\r
+    /// </summary>\r
+    void _SetAsync(bool _Async = true) { _GetImpl()->_SetAsync(_Async); }\r
+\r
+    /// <summary>\r
+    ///     Sets a field in the task impl to the return callstack for calls to the task constructors and the then\r
+    ///     method.\r
+    /// </summary>\r
+    void _SetTaskCreationCallstack(const details::_TaskCreationCallstack& _callstack)\r
+    {\r
+        _GetImpl()->_SetTaskCreationCallstack(_callstack);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     An internal version of then that takes additional flags and always execute the continuation inline by\r
+    ///     default. When _ForceInline is set to false, continuations inlining will be limited to default\r
+    ///     _DefaultAutoInline. This function is Used for runtime internal continuations only.\r
+    /// </summary>\r
+    template<typename _Function>\r
+    auto _Then(_Function&& _Func,\r
+               details::_CancellationTokenState* _PTokenState,\r
+               details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const ->\r
+        typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType\r
+    {\r
+        // inherit from antecedent\r
+        auto _Scheduler = _GetImpl()->_GetScheduler();\r
+\r
+        return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func),\r
+                                                 _PTokenState,\r
+                                                 task_continuation_context::use_default(),\r
+                                                 _Scheduler,\r
+                                                 PPLX_CAPTURE_CALLSTACK(),\r
+                                                 _InliningMode);\r
+    }\r
+\r
+private:\r
+    template<typename T>\r
+    friend class task;\r
+\r
+    // The task handle type used to construct an 'initial task' - a task with no dependents.\r
+    template<typename _InternalReturnType, typename _Function, typename _TypeSelection>\r
+    struct _InitialTaskHandle\r
+        : details::_PPLTaskHandle<_ReturnType,\r
+                                  _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>,\r
+                                  details::_UnrealizedChore_t>\r
+    {\r
+        _Function _M_function;\r
+        _InitialTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type& _TaskImpl, const _Function& _func)\r
+            : details::_PPLTaskHandle<_ReturnType,\r
+                                      _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>,\r
+                                      details::_UnrealizedChore_t>::_PPLTaskHandle(_TaskImpl)\r
+            , _M_function(_func)\r
+        {\r
+        }\r
+\r
+        virtual ~_InitialTaskHandle() {}\r
+\r
+        template<typename _Func>\r
+        auto _LogWorkItemAndInvokeUserLambda(_Func&& _func) const -> decltype(_func())\r
+        {\r
+            details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger);\r
+            (void)_LogWorkItem;\r
+            return _func();\r
+        }\r
+\r
+        void _Perform() const { _Init(_TypeSelection()); }\r
+\r
+        void _SyncCancelAndPropagateException() const { this->_M_pTask->_Cancel(true); }\r
+\r
+        //\r
+        // Overload 0: returns _InternalReturnType\r
+        //\r
+        // This is the most basic task with no unwrapping\r
+        //\r
+        void _Init(details::_TypeSelectorNoAsync) const\r
+        {\r
+            this->_M_pTask->_FinalizeAndRunContinuations(\r
+                _LogWorkItemAndInvokeUserLambda(_Init_func_transformer<_InternalReturnType>::_Perform(_M_function)));\r
+        }\r
+\r
+        //\r
+        // Overload 1: returns IAsyncOperation<_InternalReturnType>^ (only under /ZW)\r
+        //                   or\r
+        //             returns task<_InternalReturnType>\r
+        //\r
+        // This is task whose functor returns an async operation or a task which will be unwrapped for continuation\r
+        // Depending on the output type, the right _AsyncInit gets invoked\r
+        //\r
+        void _Init(details::_TypeSelectorAsyncOperationOrTask) const\r
+        {\r
+            details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(\r
+                this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function));\r
+        }\r
+\r
+#if defined(__cplusplus_winrt)\r
+        //\r
+        // Overload 2: returns IAsyncAction^\r
+        //\r
+        // This is task whose functor returns an async action which will be unwrapped for continuation\r
+        //\r
+        void _Init(details::_TypeSelectorAsyncAction) const\r
+        {\r
+            details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(\r
+                this->_M_pTask,\r
+                ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function)));\r
+        }\r
+\r
+        //\r
+        // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>^\r
+        //\r
+        // This is task whose functor returns an async operation with progress which will be unwrapped for continuation\r
+        //\r
+        void _Init(details::_TypeSelectorAsyncOperationWithProgress) const\r
+        {\r
+            typedef details::_GetProgressType<decltype(_M_function())>::_Value _ProgressType;\r
+\r
+            details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(\r
+                this->_M_pTask,\r
+                ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_InternalReturnType,\r
+                                                                                       _ProgressType>(\r
+                    _LogWorkItemAndInvokeUserLambda(_M_function)));\r
+        }\r
+\r
+        //\r
+        // Overload 4: returns IAsyncActionWithProgress<_ProgressType>^\r
+        //\r
+        // This is task whose functor returns an async action with progress which will be unwrapped for continuation\r
+        //\r
+        void _Init(details::_TypeSelectorAsyncActionWithProgress) const\r
+        {\r
+            typedef details::_GetProgressType<decltype(_M_function())>::_Value _ProgressType;\r
+\r
+            details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(\r
+                this->_M_pTask,\r
+                ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(\r
+                    _LogWorkItemAndInvokeUserLambda(_M_function)));\r
+        }\r
+#endif /* defined (__cplusplus_winrt) */\r
+    };\r
+\r
+    /// <summary>\r
+    ///     The task handle type used to create a 'continuation task'.\r
+    /// </summary>\r
+    template<typename _InternalReturnType,\r
+             typename _ContinuationReturnType,\r
+             typename _Function,\r
+             typename _IsTaskBased,\r
+             typename _TypeSelection>\r
+    struct _ContinuationTaskHandle\r
+        : details::_PPLTaskHandle<typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type,\r
+                                  _ContinuationTaskHandle<_InternalReturnType,\r
+                                                          _ContinuationReturnType,\r
+                                                          _Function,\r
+                                                          _IsTaskBased,\r
+                                                          _TypeSelection>,\r
+                                  details::_ContinuationTaskHandleBase>\r
+    {\r
+        typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type\r
+            _NormalizedContinuationReturnType;\r
+\r
+        typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl;\r
+        typename details::_CopyableFunctor<typename std::decay<_Function>::type>::_Type _M_function;\r
+\r
+        template<class _ForwardedFunction>\r
+        _ContinuationTaskHandle(\r
+            const typename details::_Task_ptr<_ReturnType>::_Type& _AncestorImpl,\r
+            const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type& _ContinuationImpl,\r
+            _ForwardedFunction&& _Func,\r
+            const task_continuation_context& _Context,\r
+            details::_TaskInliningMode_t _InliningMode)\r
+            : details::_PPLTaskHandle<typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type,\r
+                                      _ContinuationTaskHandle<_InternalReturnType,\r
+                                                              _ContinuationReturnType,\r
+                                                              _Function,\r
+                                                              _IsTaskBased,\r
+                                                              _TypeSelection>,\r
+                                      details::_ContinuationTaskHandleBase>::_PPLTaskHandle(_ContinuationImpl)\r
+            , _M_ancestorTaskImpl(_AncestorImpl)\r
+            , _M_function(std::forward<_ForwardedFunction>(_Func))\r
+        {\r
+            this->_M_isTaskBasedContinuation = _IsTaskBased::value;\r
+            this->_M_continuationContext = _Context;\r
+            this->_M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware());\r
+            this->_M_inliningMode = _InliningMode;\r
+        }\r
+\r
+        virtual ~_ContinuationTaskHandle() {}\r
+\r
+        template<typename _Func, typename _Arg>\r
+        auto _LogWorkItemAndInvokeUserLambda(_Func&& _func, _Arg&& _value) const\r
+            -> decltype(_func(std::forward<_Arg>(_value)))\r
+        {\r
+            details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger);\r
+            (void)_LogWorkItem;\r
+            return _func(std::forward<_Arg>(_value));\r
+        }\r
+\r
+        void _Perform() const { _Continue(_IsTaskBased(), _TypeSelection()); }\r
+\r
+        void _SyncCancelAndPropagateException() const\r
+        {\r
+            if (_M_ancestorTaskImpl->_HasUserException())\r
+            {\r
+                // If the ancestor encountered an exception, transfer the exception to the continuation\r
+                // This traverses down the tree to propagate the exception.\r
+                this->_M_pTask->_CancelWithExceptionHolder(_M_ancestorTaskImpl->_GetExceptionHolder(), true);\r
+            }\r
+            else\r
+            {\r
+                // If the ancestor was canceled, then your own execution should be canceled.\r
+                // This traverses down the tree to cancel it.\r
+                this->_M_pTask->_Cancel(true);\r
+            }\r
+        }\r
+\r
+        //\r
+        // Overload 0-0: _InternalReturnType -> _TaskType\r
+        //\r
+        // This is a straight task continuation which simply invokes its target with the ancestor's completion argument\r
+        //\r
+        void _Continue(std::false_type, details::_TypeSelectorNoAsync) const\r
+        {\r
+            this->_M_pTask->_FinalizeAndRunContinuations(_LogWorkItemAndInvokeUserLambda(\r
+                _Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function),\r
+                _M_ancestorTaskImpl->_GetResult()));\r
+        }\r
+\r
+        //\r
+        // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>^ (only under /ZW)\r
+        //               or\r
+        //               _InternalReturnType -> task<_TaskType>\r
+        //\r
+        // This is a straight task continuation which returns an async operation or a task which will be unwrapped for\r
+        // continuation Depending on the output type, the right _AsyncInit gets invoked\r
+        //\r
+        void _Continue(std::false_type, details::_TypeSelectorAsyncOperationOrTask) const\r
+        {\r
+            typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType;\r
+\r
+            details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(\r
+                this->_M_pTask,\r
+                _LogWorkItemAndInvokeUserLambda(\r
+                    _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function),\r
+                    _M_ancestorTaskImpl->_GetResult()));\r
+        }\r
+\r
+#if defined(__cplusplus_winrt)\r
+        //\r
+        // Overload 0-2: _InternalReturnType -> IAsyncAction^\r
+        //\r
+        // This is a straight task continuation which returns an async action which will be unwrapped for continuation\r
+        //\r
+        void _Continue(std::false_type, details::_TypeSelectorAsyncAction) const\r
+        {\r
+            typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType;\r
+\r
+            details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(\r
+                this->_M_pTask,\r
+                ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(\r
+                    _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function),\r
+                    _M_ancestorTaskImpl->_GetResult())));\r
+        }\r
+\r
+        //\r
+        // Overload 0-3: _InternalReturnType -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^\r
+        //\r
+        // This is a straight task continuation which returns an async operation with progress which will be unwrapped\r
+        // for continuation\r
+        //\r
+        void _Continue(std::false_type, details::_TypeSelectorAsyncOperationWithProgress) const\r
+        {\r
+            typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType;\r
+\r
+            auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(\r
+                _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function),\r
+                _M_ancestorTaskImpl->_GetResult());\r
+            typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType;\r
+\r
+            details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(\r
+                this->_M_pTask,\r
+                ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType,\r
+                                                                                       _ProgressType>(_OpWithProgress));\r
+        }\r
+\r
+        //\r
+        // Overload 0-4: _InternalReturnType -> IAsyncActionWithProgress<_ProgressType>^\r
+        //\r
+        // This is a straight task continuation which returns an async action with progress which will be unwrapped for\r
+        // continuation\r
+        //\r
+        void _Continue(std::false_type, details::_TypeSelectorAsyncActionWithProgress) const\r
+        {\r
+            typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType;\r
+\r
+            auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(\r
+                _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function),\r
+                _M_ancestorTaskImpl->_GetResult());\r
+            typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType;\r
+\r
+            details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(\r
+                this->_M_pTask,\r
+                ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress));\r
+        }\r
+\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+        //\r
+        // Overload 1-0: task<_InternalReturnType> -> _TaskType\r
+        //\r
+        // This is an exception handling type of continuation which takes the task rather than the task's result.\r
+        //\r
+        void _Continue(std::true_type, details::_TypeSelectorNoAsync) const\r
+        {\r
+            typedef task<_InternalReturnType> _FuncInputType;\r
+            task<_InternalReturnType> _ResultTask;\r
+            _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl));\r
+            this->_M_pTask->_FinalizeAndRunContinuations(_LogWorkItemAndInvokeUserLambda(\r
+                _Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function),\r
+                std::move(_ResultTask)));\r
+        }\r
+\r
+        //\r
+        // Overload 1-1: task<_InternalReturnType> -> IAsyncOperation<_TaskType>^\r
+        //                                            or\r
+        //                                            task<_TaskType>\r
+        //\r
+        // This is an exception handling type of continuation which takes the task rather than\r
+        // the task's result. It also returns an async operation or a task which will be unwrapped\r
+        // for continuation\r
+        //\r
+        void _Continue(std::true_type, details::_TypeSelectorAsyncOperationOrTask) const\r
+        {\r
+            // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task.\r
+            task<_InternalReturnType> _ResultTask;\r
+            _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl));\r
+            details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(\r
+                this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)));\r
+        }\r
+\r
+#if defined(__cplusplus_winrt)\r
+\r
+        //\r
+        // Overload 1-2: task<_InternalReturnType> -> IAsyncAction^\r
+        //\r
+        // This is an exception handling type of continuation which takes the task rather than\r
+        // the task's result. It also returns an async action which will be unwrapped for continuation\r
+        //\r
+        void _Continue(std::true_type, details::_TypeSelectorAsyncAction) const\r
+        {\r
+            // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task.\r
+            task<_InternalReturnType> _ResultTask;\r
+            _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl));\r
+            details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(\r
+                this->_M_pTask,\r
+                ref new details::_IAsyncActionToAsyncOperationConverter(\r
+                    _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))));\r
+        }\r
+\r
+        //\r
+        // Overload 1-3: task<_InternalReturnType> -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^\r
+        //\r
+        // This is an exception handling type of continuation which takes the task rather than\r
+        // the task's result. It also returns an async operation with progress which will be unwrapped\r
+        // for continuation\r
+        //\r
+        void _Continue(std::true_type, details::_TypeSelectorAsyncOperationWithProgress) const\r
+        {\r
+            // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task.\r
+            task<_InternalReturnType> _ResultTask;\r
+            _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl));\r
+\r
+            typedef details::_GetProgressType<decltype(_M_function(_ResultTask))>::_Value _ProgressType;\r
+\r
+            details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(\r
+                this->_M_pTask,\r
+                ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType,\r
+                                                                                       _ProgressType>(\r
+                    _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))));\r
+        }\r
+\r
+        //\r
+        // Overload 1-4: task<_InternalReturnType> -> IAsyncActionWithProgress<_ProgressType>^\r
+        //\r
+        // This is an exception handling type of continuation which takes the task rather than\r
+        // the task's result. It also returns an async operation with progress which will be unwrapped\r
+        // for continuation\r
+        //\r
+        void _Continue(std::true_type, details::_TypeSelectorAsyncActionWithProgress) const\r
+        {\r
+            // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task.\r
+            task<_InternalReturnType> _ResultTask;\r
+            _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl));\r
+\r
+            typedef details::_GetProgressType<decltype(_M_function(_ResultTask))>::_Value _ProgressType;\r
+\r
+            details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(\r
+                this->_M_pTask,\r
+                ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(\r
+                    _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))));\r
+        }\r
+#endif /* defined (__cplusplus_winrt) */\r
+    };\r
+\r
+    /// <summary>\r
+    ///     Initializes a task using a lambda, function pointer or function object.\r
+    /// </summary>\r
+    template<typename _InternalReturnType, typename _Function>\r
+    void _TaskInitWithFunctor(const _Function& _Func)\r
+    {\r
+        typedef typename details::_InitFunctorTypeTraits<_InternalReturnType, decltype(_Func())> _Async_type_traits;\r
+\r
+        _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask;\r
+        _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync;\r
+        _M_Impl->_M_taskEventLogger._LogScheduleTask(false);\r
+        _M_Impl->_ScheduleTask(\r
+            new _InitialTaskHandle<_InternalReturnType, _Function, typename _Async_type_traits::_AsyncKind>(_GetImpl(),\r
+                                                                                                            _Func),\r
+            details::_NoInline);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Initializes a task using a task completion event.\r
+    /// </summary>\r
+    void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) { _Event._RegisterTask(_M_Impl); }\r
+\r
+#if defined(__cplusplus_winrt)\r
+    /// <summary>\r
+    ///     Initializes a task using an asynchronous operation IAsyncOperation<T>^\r
+    /// </summary>\r
+    void _TaskInitAsyncOp(\r
+        Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value> ^ _AsyncOp)\r
+    {\r
+        _M_Impl->_M_fFromAsync = true;\r
+\r
+        // Mark this task as started here since we can set the state in the constructor without acquiring a lock. Once\r
+        // _AsyncInit returns a completion could execute concurrently and the task must be fully initialized before that\r
+        // happens.\r
+        _M_Impl->_M_TaskState = details::_Task_impl_base::_Started;\r
+        // Pass the shared pointer into _AsyncInit for storage in the Async Callback.\r
+        details::_Task_impl_base::_AsyncInit<_ReturnType, _ReturnType>(_M_Impl, _AsyncOp);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Initializes a task using an asynchronous operation IAsyncOperation<T>^\r
+    /// </summary>\r
+    void _TaskInitNoFunctor(\r
+        Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value> ^ _AsyncOp)\r
+    {\r
+        _TaskInitAsyncOp(_AsyncOp);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress<T, P>^\r
+    /// </summary>\r
+    template<typename _Progress>\r
+    void _TaskInitNoFunctor(\r
+        Windows::Foundation::IAsyncOperationWithProgress<typename details::_ValueTypeOrRefType<_ReturnType>::_Value,\r
+                                                         _Progress> ^\r
+        _AsyncOp)\r
+    {\r
+        _TaskInitAsyncOp(ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<\r
+                         typename details::_ValueTypeOrRefType<_ReturnType>::_Value,\r
+                         _Progress>(_AsyncOp));\r
+    }\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+    /// <summary>\r
+    ///     Initializes a task using a callable object.\r
+    /// </summary>\r
+    template<typename _Function>\r
+    void _TaskInitMaybeFunctor(_Function& _Func, std::true_type)\r
+    {\r
+        _TaskInitWithFunctor<_ReturnType, _Function>(_Func);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Initializes a task using a non-callable object.\r
+    /// </summary>\r
+    template<typename _Ty>\r
+    void _TaskInitMaybeFunctor(_Ty& _Param, std::false_type)\r
+    {\r
+        _TaskInitNoFunctor(_Param);\r
+    }\r
+\r
+    template<typename _InternalReturnType, typename _Function>\r
+    auto _ThenImpl(_Function&& _Func, const task_options& _TaskOptions) const ->\r
+        typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType\r
+    {\r
+        if (!_M_Impl)\r
+        {\r
+            throw invalid_operation("then() cannot be called on a default constructed task.");\r
+        }\r
+\r
+        details::_CancellationTokenState* _PTokenState =\r
+            _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;\r
+        auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler();\r
+        auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack\r
+                                  ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack\r
+                                  : details::_TaskCreationCallstack();\r
+        return _ThenImpl<_InternalReturnType, _Function>(std::forward<_Function>(_Func),\r
+                                                         _PTokenState,\r
+                                                         _TaskOptions.get_continuation_context(),\r
+                                                         _Scheduler,\r
+                                                         _CreationStack);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     The one and only implementation of then for void and non-void tasks.\r
+    /// </summary>\r
+    template<typename _InternalReturnType, typename _Function>\r
+    auto _ThenImpl(_Function&& _Func,\r
+                   details::_CancellationTokenState* _PTokenState,\r
+                   const task_continuation_context& _ContinuationContext,\r
+                   scheduler_ptr _Scheduler,\r
+                   details::_TaskCreationCallstack _CreationStack,\r
+                   details::_TaskInliningMode_t _InliningMode = details::_NoInline) const ->\r
+        typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType\r
+    {\r
+        if (!_M_Impl)\r
+        {\r
+            throw invalid_operation("then() cannot be called on a default constructed task.");\r
+        }\r
+\r
+        typedef details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits;\r
+        typedef details::_TaskTypeTraits<typename _Function_type_traits::_FuncRetType> _Async_type_traits;\r
+        typedef typename _Async_type_traits::_TaskRetType _TaskType;\r
+\r
+        //\r
+        // A **nullptr** token state indicates that it was not provided by the user. In this case, we inherit the\r
+        // antecedent's token UNLESS this is a an exception handling continuation. In that case, we break the chain with\r
+        // a _None. That continuation is never canceled unless the user explicitly passes the same token.\r
+        //\r
+        if (_PTokenState == nullptr)\r
+        {\r
+            if (_Function_type_traits::_Takes_task::value)\r
+            {\r
+                _PTokenState = details::_CancellationTokenState::_None();\r
+            }\r
+            else\r
+            {\r
+                _PTokenState = _GetImpl()->_M_pTokenState;\r
+            }\r
+        }\r
+\r
+        task<_TaskType> _ContinuationTask;\r
+        _ContinuationTask._CreateImpl(_PTokenState, _Scheduler);\r
+\r
+        _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask);\r
+        _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync;\r
+        _ContinuationTask._SetTaskCreationCallstack(_CreationStack);\r
+\r
+        _GetImpl()->_ScheduleContinuation(\r
+            new _ContinuationTaskHandle<_InternalReturnType,\r
+                                        _TaskType,\r
+                                        _Function,\r
+                                        typename _Function_type_traits::_Takes_task,\r
+                                        typename _Async_type_traits::_AsyncKind>(_GetImpl(),\r
+                                                                                 _ContinuationTask._GetImpl(),\r
+                                                                                 std::forward<_Function>(_Func),\r
+                                                                                 _ContinuationContext,\r
+                                                                                 _InliningMode));\r
+\r
+        return _ContinuationTask;\r
+    }\r
+\r
+    // The underlying implementation for this task\r
+    typename details::_Task_ptr<_ReturnType>::_Type _M_Impl;\r
+};\r
+\r
+/// <summary>\r
+///     The Parallel Patterns Library (PPL) <c>task</c> class. A <c>task</c> object represents work that can be executed\r
+///     asynchronously, and concurrently with other tasks and parallel work produced by parallel algorithms in the\r
+///     Concurrency Runtime. It produces a result of type <typeparamref name="_ResultType"/> on successful completion.\r
+///     Tasks of type <c>task&lt;void&gt;</c> produce no result. A task can be waited upon and canceled independently of\r
+///     other tasks. It can also be composed with other tasks using continuations(<c>then</c>), and\r
+///     join(<c>when_all</c>) and choice(<c>when_any</c>) patterns.\r
+/// </summary>\r
+/// <remarks>\r
+///     For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.\r
+/// </remarks>\r
+/**/\r
+template<>\r
+class task<void>\r
+{\r
+public:\r
+    /// <summary>\r
+    ///     The type of the result an object of this class produces.\r
+    /// </summary>\r
+    /**/\r
+    typedef void result_type;\r
+\r
+    /// <summary>\r
+    ///     Constructs a <c>task</c> object.\r
+    /// </summary>\r
+    /// <remarks>\r
+    ///     The default constructor for a <c>task</c> is only present in order to allow tasks to be used within\r
+    ///     containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as\r
+    ///     <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument\r
+    ///     Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is\r
+    ///     created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the\r
+    ///     task completion event is set.</para> <para>The version of the constructor that takes a cancellation token\r
+    ///     creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.\r
+    ///     Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a\r
+    ///     <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface\r
+    ///     reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.\r
+    ///     Similarly, tasks created from a lambda that returns a <c>task&lt;result_type&gt;</c> reach their terminal\r
+    ///     state when the inner task reaches its terminal state, and not when the lambda returns.</para>\r
+    ///     <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by\r
+    ///     multiple threads without the need for locks.</para> <para>The constructor overloads that take a\r
+    ///     Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to\r
+    ///     Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency\r
+    ///     Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    task() : _M_unitTask()\r
+    {\r
+        // The default constructor should create a task with a nullptr impl. This is a signal that the\r
+        // task is not usable and should throw if any wait(), get() or then() APIs are used.\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Constructs a <c>task</c> object.\r
+    /// </summary>\r
+    /// <typeparam name="_Ty">\r
+    ///     The type of the parameter from which the task is to be constructed.\r
+    /// </typeparam>\r
+    /// <param name="_Param">\r
+    ///     The parameter from which the task is to be constructed. This could be a lambda, a function object, a\r
+    ///     <c>task_completion_event&lt;result_type&gt;</c> object, or a Windows::Foundation::IAsyncInfo if you are\r
+    ///     using tasks in your Windows Store app. The lambda or function object should be a type equivalent to\r
+    ///     <c>std::function&lt;X(void)&gt;</c>, where X can be a variable of type <c>result_type</c>,\r
+    ///     <c>task&lt;result_type&gt;</c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps.\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     The default constructor for a <c>task</c> is only present in order to allow tasks to be used within\r
+    ///     containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as\r
+    ///     <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument\r
+    ///     Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is\r
+    ///     created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the\r
+    ///     task completion event is set.</para> <para>The version of the constructor that takes a cancellation token\r
+    ///     creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.\r
+    ///     Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a\r
+    ///     <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface\r
+    ///     reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.\r
+    ///     Similarly, tasks created from a lambda that returns a <c>task&lt;result_type&gt;</c> reach their terminal\r
+    ///     state when the inner task reaches its terminal state, and not when the lambda returns.</para>\r
+    ///     <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by\r
+    ///     multiple threads without the need for locks.</para> <para>The constructor overloads that take a\r
+    ///     Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to\r
+    ///     Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency\r
+    ///     Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    template<typename _Ty>\r
+    __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result\r
+        explicit task(_Ty _Param, const task_options& _TaskOptions = task_options())\r
+    {\r
+        details::_ValidateTaskConstructorArgs<void, _Ty>(_Param);\r
+\r
+        _M_unitTask._CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler());\r
+        // Do not move the next line out of this function. It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the\r
+        // the call site of the task constructor.\r
+        _M_unitTask._SetTaskCreationCallstack(\r
+            details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack\r
+                ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack\r
+                : PPLX_CAPTURE_CALLSTACK());\r
+\r
+        _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param, 0));\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Constructs a <c>task</c> object.\r
+    /// </summary>\r
+    /// <param name="_Other">\r
+    ///     The source <c>task</c> object.\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     The default constructor for a <c>task</c> is only present in order to allow tasks to be used within\r
+    ///     containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as\r
+    ///     <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument\r
+    ///     Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is\r
+    ///     created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the\r
+    ///     task completion event is set.</para> <para>The version of the constructor that takes a cancellation token\r
+    ///     creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.\r
+    ///     Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a\r
+    ///     <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface\r
+    ///     reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.\r
+    ///     Similarly, tasks created from a lambda that returns a <c>task&lt;result_type&gt;</c> reach their terminal\r
+    ///     state when the inner task reaches its terminal state, and not when the lambda returns.</para>\r
+    ///     <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by\r
+    ///     multiple threads without the need for locks.</para> <para>The constructor overloads that take a\r
+    ///     Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to\r
+    ///     Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency\r
+    ///     Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    task(const task& _Other) : _M_unitTask(_Other._M_unitTask) {}\r
+\r
+    /// <summary>\r
+    ///     Constructs a <c>task</c> object.\r
+    /// </summary>\r
+    /// <param name="_Other">\r
+    ///     The source <c>task</c> object.\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     The default constructor for a <c>task</c> is only present in order to allow tasks to be used within\r
+    ///     containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as\r
+    ///     <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument\r
+    ///     Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is\r
+    ///     created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the\r
+    ///     task completion event is set.</para> <para>The version of the constructor that takes a cancellation token\r
+    ///     creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.\r
+    ///     Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a\r
+    ///     <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface\r
+    ///     reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.\r
+    ///     Similarly, tasks created from a lambda that returns a <c>task&lt;result_type&gt;</c> reach their terminal\r
+    ///     state when the inner task reaches its terminal state, and not when the lambda returns.</para>\r
+    ///     <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by\r
+    ///     multiple threads without the need for locks.</para> <para>The constructor overloads that take a\r
+    ///     Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to\r
+    ///     Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency\r
+    ///     Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {}\r
+\r
+    /// <summary>\r
+    ///     Replaces the contents of one <c>task</c> object with another.\r
+    /// </summary>\r
+    /// <param name="_Other">\r
+    ///     The source <c>task</c> object.\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents\r
+    ///     the same actual task as <paramref name="_Other"/> does.\r
+    /// </remarks>\r
+    /**/\r
+    task& operator=(const task& _Other)\r
+    {\r
+        if (this != &_Other)\r
+        {\r
+            _M_unitTask = _Other._M_unitTask;\r
+        }\r
+        return *this;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Replaces the contents of one <c>task</c> object with another.\r
+    /// </summary>\r
+    /// <param name="_Other">\r
+    ///     The source <c>task</c> object.\r
+    /// </param>\r
+    /// <remarks>\r
+    ///     As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents\r
+    ///     the same actual task as <paramref name="_Other"/> does.\r
+    /// </remarks>\r
+    /**/\r
+    task& operator=(task&& _Other)\r
+    {\r
+        if (this != &_Other)\r
+        {\r
+            _M_unitTask = std::move(_Other._M_unitTask);\r
+        }\r
+        return *this;\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Adds a continuation task to this task.\r
+    /// </summary>\r
+    /// <typeparam name="_Function">\r
+    ///     The type of the function object that will be invoked by this task.\r
+    /// </typeparam>\r
+    /// <param name="_Func">\r
+    ///     The continuation function to execute when this task completes. This continuation function must take as input\r
+    ///     a variable of either <c>result_type</c> or <c>task&lt;result_type&gt;</c>, where <c>result_type</c> is the\r
+    ///     type of the result this task produces.\r
+    /// </param>\r
+    /// <param name="_CancellationToken">\r
+    ///     The cancellation token to associate with the continuation task. A continuation task that is created without\r
+    ///     a cancellation token will inherit the token of its antecedent task.\r
+    /// </param>\r
+    /// <returns>\r
+    ///     The newly created continuation task. The result type of the returned task is determined by what <paramref\r
+    ///     name="_Func"/> returns.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo\r
+    ///     interface, are only available to Windows Store apps. <para>For more information on how to use task\r
+    ///     continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    template<typename _Function>\r
+    __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result\r
+        auto then(_Function&& _Func, task_options _TaskOptions = task_options()) const ->\r
+        typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType\r
+    {\r
+        details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());\r
+        return _M_unitTask._ThenImpl<void, _Function>(std::forward<_Function>(_Func), _TaskOptions);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Adds a continuation task to this task.\r
+    /// </summary>\r
+    /// <typeparam name="_Function">\r
+    ///     The type of the function object that will be invoked by this task.\r
+    /// </typeparam>\r
+    /// <param name="_Func">\r
+    ///     The continuation function to execute when this task completes. This continuation function must take as input\r
+    ///     a variable of either <c>result_type</c> or <c>task&lt;result_type&gt;</c>, where <c>result_type</c> is the\r
+    ///     type of the result this task produces.\r
+    /// </param>\r
+    /// <param name="_CancellationToken">\r
+    ///     The cancellation token to associate with the continuation task. A continuation task that is created without\r
+    ///     a cancellation token will inherit the token of its antecedent task.\r
+    /// </param>\r
+    /// <param name="_ContinuationContext">\r
+    ///     A variable that specifies where the continuation should execute. This variable is only useful when used in a\r
+    ///     Windows Store style app. For more information, see <see cref="task_continuation_context\r
+    ///     Class">task_continuation_context</see>\r
+    /// </param>\r
+    /// <returns>\r
+    ///     The newly created continuation task. The result type of the returned task is determined by what <paramref\r
+    ///     name="_Func"/> returns.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo\r
+    ///     interface, are only available to Windows Store apps. <para>For more information on how to use task\r
+    ///     continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>\r
+    /// </remarks>\r
+    /**/\r
+    template<typename _Function>\r
+    __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result\r
+        auto then(_Function&& _Func,\r
+                  cancellation_token _CancellationToken,\r
+                  task_continuation_context _ContinuationContext) const ->\r
+        typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType\r
+    {\r
+        task_options _TaskOptions(_CancellationToken, _ContinuationContext);\r
+        details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());\r
+        return _M_unitTask._ThenImpl<void, _Function>(std::forward<_Function>(_Func), _TaskOptions);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Waits for this task to reach a terminal state. It is possible for <c>wait</c> to execute the task inline, if\r
+    ///     all of the tasks dependencies are satisfied, and it has not already been picked up for execution by a\r
+    ///     background worker.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     A <c>task_status</c> value which could be either <c>completed</c> or <c>canceled</c>. If the task\r
+    ///     encountered an exception during execution, or an exception was propagated to it from an antecedent task,\r
+    ///     <c>wait</c> will throw that exception.\r
+    /// </returns>\r
+    /**/\r
+    task_status wait() const { return _M_unitTask.wait(); }\r
+\r
+    /// <summary>\r
+    ///     Returns the result this task produced. If the task is not in a terminal state, a call to <c>get</c> will\r
+    ///     wait for the task to finish. This method does not return a value when called on a task with a\r
+    ///     <c>result_type</c> of <c>void</c>.\r
+    /// </summary>\r
+    /// <remarks>\r
+    ///     If the task is canceled, a call to <c>get</c> will throw a <see cref="task_canceled\r
+    ///     Class">task_canceled</see> exception. If the task encountered an different exception or an exception was\r
+    ///     propagated to it from an antecedent task, a call to <c>get</c> will throw that exception.\r
+    /// </remarks>\r
+    /**/\r
+    void get() const { _M_unitTask.get(); }\r
+\r
+    /// <summary>\r
+    ///     Determines if the task is completed.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     True if the task has completed, false otherwise.\r
+    /// </returns>\r
+    /// <remarks>\r
+    ///     The function returns true if the task is completed or canceled (with or without user exception).\r
+    /// </remarks>\r
+    bool is_done() const { return _M_unitTask.is_done(); }\r
+\r
+    /// <summary>\r
+    ///     Returns the scheduler for this task\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     A pointer to the scheduler\r
+    /// </returns>\r
+    scheduler_ptr scheduler() const { return _M_unitTask.scheduler(); }\r
+\r
+    /// <summary>\r
+    ///     Determines whether the task unwraps a Windows Runtime <c>IAsyncInfo</c> interface or is descended from such\r
+    ///     a task.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     <c>true</c> if the task unwraps an <c>IAsyncInfo</c> interface or is descended from such a task,\r
+    ///     <c>false</c> otherwise.\r
+    /// </returns>\r
+    /**/\r
+    bool is_apartment_aware() const { return _M_unitTask.is_apartment_aware(); }\r
+\r
+    /// <summary>\r
+    ///     Determines whether two <c>task</c> objects represent the same internal task.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     <c>true</c> if the objects refer to the same underlying task, and <c>false</c> otherwise.\r
+    /// </returns>\r
+    /**/\r
+    bool operator==(const task<void>& _Rhs) const { return (_M_unitTask == _Rhs._M_unitTask); }\r
+\r
+    /// <summary>\r
+    ///     Determines whether two <c>task</c> objects represent different internal tasks.\r
+    /// </summary>\r
+    /// <returns>\r
+    ///     <c>true</c> if the objects refer to different underlying tasks, and <c>false</c> otherwise.\r
+    /// </returns>\r
+    /**/\r
+    bool operator!=(const task<void>& _Rhs) const { return !operator==(_Rhs); }\r
+\r
+    /// <summary>\r
+    ///     Create an underlying task implementation.\r
+    /// </summary>\r
+    void _CreateImpl(details::_CancellationTokenState* _Ct, scheduler_ptr _Scheduler)\r
+    {\r
+        _M_unitTask._CreateImpl(_Ct, _Scheduler);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Return the underlying implementation for this task.\r
+    /// </summary>\r
+    const details::_Task_ptr<details::_Unit_type>::_Type& _GetImpl() const { return _M_unitTask._M_Impl; }\r
+\r
+    /// <summary>\r
+    ///     Set the implementation of the task to be the supplied implementation.\r
+    /// </summary>\r
+    void _SetImpl(const details::_Task_ptr<details::_Unit_type>::_Type& _Impl) { _M_unitTask._SetImpl(_Impl); }\r
+\r
+    /// <summary>\r
+    ///     Set the implementation of the task to be the supplied implementation using a move instead of a copy.\r
+    /// </summary>\r
+    void _SetImpl(details::_Task_ptr<details::_Unit_type>::_Type&& _Impl) { _M_unitTask._SetImpl(std::move(_Impl)); }\r
+\r
+    /// <summary>\r
+    ///     Sets a property determining whether the task is apartment aware.\r
+    /// </summary>\r
+    void _SetAsync(bool _Async = true) { _M_unitTask._SetAsync(_Async); }\r
+\r
+    /// <summary>\r
+    ///     Sets a field in the task impl to the return callstack for calls to the task constructors and the then\r
+    ///     method.\r
+    /// </summary>\r
+    void _SetTaskCreationCallstack(const details::_TaskCreationCallstack& _callstack)\r
+    {\r
+        _M_unitTask._SetTaskCreationCallstack(_callstack);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     An internal version of then that takes additional flags and executes the continuation inline. Used for\r
+    ///     runtime internal continuations only.\r
+    /// </summary>\r
+    template<typename _Function>\r
+    auto _Then(_Function&& _Func,\r
+               details::_CancellationTokenState* _PTokenState,\r
+               details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const ->\r
+        typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType\r
+    {\r
+        // inherit from antecedent\r
+        auto _Scheduler = _GetImpl()->_GetScheduler();\r
+\r
+        return _M_unitTask._ThenImpl<void, _Function>(std::forward<_Function>(_Func),\r
+                                                      _PTokenState,\r
+                                                      task_continuation_context::use_default(),\r
+                                                      _Scheduler,\r
+                                                      PPLX_CAPTURE_CALLSTACK(),\r
+                                                      _InliningMode);\r
+    }\r
+\r
+private:\r
+    template<typename T>\r
+    friend class task;\r
+    template<typename T>\r
+    friend class task_completion_event;\r
+\r
+    /// <summary>\r
+    ///     Initializes a task using a task completion event.\r
+    /// </summary>\r
+    void _TaskInitNoFunctor(task_completion_event<void>& _Event)\r
+    {\r
+        _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent);\r
+    }\r
+\r
+#if defined(__cplusplus_winrt)\r
+    /// <summary>\r
+    ///     Initializes a task using an asynchronous action IAsyncAction^\r
+    /// </summary>\r
+    void _TaskInitNoFunctor(Windows::Foundation::IAsyncAction ^ _AsyncAction)\r
+    {\r
+        _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionToAsyncOperationConverter(_AsyncAction));\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>^\r
+    /// </summary>\r
+    template<typename _P>\r
+    void _TaskInitNoFunctor(Windows::Foundation::IAsyncActionWithProgress<_P> ^ _AsyncActionWithProgress)\r
+    {\r
+        _M_unitTask._TaskInitAsyncOp(\r
+            ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_P>(_AsyncActionWithProgress));\r
+    }\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+    /// <summary>\r
+    ///     Initializes a task using a callable object.\r
+    /// </summary>\r
+    template<typename _Function>\r
+    void _TaskInitMaybeFunctor(_Function& _Func, std::true_type)\r
+    {\r
+        _M_unitTask._TaskInitWithFunctor<void, _Function>(_Func);\r
+    }\r
+\r
+    /// <summary>\r
+    ///     Initializes a task using a non-callable object.\r
+    /// </summary>\r
+    template<typename _T>\r
+    void _TaskInitMaybeFunctor(_T& _Param, std::false_type)\r
+    {\r
+        _TaskInitNoFunctor(_Param);\r
+    }\r
+\r
+    // The void task contains a task of a dummy type so common code can be used for tasks with void and non-void\r
+    // results.\r
+    task<details::_Unit_type> _M_unitTask;\r
+};\r
+\r
+namespace details\r
+{\r
+/// <summary>\r
+///   The following type traits are used for the create_task function.\r
+/// </summary>\r
+\r
+#if defined(__cplusplus_winrt)\r
+// Unwrap functions for asyncOperations\r
+template<typename _Ty>\r
+_Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperation<_Ty> ^);\r
+\r
+void _GetUnwrappedType(Windows::Foundation::IAsyncAction ^);\r
+\r
+template<typename _Ty, typename _Progress>\r
+_Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress> ^);\r
+\r
+template<typename _Progress>\r
+void _GetUnwrappedType(Windows::Foundation::IAsyncActionWithProgress<_Progress> ^);\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+// Unwrap task<T>\r
+template<typename _Ty>\r
+_Ty _GetUnwrappedType(task<_Ty>);\r
+\r
+// Unwrap all supported types\r
+template<typename _Ty>\r
+auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg));\r
+// fallback\r
+template<typename _Ty>\r
+_Ty _GetUnwrappedReturnType(_Ty, ...);\r
+\r
+/// <summary>\r
+///   <c>_GetTaskType</c> functions will retrieve task type <c>T</c> in <c>task[T](Arg)</c>,\r
+///   for given constructor argument <c>Arg</c> and its property "callable".\r
+///   It will automatically unwrap argument to get the final return type if necessary.\r
+/// </summary>\r
+\r
+// Non-Callable\r
+template<typename _Ty>\r
+_Ty _GetTaskType(task_completion_event<_Ty>, std::false_type);\r
+\r
+// Non-Callable\r
+template<typename _Ty>\r
+auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc));\r
+\r
+// Callable\r
+template<typename _Ty>\r
+auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(_Func(), 0));\r
+\r
+// Special callable returns void\r
+void _GetTaskType(std::function<void()>, std::true_type);\r
+struct _BadArgType\r
+{\r
+};\r
+\r
+template<typename _Ty>\r
+auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable(_Param, 0)));\r
+\r
+template<typename _Ty>\r
+_BadArgType _FilterValidTaskType(_Ty _Param, ...);\r
+\r
+template<typename _Ty>\r
+struct _TaskTypeFromParam\r
+{\r
+    typedef decltype(_FilterValidTaskType(stdx::declval<_Ty>(), 0)) _Type;\r
+};\r
+} // namespace details\r
+\r
+/// <summary>\r
+///     Creates a PPL <see cref="task Class">task</see> object. <c>create_task</c> can be used anywhere you would have\r
+///     used a task constructor. It is provided mainly for convenience, because it allows use of the <c>auto</c> keyword\r
+///     while creating tasks.\r
+/// </summary>\r
+/// <typeparam name="_Ty">\r
+///     The type of the parameter from which the task is to be constructed.\r
+/// </typeparam>\r
+/// <param name="_Param">\r
+///     The parameter from which the task is to be constructed. This could be a lambda or function object, a\r
+///     <c>task_completion_event</c> object, a different <c>task</c> object, or a Windows::Foundation::IAsyncInfo\r
+///     interface if you are using tasks in your Windows Store app.\r
+/// </param>\r
+/// <returns>\r
+///     A new task of type <c>T</c>, that is inferred from <paramref name="_Param"/>.\r
+/// </returns>\r
+/// <remarks>\r
+///     The first overload behaves like a task constructor that takes a single parameter.\r
+///     <para>The second overload associates the cancellation token provided with the newly created task. If you use\r
+///     this overload you are not allowed to pass in a different <c>task</c> object as the first parameter.</para>\r
+///     <para>The type of the returned task is inferred from the first parameter to the function. If <paramref\r
+///     name="_Param"/> is a <c>task_completion_event&lt;T&gt;</c>, a <c>task&lt;T&gt;</c>, or a functor that returns\r
+///     either type <c>T</c> or <c>task&lt;T&gt;</c>, the type of the created task is <c>task&lt;T&gt;</c>.</para>\r
+///     <para>In a Windows Store app, if <paramref name="_Param"/> is of type\r
+///     Windows::Foundation::IAsyncOperation&lt;T&gt;^ or Windows::Foundation::IAsyncOperationWithProgress&lt;T,P&gt;^,\r
+///     or a functor that returns either of those types, the created task will be of type <c>task&lt;T&gt;</c>. If\r
+///     <paramref name="_Param"/> is of type Windows::Foundation::IAsyncAction^ or\r
+///     Windows::Foundation::IAsyncActionWithProgress&lt;P&gt;^, or a functor that returns either of those types, the\r
+///     created task will have type <c>task&lt;void&gt;</c>.</para>\r
+/// </remarks>\r
+/// <seealso cref="task Class"/>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _Ty>\r
+__declspec(noinline) auto create_task(_Ty _Param, task_options _TaskOptions = task_options())\r
+    -> task<typename details::_TaskTypeFromParam<_Ty>::_Type>\r
+{\r
+    static_assert(!std::is_same<typename details::_TaskTypeFromParam<_Ty>::_Type, details::_BadArgType>::value,\r
+#if defined(__cplusplus_winrt)\r
+                  "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a "\r
+                  "task_completion_event"\r
+#else  /* defined (__cplusplus_winrt) */\r
+                  "incorrect argument for create_task; can be a callable object or a task_completion_event"\r
+#endif /* defined (__cplusplus_winrt) */\r
+    );\r
+    details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());\r
+    task<typename details::_TaskTypeFromParam<_Ty>::_Type> _CreatedTask(_Param, _TaskOptions);\r
+    return _CreatedTask;\r
+}\r
+\r
+/// <summary>\r
+///     Creates a PPL <see cref="task Class">task</see> object. <c>create_task</c> can be used anywhere you would have\r
+///     used a task constructor. It is provided mainly for convenience, because it allows use of the <c>auto</c> keyword\r
+///     while creating tasks.\r
+/// </summary>\r
+/// <typeparam name="_Ty">\r
+///     The type of the parameter from which the task is to be constructed.\r
+/// </typeparam>\r
+/// <param name="_Param">\r
+///     The parameter from which the task is to be constructed. This could be a lambda or function object, a\r
+///     <c>task_completion_event</c> object, a different <c>task</c> object, or a Windows::Foundation::IAsyncInfo\r
+///     interface if you are using tasks in your Windows Store app.\r
+/// </param>\r
+/// <param name="_Token">\r
+///     The cancellation token to associate with the task. When the source for this token is canceled, cancellation will\r
+///     be requested on the task.\r
+/// </param>\r
+/// <returns>\r
+///     A new task of type <c>T</c>, that is inferred from <paramref name="_Param"/>.\r
+/// </returns>\r
+/// <remarks>\r
+///     The first overload behaves like a task constructor that takes a single parameter.\r
+///     <para>The second overload associates the cancellation token provided with the newly created task. If you use\r
+///     this overload you are not allowed to pass in a different <c>task</c> object as the first parameter.</para>\r
+///     <para>The type of the returned task is inferred from the first parameter to the function. If <paramref\r
+///     name="_Param"/> is a <c>task_completion_event&lt;T&gt;</c>, a <c>task&lt;T&gt;</c>, or a functor that returns\r
+///     either type <c>T</c> or <c>task&lt;T&gt;</c>, the type of the created task is <c>task&lt;T&gt;</c>.</para>\r
+///     <para>In a Windows Store app, if <paramref name="_Param"/> is of type\r
+///     Windows::Foundation::IAsyncOperation&lt;T&gt;^ or Windows::Foundation::IAsyncOperationWithProgress&lt;T,P&gt;^,\r
+///     or a functor that returns either of those types, the created task will be of type <c>task&lt;T&gt;</c>. If\r
+///     <paramref name="_Param"/> is of type Windows::Foundation::IAsyncAction^ or\r
+///     Windows::Foundation::IAsyncActionWithProgress&lt;P&gt;^, or a functor that returns either of those types, the\r
+///     created task will have type <c>task&lt;void&gt;</c>.</para>\r
+/// </remarks>\r
+/// <seealso cref="task Class"/>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _ReturnType>\r
+__declspec(noinline) task<_ReturnType> create_task(const task<_ReturnType>& _Task)\r
+{\r
+    task<_ReturnType> _CreatedTask(_Task);\r
+    return _CreatedTask;\r
+}\r
+\r
+#if defined(__cplusplus_winrt)\r
+namespace details\r
+{\r
+template<typename _T>\r
+task<_T> _To_task_helper(Windows::Foundation::IAsyncOperation<_T> ^ op)\r
+{\r
+    return task<_T>(op);\r
+}\r
+\r
+template<typename _T, typename _Progress>\r
+task<_T> _To_task_helper(Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress> ^ op)\r
+{\r
+    return task<_T>(op);\r
+}\r
+\r
+inline task<void> _To_task_helper(Windows::Foundation::IAsyncAction ^ op) { return task<void>(op); }\r
+\r
+template<typename _Progress>\r
+task<void> _To_task_helper(Windows::Foundation::IAsyncActionWithProgress<_Progress> ^ op)\r
+{\r
+    return task<void>(op);\r
+}\r
+\r
+template<typename _ProgressType>\r
+class _ProgressDispatcherBase\r
+{\r
+public:\r
+    virtual ~_ProgressDispatcherBase() {}\r
+\r
+    virtual void _Report(const _ProgressType& _Val) = 0;\r
+};\r
+\r
+template<typename _ProgressType, typename _ClassPtrType>\r
+class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType>\r
+{\r
+public:\r
+    virtual ~_ProgressDispatcher() {}\r
+\r
+    _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr) {}\r
+\r
+    virtual void _Report(const _ProgressType& _Val) { _M_ptr->_FireProgress(_Val); }\r
+\r
+private:\r
+    _ClassPtrType _M_ptr;\r
+};\r
+class _ProgressReporterCtorArgType\r
+{\r
+};\r
+} // namespace details\r
+\r
+/// <summary>\r
+///     The progress reporter class allows reporting progress notifications of a specific type. Each progress_reporter\r
+///     object is bound to a particular asynchronous action or operation.\r
+/// </summary>\r
+/// <typeparam name="_ProgressType">\r
+///     The payload type of each progress notification reported through the progress reporter.\r
+/// </typeparam>\r
+/// <remarks>\r
+///     This type is only available to Windows Store apps.\r
+/// </remarks>\r
+/// <seealso cref="create_async Function"/>\r
+/**/\r
+template<typename _ProgressType>\r
+class progress_reporter\r
+{\r
+    typedef std::shared_ptr<details::_ProgressDispatcherBase<_ProgressType>> _PtrType;\r
+\r
+public:\r
+    /// <summary>\r
+    ///     Sends a progress report to the asynchronous action or operation to which this progress reporter is bound.\r
+    /// </summary>\r
+    /// <param name="_Val">\r
+    ///     The payload to report through a progress notification.\r
+    /// </param>\r
+    /**/\r
+    void report(const _ProgressType& _Val) const { _M_dispatcher->_Report(_Val); }\r
+\r
+    template<typename _ClassPtrType>\r
+    static progress_reporter _CreateReporter(_ClassPtrType _Ptr)\r
+    {\r
+        progress_reporter _Reporter;\r
+        details::_ProgressDispatcherBase<_ProgressType>* _PDispatcher =\r
+            new details::_ProgressDispatcher<_ProgressType, _ClassPtrType>(_Ptr);\r
+        _Reporter._M_dispatcher = _PtrType(_PDispatcher);\r
+        return _Reporter;\r
+    }\r
+    progress_reporter() {}\r
+\r
+private:\r
+    progress_reporter(details::_ProgressReporterCtorArgType);\r
+\r
+    _PtrType _M_dispatcher;\r
+};\r
+\r
+namespace details\r
+{\r
+//\r
+// maps internal definitions for AsyncStatus and defines states that are not client visible\r
+//\r
+enum _AsyncStatusInternal\r
+{\r
+    _AsyncCreated = -1, // externally invisible\r
+    // client visible states (must match AsyncStatus exactly)\r
+    _AsyncStarted = 0,   // Windows::Foundation::AsyncStatus::Started,\r
+    _AsyncCompleted = 1, // Windows::Foundation::AsyncStatus::Completed,\r
+    _AsyncCanceled = 2,  // Windows::Foundation::AsyncStatus::Canceled,\r
+    _AsyncError = 3,     // Windows::Foundation::AsyncStatus::Error,\r
+    // non-client visible internal states\r
+    _AsyncCancelPending,\r
+    _AsyncClosed,\r
+    _AsyncUndefined\r
+};\r
+\r
+//\r
+// designates whether the "GetResults" method returns a single result (after complete fires) or multiple results\r
+// (which are progressively consumable between Start state and before Close is called)\r
+//\r
+enum _AsyncResultType\r
+{\r
+    SingleResult = 0x0001,\r
+    MultipleResults = 0x0002\r
+};\r
+\r
+// ***************************************************************************\r
+// Template type traits and helpers for async production APIs:\r
+//\r
+\r
+struct _ZeroArgumentFunctor\r
+{\r
+};\r
+struct _OneArgumentFunctor\r
+{\r
+};\r
+struct _TwoArgumentFunctor\r
+{\r
+};\r
+\r
+// ****************************************\r
+// CLASS TYPES:\r
+\r
+// ********************\r
+// TWO ARGUMENTS:\r
+\r
+// non-void arg:\r
+template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const);\r
+\r
+// non-void arg:\r
+template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_Arg2 _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const);\r
+\r
+template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const);\r
+\r
+template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_TwoArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1, _Arg2) const);\r
+\r
+// ********************\r
+// ONE ARGUMENT:\r
+\r
+// non-void arg:\r
+template<typename _Class, typename _ReturnType, typename _Arg1>\r
+_Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const);\r
+\r
+// non-void arg:\r
+template<typename _Class, typename _ReturnType, typename _Arg1>\r
+void _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const);\r
+\r
+template<typename _Class, typename _ReturnType, typename _Arg1>\r
+_ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const);\r
+\r
+template<typename _Class, typename _ReturnType, typename _Arg1>\r
+_OneArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1) const);\r
+\r
+// ********************\r
+// ZERO ARGUMENT:\r
+\r
+// void arg:\r
+template<typename _Class, typename _ReturnType>\r
+void _Arg1ClassHelperThunk(_ReturnType (_Class::*)() const);\r
+\r
+// void arg:\r
+template<typename _Class, typename _ReturnType>\r
+void _Arg2ClassHelperThunk(_ReturnType (_Class::*)() const);\r
+\r
+// void arg:\r
+template<typename _Class, typename _ReturnType>\r
+_ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)() const);\r
+\r
+template<typename _Class, typename _ReturnType>\r
+_ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)() const);\r
+\r
+// ****************************************\r
+// POINTER TYPES:\r
+\r
+// ********************\r
+// TWO ARGUMENTS:\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall*)(_Arg1, _Arg2));\r
+\r
+template<typename _ReturnType, typename _Arg1, typename _Arg2>\r
+_TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall*)(_Arg1, _Arg2));\r
+\r
+// ********************\r
+// ONE ARGUMENT:\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+_Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+void _Arg2PFNHelperThunk(_ReturnType(__cdecl*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+_ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+_OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+_Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+void _Arg2PFNHelperThunk(_ReturnType(__stdcall*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+_ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+_OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+_Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+void _Arg2PFNHelperThunk(_ReturnType(__fastcall*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+_ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall*)(_Arg1));\r
+\r
+template<typename _ReturnType, typename _Arg1>\r
+_OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall*)(_Arg1));\r
+\r
+// ********************\r
+// ZERO ARGUMENT:\r
+\r
+template<typename _ReturnType>\r
+void _Arg1PFNHelperThunk(_ReturnType(__cdecl*)());\r
+\r
+template<typename _ReturnType>\r
+void _Arg2PFNHelperThunk(_ReturnType(__cdecl*)());\r
+\r
+template<typename _ReturnType>\r
+_ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl*)());\r
+\r
+template<typename _ReturnType>\r
+_ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl*)());\r
+\r
+template<typename _ReturnType>\r
+void _Arg1PFNHelperThunk(_ReturnType(__stdcall*)());\r
+\r
+template<typename _ReturnType>\r
+void _Arg2PFNHelperThunk(_ReturnType(__stdcall*)());\r
+\r
+template<typename _ReturnType>\r
+_ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall*)());\r
+\r
+template<typename _ReturnType>\r
+_ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall*)());\r
+\r
+template<typename _ReturnType>\r
+void _Arg1PFNHelperThunk(_ReturnType(__fastcall*)());\r
+\r
+template<typename _ReturnType>\r
+void _Arg2PFNHelperThunk(_ReturnType(__fastcall*)());\r
+\r
+template<typename _ReturnType>\r
+_ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall*)());\r
+\r
+template<typename _ReturnType>\r
+_ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall*)());\r
+\r
+template<typename _T>\r
+struct _FunctorArguments\r
+{\r
+    static const size_t _Count = 0;\r
+};\r
+\r
+template<>\r
+struct _FunctorArguments<_OneArgumentFunctor>\r
+{\r
+    static const size_t _Count = 1;\r
+};\r
+\r
+template<>\r
+struct _FunctorArguments<_TwoArgumentFunctor>\r
+{\r
+    static const size_t _Count = 2;\r
+};\r
+\r
+template<typename _T>\r
+struct _FunctorTypeTraits\r
+{\r
+    typedef decltype(_ArgumentCountHelper(&(_T::operator()))) _ArgumentCountType;\r
+    static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count;\r
+\r
+    typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType;\r
+    typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type;\r
+    typedef decltype(_Arg2ClassHelperThunk(&(_T::operator()))) _Argument2Type;\r
+};\r
+\r
+template<typename _T>\r
+struct _FunctorTypeTraits<_T*>\r
+{\r
+    typedef decltype(_ArgumentCountHelper(stdx::declval<_T*>())) _ArgumentCountType;\r
+    static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count;\r
+\r
+    typedef decltype(_ReturnTypePFNHelperThunk(stdx::declval<_T*>())) _ReturnType;\r
+    typedef decltype(_Arg1PFNHelperThunk(stdx::declval<_T*>())) _Argument1Type;\r
+    typedef decltype(_Arg2PFNHelperThunk(stdx::declval<_T*>())) _Argument2Type;\r
+};\r
+\r
+template<typename _T>\r
+struct _ProgressTypeTraits\r
+{\r
+    static const bool _TakesProgress = false;\r
+    typedef void _ProgressType;\r
+};\r
+\r
+template<typename _T>\r
+struct _ProgressTypeTraits<progress_reporter<_T>>\r
+{\r
+    static const bool _TakesProgress = true;\r
+    typedef typename _T _ProgressType;\r
+};\r
+\r
+template<typename _T, size_t count = _FunctorTypeTraits<_T>::_ArgumentCount>\r
+struct _CAFunctorOptions\r
+{\r
+    static const bool _TakesProgress = false;\r
+    static const bool _TakesToken = false;\r
+    typedef void _ProgressType;\r
+};\r
+\r
+template<typename _T>\r
+struct _CAFunctorOptions<_T, 1>\r
+{\r
+private:\r
+    typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type;\r
+\r
+public:\r
+    static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress;\r
+    static const bool _TakesToken = !_TakesProgress;\r
+    typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType;\r
+};\r
+\r
+template<typename _T>\r
+struct _CAFunctorOptions<_T, 2>\r
+{\r
+private:\r
+    typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type;\r
+\r
+public:\r
+    static const bool _TakesProgress = true;\r
+    static const bool _TakesToken = true;\r
+    typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType;\r
+};\r
+\r
+ref class _Zip\r
+{\r
+};\r
+\r
+// ***************************************************************************\r
+// Async Operation Task Generators\r
+//\r
+\r
+//\r
+// Functor returns an IAsyncInfo - result needs to be wrapped in a task:\r
+//\r
+template<typename _AsyncSelector, typename _ReturnType>\r
+struct _SelectorTaskGenerator\r
+{\r
+    template<typename _Function>\r
+    static task<_ReturnType> _GenerateTask_0(const _Function& _Func,\r
+                                             cancellation_token_source _Cts,\r
+                                             const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<_ReturnType>(_Func(), _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function>\r
+    static task<_ReturnType> _GenerateTask_1C(const _Function& _Func,\r
+                                              cancellation_token_source _Cts,\r
+                                              const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<_ReturnType>(_Func(_Cts.get_token()), _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<_ReturnType> _GenerateTask_1P(const _Function& _Func,\r
+                                              const _ProgressObject& _Progress,\r
+                                              cancellation_token_source _Cts,\r
+                                              const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<_ReturnType>(_Func(_Progress), _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func,\r
+                                               const _ProgressObject& _Progress,\r
+                                               cancellation_token_source _Cts,\r
+                                               const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<_ReturnType>(_Func(_Progress, _Cts.get_token()), _taskOptinos);\r
+    }\r
+};\r
+\r
+template<typename _AsyncSelector>\r
+struct _SelectorTaskGenerator<_AsyncSelector, void>\r
+{\r
+    template<typename _Function>\r
+    static task<void> _GenerateTask_0(const _Function& _Func,\r
+                                      cancellation_token_source _Cts,\r
+                                      const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<void>(_Func(), _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function>\r
+    static task<void> _GenerateTask_1C(const _Function& _Func,\r
+                                       cancellation_token_source _Cts,\r
+                                       const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<void>(_Func(_Cts.get_token()), _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<void> _GenerateTask_1P(const _Function& _Func,\r
+                                       const _ProgressObject& _Progress,\r
+                                       cancellation_token_source _Cts,\r
+                                       const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<void>(_Func(_Progress), _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<void> _GenerateTask_2PC(const _Function& _Func,\r
+                                        const _ProgressObject& _Progress,\r
+                                        cancellation_token_source _Cts,\r
+                                        const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<void>(_Func(_Progress, _Cts.get_token()), _taskOptinos);\r
+    }\r
+};\r
+\r
+//\r
+// Functor returns a result - it needs to be wrapped in a task:\r
+//\r
+template<typename _ReturnType>\r
+struct _SelectorTaskGenerator<_TypeSelectorNoAsync, _ReturnType>\r
+{\r
+\r
+#pragma warning(push)\r
+#pragma warning(disable : 4702)\r
+    template<typename _Function>\r
+    static task<_ReturnType> _GenerateTask_0(const _Function& _Func,\r
+                                             cancellation_token_source _Cts,\r
+                                             const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<_ReturnType>(\r
+            [=]() -> _ReturnType {\r
+                _Task_generator_oversubscriber_t _Oversubscriber;\r
+                (_Oversubscriber);\r
+                return _Func();\r
+            },\r
+            _taskOptinos);\r
+    }\r
+#pragma warning(pop)\r
+\r
+    template<typename _Function>\r
+    static task<_ReturnType> _GenerateTask_1C(const _Function& _Func,\r
+                                              cancellation_token_source _Cts,\r
+                                              const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<_ReturnType>(\r
+            [=]() -> _ReturnType {\r
+                _Task_generator_oversubscriber_t _Oversubscriber;\r
+                (_Oversubscriber);\r
+                return _Func(_Cts.get_token());\r
+            },\r
+            _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<_ReturnType> _GenerateTask_1P(const _Function& _Func,\r
+                                              const _ProgressObject& _Progress,\r
+                                              cancellation_token_source _Cts,\r
+                                              const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<_ReturnType>(\r
+            [=]() -> _ReturnType {\r
+                _Task_generator_oversubscriber_t _Oversubscriber;\r
+                (_Oversubscriber);\r
+                return _Func(_Progress);\r
+            },\r
+            _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func,\r
+                                               const _ProgressObject& _Progress,\r
+                                               cancellation_token_source _Cts,\r
+                                               const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<_ReturnType>(\r
+            [=]() -> _ReturnType {\r
+                _Task_generator_oversubscriber_t _Oversubscriber;\r
+                (_Oversubscriber);\r
+                return _Func(_Progress, _Cts.get_token());\r
+            },\r
+            _taskOptinos);\r
+    }\r
+};\r
+\r
+template<>\r
+struct _SelectorTaskGenerator<_TypeSelectorNoAsync, void>\r
+{\r
+    template<typename _Function>\r
+    static task<void> _GenerateTask_0(const _Function& _Func,\r
+                                      cancellation_token_source _Cts,\r
+                                      const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<void>(\r
+            [=]() {\r
+                _Task_generator_oversubscriber_t _Oversubscriber;\r
+                (_Oversubscriber);\r
+                _Func();\r
+            },\r
+            _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function>\r
+    static task<void> _GenerateTask_1C(const _Function& _Func,\r
+                                       cancellation_token_source _Cts,\r
+                                       const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<void>(\r
+            [=]() {\r
+                _Task_generator_oversubscriber_t _Oversubscriber;\r
+                (_Oversubscriber);\r
+                _Func(_Cts.get_token());\r
+            },\r
+            _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<void> _GenerateTask_1P(const _Function& _Func,\r
+                                       const _ProgressObject& _Progress,\r
+                                       cancellation_token_source _Cts,\r
+                                       const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<void>(\r
+            [=]() {\r
+                _Task_generator_oversubscriber_t _Oversubscriber;\r
+                (_Oversubscriber);\r
+                _Func(_Progress);\r
+            },\r
+            _taskOptinos);\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<void> _GenerateTask_2PC(const _Function& _Func,\r
+                                        const _ProgressObject& _Progress,\r
+                                        cancellation_token_source _Cts,\r
+                                        const _TaskCreationCallstack& _callstack)\r
+    {\r
+        task_options _taskOptinos(_Cts.get_token());\r
+        details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);\r
+        return task<void>(\r
+            [=]() {\r
+                _Task_generator_oversubscriber_t _Oversubscriber;\r
+                (_Oversubscriber);\r
+                _Func(_Progress, _Cts.get_token());\r
+            },\r
+            _taskOptinos);\r
+    }\r
+};\r
+\r
+//\r
+// Functor returns a task - the task can directly be returned:\r
+//\r
+template<typename _ReturnType>\r
+struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, _ReturnType>\r
+{\r
+    template<typename _Function>\r
+    static task<_ReturnType> _GenerateTask_0(const _Function& _Func,\r
+                                             cancellation_token_source _Cts,\r
+                                             const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _Func();\r
+    }\r
+\r
+    template<typename _Function>\r
+    static task<_ReturnType> _GenerateTask_1C(const _Function& _Func,\r
+                                              cancellation_token_source _Cts,\r
+                                              const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _Func(_Cts.get_token());\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<_ReturnType> _GenerateTask_1P(const _Function& _Func,\r
+                                              const _ProgressObject& _Progress,\r
+                                              cancellation_token_source _Cts,\r
+                                              const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _Func(_Progress);\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func,\r
+                                               const _ProgressObject& _Progress,\r
+                                               cancellation_token_source _Cts,\r
+                                               const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _Func(_Progress, _Cts.get_token());\r
+    }\r
+};\r
+\r
+template<>\r
+struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, void>\r
+{\r
+    template<typename _Function>\r
+    static task<void> _GenerateTask_0(const _Function& _Func,\r
+                                      cancellation_token_source _Cts,\r
+                                      const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _Func();\r
+    }\r
+\r
+    template<typename _Function>\r
+    static task<void> _GenerateTask_1C(const _Function& _Func,\r
+                                       cancellation_token_source _Cts,\r
+                                       const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _Func(_Cts.get_token());\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<void> _GenerateTask_1P(const _Function& _Func,\r
+                                       const _ProgressObject& _Progress,\r
+                                       cancellation_token_source _Cts,\r
+                                       const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _Func(_Progress);\r
+    }\r
+\r
+    template<typename _Function, typename _ProgressObject>\r
+    static task<void> _GenerateTask_2PC(const _Function& _Func,\r
+                                        const _ProgressObject& _Progress,\r
+                                        cancellation_token_source _Cts,\r
+                                        const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _Func(_Progress, _Cts.get_token());\r
+    }\r
+};\r
+\r
+template<typename _Generator, bool _TakesToken, bool TakesProgress>\r
+struct _TaskGenerator\r
+{\r
+};\r
+\r
+template<typename _Generator>\r
+struct _TaskGenerator<_Generator, false, false>\r
+{\r
+    template<typename _Function, typename _ClassPtr, typename _ProgressType>\r
+    static auto _GenerateTask(const _Function& _Func,\r
+                              _ClassPtr _Ptr,\r
+                              cancellation_token_source _Cts,\r
+                              const _TaskCreationCallstack& _callstack)\r
+        -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack))\r
+    {\r
+        return _Generator::_GenerateTask_0(_Func, _Cts, _callstack);\r
+    }\r
+};\r
+\r
+template<typename _Generator>\r
+struct _TaskGenerator<_Generator, true, false>\r
+{\r
+    template<typename _Function, typename _ClassPtr, typename _ProgressType>\r
+    static auto _GenerateTask(const _Function& _Func,\r
+                              _ClassPtr _Ptr,\r
+                              cancellation_token_source _Cts,\r
+                              const _TaskCreationCallstack& _callstack)\r
+        -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack))\r
+    {\r
+        return _Generator::_GenerateTask_1C(_Func, _Cts, _callstack);\r
+    }\r
+};\r
+\r
+template<typename _Generator>\r
+struct _TaskGenerator<_Generator, false, true>\r
+{\r
+    template<typename _Function, typename _ClassPtr, typename _ProgressType>\r
+    static auto _GenerateTask(const _Function& _Func,\r
+                              _ClassPtr _Ptr,\r
+                              cancellation_token_source _Cts,\r
+                              const _TaskCreationCallstack& _callstack)\r
+        -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack))\r
+    {\r
+        return _Generator::_GenerateTask_1P(\r
+            _Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack);\r
+    }\r
+};\r
+\r
+template<typename _Generator>\r
+struct _TaskGenerator<_Generator, true, true>\r
+{\r
+    template<typename _Function, typename _ClassPtr, typename _ProgressType>\r
+    static auto _GenerateTask(const _Function& _Func,\r
+                              _ClassPtr _Ptr,\r
+                              cancellation_token_source _Cts,\r
+                              const _TaskCreationCallstack& _callstack)\r
+        -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack))\r
+    {\r
+        return _Generator::_GenerateTask_2PC(\r
+            _Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack);\r
+    }\r
+};\r
+\r
+// ***************************************************************************\r
+// Async Operation Attributes Classes\r
+//\r
+// These classes are passed through the hierarchy of async base classes in order to hold multiple attributes of a given\r
+// async construct in a single container. An attribute class must define:\r
+//\r
+// Mandatory:\r
+// -------------------------\r
+//\r
+// _AsyncBaseType           : The Windows Runtime interface which is being implemented.\r
+// _CompletionDelegateType  : The Windows Runtime completion delegate type for the interface.\r
+// _ProgressDelegateType    : If _TakesProgress is true, the Windows Runtime progress delegate type for the interface.\r
+// If it is false, an empty Windows Runtime type. _ReturnType              : The return type of the async construct\r
+// (void for actions / non-void for operations)\r
+//\r
+// _TakesProgress           : An indication as to whether or not\r
+//\r
+// _Generate_Task           : A function adapting the user's function into what's necessary to produce the appropriate\r
+// task\r
+//\r
+// Optional:\r
+// -------------------------\r
+//\r
+\r
+template<typename _Function,\r
+         typename _ProgressType,\r
+         typename _ReturnType,\r
+         typename _TaskTraits,\r
+         bool _TakesToken,\r
+         bool _TakesProgress>\r
+struct _AsyncAttributes\r
+{\r
+};\r
+\r
+template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesToken>\r
+struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, true>\r
+{\r
+    typedef typename Windows::Foundation::IAsyncOperationWithProgress<_ReturnType, _ProgressType> _AsyncBaseType;\r
+    typedef typename Windows::Foundation::AsyncOperationProgressHandler<_ReturnType, _ProgressType>\r
+        _ProgressDelegateType;\r
+    typedef typename Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_ReturnType, _ProgressType>\r
+        _CompletionDelegateType;\r
+    typedef typename _ReturnType _ReturnType;\r
+    typedef typename _ProgressType _ProgressType;\r
+    typedef typename _TaskTraits::_AsyncKind _AsyncKind;\r
+    typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator;\r
+    typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator;\r
+\r
+    static const bool _TakesProgress = true;\r
+    static const bool _TakesToken = _TakesToken;\r
+\r
+    template<typename _Function, typename _ClassPtr>\r
+    static task<_ReturnType> _Generate_Task(const _Function& _Func,\r
+                                            _ClassPtr _Ptr,\r
+                                            cancellation_token_source _Cts,\r
+                                            const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack);\r
+    }\r
+};\r
+\r
+template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesToken>\r
+struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, false>\r
+{\r
+    typedef typename Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType;\r
+    typedef _Zip _ProgressDelegateType;\r
+    typedef typename Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType;\r
+    typedef typename _ReturnType _ReturnType;\r
+    typedef typename _TaskTraits::_AsyncKind _AsyncKind;\r
+    typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator;\r
+    typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator;\r
+\r
+    static const bool _TakesProgress = false;\r
+    static const bool _TakesToken = _TakesToken;\r
+\r
+    template<typename _Function, typename _ClassPtr>\r
+    static task<_ReturnType> _Generate_Task(const _Function& _Func,\r
+                                            _ClassPtr _Ptr,\r
+                                            cancellation_token_source _Cts,\r
+                                            const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack);\r
+    }\r
+};\r
+\r
+template<typename _Function, typename _ProgressType, typename _TaskTraits, bool _TakesToken>\r
+struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, true>\r
+{\r
+    typedef typename Windows::Foundation::IAsyncActionWithProgress<_ProgressType> _AsyncBaseType;\r
+    typedef typename Windows::Foundation::AsyncActionProgressHandler<_ProgressType> _ProgressDelegateType;\r
+    typedef typename Windows::Foundation::AsyncActionWithProgressCompletedHandler<_ProgressType>\r
+        _CompletionDelegateType;\r
+    typedef void _ReturnType;\r
+    typedef typename _ProgressType _ProgressType;\r
+    typedef typename _TaskTraits::_AsyncKind _AsyncKind;\r
+    typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator;\r
+    typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator;\r
+\r
+    static const bool _TakesProgress = true;\r
+    static const bool _TakesToken = _TakesToken;\r
+\r
+    template<typename _Function, typename _ClassPtr>\r
+    static task<_ReturnType> _Generate_Task(const _Function& _Func,\r
+                                            _ClassPtr _Ptr,\r
+                                            cancellation_token_source _Cts,\r
+                                            const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack);\r
+    }\r
+};\r
+\r
+template<typename _Function, typename _ProgressType, typename _TaskTraits, bool _TakesToken>\r
+struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, false>\r
+{\r
+    typedef typename Windows::Foundation::IAsyncAction _AsyncBaseType;\r
+    typedef _Zip _ProgressDelegateType;\r
+    typedef typename Windows::Foundation::AsyncActionCompletedHandler _CompletionDelegateType;\r
+    typedef void _ReturnType;\r
+    typedef typename _TaskTraits::_AsyncKind _AsyncKind;\r
+    typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator;\r
+    typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator;\r
+\r
+    static const bool _TakesProgress = false;\r
+    static const bool _TakesToken = _TakesToken;\r
+\r
+    template<typename _Function, typename _ClassPtr>\r
+    static task<_ReturnType> _Generate_Task(const _Function& _Func,\r
+                                            _ClassPtr _Ptr,\r
+                                            cancellation_token_source _Cts,\r
+                                            const _TaskCreationCallstack& _callstack)\r
+    {\r
+        return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack);\r
+    }\r
+};\r
+\r
+template<typename _Function>\r
+struct _AsyncLambdaTypeTraits\r
+{\r
+    typedef typename _FunctorTypeTraits<_Function>::_ReturnType _ReturnType;\r
+    typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type;\r
+    typedef typename _CAFunctorOptions<_Function>::_ProgressType _ProgressType;\r
+\r
+    static const bool _TakesProgress = _CAFunctorOptions<_Function>::_TakesProgress;\r
+    static const bool _TakesToken = _CAFunctorOptions<_Function>::_TakesToken;\r
+\r
+    typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits;\r
+    typedef typename _AsyncAttributes<_Function,\r
+                                      _ProgressType,\r
+                                      typename _TaskTraits::_TaskRetType,\r
+                                      _TaskTraits,\r
+                                      _TakesToken,\r
+                                      _TakesProgress>\r
+        _AsyncAttributes;\r
+};\r
+\r
+// ***************************************************************************\r
+// AsyncInfo (and completion) Layer:\r
+//\r
+\r
+//\r
+// Internal base class implementation for async operations (based on internal Windows representation for ABI level async\r
+// operations)\r
+//\r
+template<typename _Attributes, _AsyncResultType resultType = SingleResult>\r
+ref class _AsyncInfoBase abstract : _Attributes::_AsyncBaseType\r
+{\r
+    internal :\r
+\r
+        _AsyncInfoBase()\r
+        : _M_currentStatus(_AsyncStatusInternal::_AsyncCreated)\r
+        , _M_errorCode(S_OK)\r
+        , _M_completeDelegate(nullptr)\r
+        , _M_CompleteDelegateAssigned(0)\r
+        , _M_CallbackMade(0)\r
+    {\r
+        _M_id = ::pplx::details::platform::GetNextAsyncId();\r
+    }\r
+\r
+public:\r
+    virtual typename _Attributes::_ReturnType GetResults()\r
+    {\r
+        throw ::Platform::Exception::CreateException(E_UNEXPECTED);\r
+    }\r
+\r
+    virtual property unsigned int Id\r
+    {\r
+        unsigned int get()\r
+        {\r
+            _CheckValidStateForAsyncInfoCall();\r
+\r
+            return _M_id;\r
+        }\r
+\r
+        void set(unsigned int id)\r
+        {\r
+            _CheckValidStateForAsyncInfoCall();\r
+\r
+            if (id == 0)\r
+            {\r
+                throw ::Platform::Exception::CreateException(E_INVALIDARG);\r
+            }\r
+            else if (_M_currentStatus != _AsyncStatusInternal::_AsyncCreated)\r
+            {\r
+                throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL);\r
+            }\r
+\r
+            _M_id = id;\r
+        }\r
+    }\r
+\r
+    virtual property Windows::Foundation::AsyncStatus Status\r
+    {\r
+        Windows::Foundation::AsyncStatus get()\r
+        {\r
+            _CheckValidStateForAsyncInfoCall();\r
+\r
+            _AsyncStatusInternal _Current = _M_currentStatus;\r
+\r
+            //\r
+            // Map our internal cancel pending to canceled. This way "pending canceled" looks to the outside as\r
+            // "canceled" but can still transition to "completed" if the operation completes without acknowledging the\r
+            // cancellation request\r
+            //\r
+            switch (_Current)\r
+            {\r
+                case _AsyncCancelPending: _Current = _AsyncCanceled; break;\r
+                case _AsyncCreated: _Current = _AsyncStarted; break;\r
+                default: break;\r
+            }\r
+\r
+            return static_cast<Windows::Foundation::AsyncStatus>(_Current);\r
+        }\r
+    }\r
+\r
+    virtual property Windows::Foundation::HResult ErrorCode\r
+    {\r
+        Windows::Foundation::HResult get()\r
+        {\r
+            _CheckValidStateForAsyncInfoCall();\r
+\r
+            Windows::Foundation::HResult _Hr;\r
+            _Hr.Value = _M_errorCode;\r
+            return _Hr;\r
+        }\r
+    }\r
+\r
+    virtual property typename _Attributes::_ProgressDelegateType ^\r
+        Progress {\r
+            typename typename _Attributes::_ProgressDelegateType ^ get() { return _GetOnProgress(); }\r
+\r
+                void set(typename _Attributes::_ProgressDelegateType ^ _ProgressHandler)\r
+            {\r
+                _PutOnProgress(_ProgressHandler);\r
+            }\r
+        }\r
+\r
+        virtual void\r
+        Cancel()\r
+    {\r
+        if (_TransitionToState(_AsyncCancelPending))\r
+        {\r
+            _OnCancel();\r
+        }\r
+    }\r
+\r
+    virtual void Close()\r
+    {\r
+        if (_TransitionToState(_AsyncClosed))\r
+        {\r
+            _OnClose();\r
+        }\r
+        else\r
+        {\r
+            if (_M_currentStatus != _AsyncClosed) // Closed => Closed transition is just ignored\r
+            {\r
+                throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE);\r
+            }\r
+        }\r
+    }\r
+\r
+    virtual property typename _Attributes::_CompletionDelegateType ^\r
+        Completed {\r
+            typename _Attributes::_CompletionDelegateType ^\r
+                get() {\r
+                    _CheckValidStateForDelegateCall();\r
+                    return _M_completeDelegate;\r
+                }\r
+\r
+                void set(typename _Attributes::_CompletionDelegateType ^ _CompleteHandler)\r
+            {\r
+                _CheckValidStateForDelegateCall();\r
+                // this delegate property is "write once"\r
+                if (InterlockedIncrement(&_M_CompleteDelegateAssigned) == 1)\r
+                {\r
+                    _M_completeDelegateContext = _ContextCallback::_CaptureCurrent();\r
+                    _M_completeDelegate = _CompleteHandler;\r
+                    // Guarantee that the write of _M_completeDelegate is ordered with respect to the read of state\r
+                    // below as perceived from _FireCompletion on another thread.\r
+                    MemoryBarrier();\r
+                    if (_IsTerminalState())\r
+                    {\r
+                        _FireCompletion();\r
+                    }\r
+                }\r
+                else\r
+                {\r
+                    throw ::Platform::Exception::CreateException(E_ILLEGAL_DELEGATE_ASSIGNMENT);\r
+                }\r
+            }\r
+        }\r
+\r
+        protected private :\r
+\r
+        // _Start - this is not externally visible since async operations "hot start" before returning to the caller\r
+        void\r
+        _Start()\r
+    {\r
+        if (_TransitionToState(_AsyncStarted))\r
+        {\r
+            _OnStart();\r
+        }\r
+        else\r
+        {\r
+            throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE);\r
+        }\r
+    }\r
+\r
+    void _FireCompletion()\r
+    {\r
+        _TryTransitionToCompleted();\r
+\r
+        // we guarantee that completion can only ever be fired once\r
+        if (_M_completeDelegate != nullptr && InterlockedIncrement(&_M_CallbackMade) == 1)\r
+        {\r
+            _M_completeDelegateContext._CallInContext([=] {\r
+                _M_completeDelegate((_Attributes::_AsyncBaseType ^) this, this->Status);\r
+                _M_completeDelegate = nullptr;\r
+            });\r
+        }\r
+    }\r
+\r
+    virtual typename _Attributes::_ProgressDelegateType ^\r
+        _GetOnProgress() { throw ::Platform::Exception::CreateException(E_UNEXPECTED); }\r
+\r
+        virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType ^ _ProgressHandler)\r
+    {\r
+        throw ::Platform::Exception::CreateException(E_UNEXPECTED);\r
+    }\r
+\r
+    bool _TryTransitionToCompleted() { return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); }\r
+\r
+    bool _TryTransitionToCancelled() { return _TransitionToState(_AsyncStatusInternal::_AsyncCanceled); }\r
+\r
+    bool _TryTransitionToError(const HRESULT error)\r
+    {\r
+        _InterlockedCompareExchange(reinterpret_cast<volatile LONG*>(&_M_errorCode), error, S_OK);\r
+        return _TransitionToState(_AsyncStatusInternal::_AsyncError);\r
+    }\r
+\r
+    // This method checks to see if the delegate properties can be\r
+    // modified in the current state and generates the appropriate\r
+    // error hr in the case of violation.\r
+    inline void _CheckValidStateForDelegateCall()\r
+    {\r
+        if (_M_currentStatus == _AsyncClosed)\r
+        {\r
+            throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL);\r
+        }\r
+    }\r
+\r
+    // This method checks to see if results can be collected in the\r
+    // current state and generates the appropriate error hr in\r
+    // the case of a violation.\r
+    inline void _CheckValidStateForResultsCall()\r
+    {\r
+        _AsyncStatusInternal _Current = _M_currentStatus;\r
+\r
+        if (_Current == _AsyncError)\r
+        {\r
+            throw ::Platform::Exception::CreateException(_M_errorCode);\r
+        }\r
+#pragma warning(push)\r
+#pragma warning(disable : 4127) // Conditional expression is constant\r
+        // single result illegal before transition to Completed or Cancelled state\r
+        if (resultType == SingleResult)\r
+#pragma warning(pop)\r
+        {\r
+            if (_Current != _AsyncCompleted)\r
+            {\r
+                throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL);\r
+            }\r
+        }\r
+        // multiple results can be called after Start has been called and before/after Completed\r
+        else if (_Current != _AsyncStarted && _Current != _AsyncCancelPending && _Current != _AsyncCanceled &&\r
+                 _Current != _AsyncCompleted)\r
+        {\r
+            throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL);\r
+        }\r
+    }\r
+\r
+    // This method can be called by derived classes periodically to determine\r
+    // whether the asynchronous operation should continue processing or should\r
+    // be halted.\r
+    inline bool _ContinueAsyncOperation() { return (_M_currentStatus == _AsyncStarted); }\r
+\r
+    // These two methods are used to allow the async worker implementation do work on\r
+    // state transitions. No real "work" should be done in these methods. In other words\r
+    // they should not block for a long time on UI timescales.\r
+    virtual void _OnStart() = 0;\r
+    virtual void _OnClose() = 0;\r
+    virtual void _OnCancel() = 0;\r
+\r
+private:\r
+    // This method is used to check if calls to the AsyncInfo properties\r
+    // (id, status, errorcode) are legal in the current state. It also\r
+    // generates the appropriate error hr to return in the case of an\r
+    // illegal call.\r
+    inline void _CheckValidStateForAsyncInfoCall()\r
+    {\r
+        _AsyncStatusInternal _Current = _M_currentStatus;\r
+        if (_Current == _AsyncClosed)\r
+        {\r
+            throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL);\r
+        }\r
+        else if (_Current == _AsyncCreated)\r
+        {\r
+            throw ::Platform::Exception::CreateException(E_ASYNC_OPERATION_NOT_STARTED);\r
+        }\r
+    }\r
+\r
+    inline bool _TransitionToState(const _AsyncStatusInternal _NewState)\r
+    {\r
+        _AsyncStatusInternal _Current = _M_currentStatus;\r
+\r
+        // This enforces the valid state transitions of the asynchronous worker object\r
+        // state machine.\r
+        switch (_NewState)\r
+        {\r
+            case _AsyncStatusInternal::_AsyncStarted:\r
+                if (_Current != _AsyncCreated)\r
+                {\r
+                    return false;\r
+                }\r
+                break;\r
+            case _AsyncStatusInternal::_AsyncCompleted:\r
+                if (_Current != _AsyncStarted && _Current != _AsyncCancelPending)\r
+                {\r
+                    return false;\r
+                }\r
+                break;\r
+            case _AsyncStatusInternal::_AsyncCancelPending:\r
+                if (_Current != _AsyncStarted)\r
+                {\r
+                    return false;\r
+                }\r
+                break;\r
+            case _AsyncStatusInternal::_AsyncCanceled:\r
+                if (_Current != _AsyncStarted && _Current != _AsyncCancelPending)\r
+                {\r
+                    return false;\r
+                }\r
+                break;\r
+            case _AsyncStatusInternal::_AsyncError:\r
+                if (_Current != _AsyncStarted && _Current != _AsyncCancelPending)\r
+                {\r
+                    return false;\r
+                }\r
+                break;\r
+            case _AsyncStatusInternal::_AsyncClosed:\r
+                if (!_IsTerminalState(_Current))\r
+                {\r
+                    return false;\r
+                }\r
+                break;\r
+            default: return false; break;\r
+        }\r
+\r
+        // attempt the transition to the new state\r
+        // Note: if currentStatus_ == _Current, then there was no intervening write\r
+        // by the async work object and the swap succeeded.\r
+        _AsyncStatusInternal _RetState = static_cast<_AsyncStatusInternal>(_InterlockedCompareExchange(\r
+            reinterpret_cast<volatile LONG*>(&_M_currentStatus), _NewState, static_cast<LONG>(_Current)));\r
+\r
+        // ICE returns the former state, if the returned state and the\r
+        // state we captured at the beginning of this method are the same,\r
+        // the swap succeeded.\r
+        return (_RetState == _Current);\r
+    }\r
+\r
+    inline bool _IsTerminalState() { return _IsTerminalState(_M_currentStatus); }\r
+\r
+    inline bool _IsTerminalState(_AsyncStatusInternal status)\r
+    {\r
+        return (status == _AsyncError || status == _AsyncCanceled || status == _AsyncCompleted ||\r
+                status == _AsyncClosed);\r
+    }\r
+\r
+private:\r
+    _ContextCallback _M_completeDelegateContext;\r
+    typename _Attributes::_CompletionDelegateType ^ volatile _M_completeDelegate;\r
+    _AsyncStatusInternal volatile _M_currentStatus;\r
+    HRESULT volatile _M_errorCode;\r
+    unsigned int _M_id;\r
+    long volatile _M_CompleteDelegateAssigned;\r
+    long volatile _M_CallbackMade;\r
+};\r
+\r
+// ***************************************************************************\r
+// Progress Layer (optional):\r
+//\r
+\r
+template<typename _Attributes, bool _HasProgress, _AsyncResultType _ResultType = SingleResult>\r
+ref class _AsyncProgressBase abstract : _AsyncInfoBase<_Attributes, _ResultType>\r
+{\r
+};\r
+\r
+template<typename _Attributes, _AsyncResultType _ResultType>\r
+ref class _AsyncProgressBase<_Attributes, true, _ResultType> abstract : _AsyncInfoBase<_Attributes, _ResultType>\r
+{\r
+    internal :\r
+\r
+        _AsyncProgressBase()\r
+        : _AsyncInfoBase<_Attributes, _ResultType>(), _M_progressDelegate(nullptr)\r
+    {\r
+    }\r
+\r
+    virtual typename _Attributes::_ProgressDelegateType ^ _GetOnProgress() override\r
+    {\r
+        _CheckValidStateForDelegateCall();\r
+        return _M_progressDelegate;\r
+    }\r
+\r
+    virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType ^ _ProgressHandler) override\r
+    {\r
+        _CheckValidStateForDelegateCall();\r
+        _M_progressDelegate = _ProgressHandler;\r
+        _M_progressDelegateContext = _ContextCallback::_CaptureCurrent();\r
+    }\r
+\r
+    void _FireProgress(const typename _Attributes::_ProgressType& _ProgressValue)\r
+    {\r
+        if (_M_progressDelegate != nullptr)\r
+        {\r
+            _M_progressDelegateContext._CallInContext(\r
+                [=] { _M_progressDelegate((_Attributes::_AsyncBaseType ^) this, _ProgressValue); });\r
+        }\r
+    }\r
+\r
+private:\r
+    _ContextCallback _M_progressDelegateContext;\r
+    typename _Attributes::_ProgressDelegateType ^ _M_progressDelegate;\r
+};\r
+\r
+template<typename _Attributes, _AsyncResultType _ResultType = SingleResult>\r
+ref class _AsyncBaseProgressLayer abstract : _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType>\r
+{\r
+};\r
+\r
+// ***************************************************************************\r
+// Task Adaptation Layer:\r
+//\r
+\r
+//\r
+// _AsyncTaskThunkBase provides a bridge between IAsync<Action/Operation> and task.\r
+//\r
+template<typename _Attributes, typename _ReturnType>\r
+ref class _AsyncTaskThunkBase abstract : _AsyncBaseProgressLayer<_Attributes>\r
+{\r
+public:\r
+    virtual _ReturnType GetResults() override\r
+    {\r
+        _CheckValidStateForResultsCall();\r
+        return _M_task.get();\r
+    }\r
+\r
+    internal :\r
+\r
+        typedef task<_ReturnType>\r
+            _TaskType;\r
+\r
+    _AsyncTaskThunkBase(const _TaskType& _Task) : _M_task(_Task) {}\r
+\r
+    _AsyncTaskThunkBase() {}\r
+\r
+protected:\r
+    virtual void _OnStart() override\r
+    {\r
+        _M_task.then([=](_TaskType _Antecedent) {\r
+            try\r
+            {\r
+                _Antecedent.get();\r
+            }\r
+            catch (task_canceled&)\r
+            {\r
+                _TryTransitionToCancelled();\r
+            }\r
+            catch (::Platform::Exception ^ _Ex)\r
+            {\r
+                _TryTransitionToError(_Ex->HResult);\r
+            }\r
+            catch (...)\r
+            {\r
+                _TryTransitionToError(E_FAIL);\r
+            }\r
+            _FireCompletion();\r
+        });\r
+    }\r
+\r
+    internal :\r
+\r
+        _TaskType _M_task;\r
+    cancellation_token_source _M_cts;\r
+};\r
+\r
+template<typename _Attributes>\r
+ref class _AsyncTaskThunk : _AsyncTaskThunkBase<_Attributes, typename _Attributes::_ReturnType>\r
+{\r
+    internal :\r
+\r
+        _AsyncTaskThunk(const _TaskType& _Task)\r
+        : _AsyncTaskThunkBase(_Task)\r
+    {\r
+    }\r
+\r
+    _AsyncTaskThunk() {}\r
+\r
+protected:\r
+    virtual void _OnClose() override {}\r
+\r
+    virtual void _OnCancel() override { _M_cts.cancel(); }\r
+};\r
+\r
+// ***************************************************************************\r
+// Async Creation Layer:\r
+//\r
+template<typename _Function>\r
+ref class _AsyncTaskGeneratorThunk sealed\r
+    : _AsyncTaskThunk<typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes>\r
+{\r
+    internal :\r
+\r
+        typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes;\r
+    typedef typename _AsyncTaskThunk<_Attributes> _Base;\r
+    typedef typename _Attributes::_AsyncBaseType _AsyncBaseType;\r
+\r
+    _AsyncTaskGeneratorThunk(const _Function& _Func, const _TaskCreationCallstack& _callstack)\r
+        : _M_func(_Func), _M_creationCallstack(_callstack)\r
+    {\r
+        // Virtual call here is safe as the class is declared 'sealed'\r
+        _Start();\r
+    }\r
+\r
+protected:\r
+    //\r
+    // The only thing we must do different from the base class is we must spin the hot task on transition from\r
+    // Created->Started. Otherwise, let the base thunk handle everything.\r
+    //\r
+\r
+    virtual void _OnStart() override\r
+    {\r
+        //\r
+        // Call the appropriate task generator to actually produce a task of the expected type. This might adapt the\r
+        // user lambda for progress reports, wrap the return result in a task, or allow for direct return of a task\r
+        // depending on the form of the lambda.\r
+        //\r
+        _M_task = _Attributes::_Generate_Task(_M_func, this, _M_cts, _M_creationCallstack);\r
+        _Base::_OnStart();\r
+    }\r
+\r
+    virtual void _OnCancel() override { _Base::_OnCancel(); }\r
+\r
+private:\r
+    _TaskCreationCallstack _M_creationCallstack;\r
+    _Function _M_func;\r
+};\r
+} // namespace details\r
+\r
+/// <summary>\r
+///     Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return\r
+///     type of <c>create_async</c> is one of either <c>IAsyncAction^</c>,\r
+///     <c>IAsyncActionWithProgress&lt;TProgress&gt;^</c>, <c>IAsyncOperation&lt;TResult&gt;^</c>, or\r
+///     <c>IAsyncOperationWithProgress&lt;TResult, TProgress&gt;^</c> based on the signature of the lambda passed to the\r
+///     method.\r
+/// </summary>\r
+/// <param name="_Func">\r
+///     The lambda or function object from which to create a Windows Runtime asynchronous construct.\r
+/// </param>\r
+/// <returns>\r
+///     An asynchronous construct represented by an IAsyncAction^, IAsyncActionWithProgress&lt;TProgress&gt;^,\r
+///     IAsyncOperation&lt;TResult&gt;^, or an IAsyncOperationWithProgress&lt;TResult, TProgress&gt;^. The interface\r
+///     returned depends on the signature of the lambda passed into the function.\r
+/// </returns>\r
+/// <remarks>\r
+///     The return type of the lambda determines whether the construct is an action or an operation.\r
+///     <para>Lambdas that return void cause the creation of actions. Lambdas that return a result of type\r
+///     <c>TResult</c> cause the creation of operations of TResult.</para> <para>The lambda may also return a\r
+///     <c>task&lt;TResult&gt;</c> which encapsulates the asynchronous work within itself or is the continuation of a\r
+///     chain of tasks that represent the asynchronous work. In this case, the lambda itself is executed inline, since\r
+///     the tasks are the ones that execute asynchronously, and the return type of the lambda is unwrapped to produce\r
+///     the asynchronous construct returned by <c>create_async</c>. This implies that a lambda that returns a\r
+///     task&lt;void&gt; will cause the creation of actions, and a lambda that returns a task&lt;TResult&gt; will cause\r
+///     the creation of operations of TResult.</para> <para>The lambda may take either zero, one or two arguments. The\r
+///     valid arguments are <c>progress_reporter&lt;TProgress&gt;</c> and <c>cancellation_token</c>, in that order if\r
+///     both are used. A lambda without arguments causes the creation of an asynchronous construct without the\r
+///     capability for progress reporting. A lambda that takes a progress_reporter&lt;TProgress&gt; will cause\r
+///     <c>create_async</c> to return an asynchronous construct which reports progress of type TProgress each time the\r
+///     <c>report</c> method of the progress_reporter object is called. A lambda that takes a cancellation_token may use\r
+///     that token to check for cancellation, or pass it to tasks that it creates so that cancellation of the\r
+///     asynchronous construct causes cancellation of those tasks.</para>\r
+///     <para>If the body of the lambda or function object returns a result (and not a task&lt;TResult&gt;), the lambda\r
+///     will be executed asynchronously within the process MTA in the context of a task the Runtime implicitly creates\r
+///     for it. The <c>IAsyncInfo::Cancel</c> method will cause cancellation of the implicit task.</para> <para>If the\r
+///     body of the lambda returns a task, the lambda executes inline, and by declaring the lambda to take an argument\r
+///     of type <c>cancellation_token</c> you can trigger cancellation of any tasks you create within the lambda by\r
+///     passing that token in when you create them. You may also use the <c>register_callback</c> method on the token to\r
+///     cause the Runtime to invoke a callback when you call <c>IAsyncInfo::Cancel</c> on the async operation or action\r
+///     produced..</para> <para>This function is only available to Windows Store apps.</para>\r
+/// </remarks>\r
+/// <seealso cref="task Class"/>\r
+/// <seealso cref="progress_reporter Class"/>\r
+/// <seealso cref="cancelation_token Class"/>\r
+/**/\r
+template<typename _Function>\r
+    __declspec(noinline) details::_AsyncTaskGeneratorThunk<_Function> ^\r
+    create_async(const _Function& _Func) {\r
+        static_assert(std::is_same<decltype(details::_IsValidCreateAsync(_Func, 0, 0, 0, 0)), std::true_type>::value,\r
+                      "argument to create_async must be a callable object taking zero, one or two arguments");\r
+        return ref new details::_AsyncTaskGeneratorThunk<_Function>(_Func, PPLX_CAPTURE_CALLSTACK());\r
+    }\r
+\r
+#endif /* defined (__cplusplus_winrt) */\r
+\r
+    namespace details\r
+{\r
+    // Helper struct for when_all operators to know when tasks have completed\r
+    template<typename _Type>\r
+    struct _RunAllParam\r
+    {\r
+        _RunAllParam() : _M_completeCount(0), _M_numTasks(0) {}\r
+\r
+        void _Resize(size_t _Len, bool _SkipVector = false)\r
+        {\r
+            _M_numTasks = _Len;\r
+            if (!_SkipVector)\r
+            {\r
+                _M_vector._Result.resize(_Len);\r
+            }\r
+        }\r
+\r
+        task_completion_event<_Unit_type> _M_completed;\r
+        _ResultHolder<std::vector<_Type>> _M_vector;\r
+        _ResultHolder<_Type> _M_mergeVal;\r
+        atomic_size_t _M_completeCount;\r
+        size_t _M_numTasks;\r
+    };\r
+\r
+    template<typename _Type>\r
+    struct _RunAllParam<std::vector<_Type>>\r
+    {\r
+        _RunAllParam() : _M_completeCount(0), _M_numTasks(0) {}\r
+\r
+        void _Resize(size_t _Len, bool _SkipVector = false)\r
+        {\r
+            _M_numTasks = _Len;\r
+\r
+            if (!_SkipVector)\r
+            {\r
+                _M_vector.resize(_Len);\r
+            }\r
+        }\r
+\r
+        task_completion_event<_Unit_type> _M_completed;\r
+        std::vector<_ResultHolder<std::vector<_Type>>> _M_vector;\r
+        atomic_size_t _M_completeCount;\r
+        size_t _M_numTasks;\r
+    };\r
+\r
+    // Helper struct specialization for void\r
+    template<>\r
+    struct _RunAllParam<_Unit_type>\r
+    {\r
+        _RunAllParam() : _M_completeCount(0), _M_numTasks(0) {}\r
+\r
+        void _Resize(size_t _Len) { _M_numTasks = _Len; }\r
+\r
+        task_completion_event<_Unit_type> _M_completed;\r
+        atomic_size_t _M_completeCount;\r
+        size_t _M_numTasks;\r
+    };\r
+\r
+    inline void _JoinAllTokens_Add(const cancellation_token_source& _MergedSrc,\r
+                                   _CancellationTokenState* _PJoinedTokenState)\r
+    {\r
+        if (_PJoinedTokenState != nullptr && _PJoinedTokenState != _CancellationTokenState::_None())\r
+        {\r
+            cancellation_token _T = cancellation_token::_FromImpl(_PJoinedTokenState);\r
+            _T.register_callback([=]() { _MergedSrc.cancel(); });\r
+        }\r
+    }\r
+\r
+    template<typename _ElementType, typename _Function, typename _TaskType>\r
+    void _WhenAllContinuationWrapper(_RunAllParam<_ElementType> * _PParam, _Function _Func, task<_TaskType> & _Task)\r
+    {\r
+        if (_Task._GetImpl()->_IsCompleted())\r
+        {\r
+            _Func();\r
+            if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks)\r
+            {\r
+                // Inline execute its direct continuation, the _ReturnTask\r
+                _PParam->_M_completed.set(_Unit_type());\r
+                // It's safe to delete it since all usage of _PParam in _ReturnTask has been finished.\r
+                delete _PParam;\r
+            }\r
+        }\r
+        else\r
+        {\r
+            _ASSERTE(_Task._GetImpl()->_IsCanceled());\r
+            if (_Task._GetImpl()->_HasUserException())\r
+            {\r
+                // _Cancel will return false if the TCE is already canceled with or without exception\r
+                _PParam->_M_completed._Cancel(_Task._GetImpl()->_GetExceptionHolder());\r
+            }\r
+            else\r
+            {\r
+                _PParam->_M_completed._Cancel();\r
+            }\r
+\r
+            if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks)\r
+            {\r
+                delete _PParam;\r
+            }\r
+        }\r
+    }\r
+\r
+    template<typename _ElementType, typename _Iterator>\r
+    struct _WhenAllImpl\r
+    {\r
+        static task<std::vector<_ElementType>> _Perform(const task_options& _TaskOptions,\r
+                                                        _Iterator _Begin,\r
+                                                        _Iterator _End)\r
+        {\r
+            _CancellationTokenState* _PTokenState =\r
+                _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;\r
+\r
+            auto _PParam = new _RunAllParam<_ElementType>();\r
+            cancellation_token_source _MergedSource;\r
+\r
+            // Step1: Create task completion event.\r
+            task_options _Options(_TaskOptions);\r
+            _Options.set_cancellation_token(_MergedSource.get_token());\r
+            task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options);\r
+            // The return task must be created before step 3 to enforce inline execution.\r
+            auto _ReturnTask = _All_tasks_completed._Then(\r
+                [=](_Unit_type) -> std::vector<_ElementType> { return _PParam->_M_vector.Get(); }, nullptr);\r
+\r
+            // Step2: Combine and check tokens, and count elements in range.\r
+            if (_PTokenState)\r
+            {\r
+                _JoinAllTokens_Add(_MergedSource, _PTokenState);\r
+                _PParam->_Resize(static_cast<size_t>(std::distance(_Begin, _End)));\r
+            }\r
+            else\r
+            {\r
+                size_t _TaskNum = 0;\r
+                for (auto _PTask = _Begin; _PTask != _End; ++_PTask)\r
+                {\r
+                    _TaskNum++;\r
+                    _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState);\r
+                }\r
+                _PParam->_Resize(_TaskNum);\r
+            }\r
+\r
+            // Step3: Check states of previous tasks.\r
+            if (_Begin == _End)\r
+            {\r
+                _PParam->_M_completed.set(_Unit_type());\r
+                delete _PParam;\r
+            }\r
+            else\r
+            {\r
+                size_t _Index = 0;\r
+                for (auto _PTask = _Begin; _PTask != _End; ++_PTask)\r
+                {\r
+                    if (_PTask->is_apartment_aware())\r
+                    {\r
+                        _ReturnTask._SetAsync();\r
+                    }\r
+\r
+                    _PTask->_Then(\r
+                        [_PParam, _Index](task<_ElementType> _ResultTask) {\r
+                            auto _PParamCopy = _PParam;\r
+                            auto _IndexCopy = _Index;\r
+                            auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() {\r
+                                _PParamCopy->_M_vector._Result[_IndexCopy] = _ResultTask._GetImpl()->_GetResult();\r
+                            };\r
+\r
+                            _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask);\r
+                        },\r
+                        _CancellationTokenState::_None());\r
+\r
+                    _Index++;\r
+                }\r
+            }\r
+\r
+            return _ReturnTask;\r
+        }\r
+    };\r
+\r
+    template<typename _ElementType, typename _Iterator>\r
+    struct _WhenAllImpl<std::vector<_ElementType>, _Iterator>\r
+    {\r
+        static task<std::vector<_ElementType>> _Perform(const task_options& _TaskOptions,\r
+                                                        _Iterator _Begin,\r
+                                                        _Iterator _End)\r
+        {\r
+            _CancellationTokenState* _PTokenState =\r
+                _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;\r
+\r
+            auto _PParam = new _RunAllParam<std::vector<_ElementType>>();\r
+            cancellation_token_source _MergedSource;\r
+\r
+            // Step1: Create task completion event.\r
+            task_options _Options(_TaskOptions);\r
+            _Options.set_cancellation_token(_MergedSource.get_token());\r
+            task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options);\r
+            // The return task must be created before step 3 to enforce inline execution.\r
+            auto _ReturnTask = _All_tasks_completed._Then(\r
+                [=](_Unit_type) -> std::vector<_ElementType> {\r
+                    _ASSERTE(_PParam->_M_completeCount == _PParam->_M_numTasks);\r
+                    std::vector<_ElementType> _Result;\r
+                    for (size_t _I = 0; _I < _PParam->_M_numTasks; _I++)\r
+                    {\r
+                        const std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I].Get();\r
+                        _Result.insert(_Result.end(), _Vec.begin(), _Vec.end());\r
+                    }\r
+                    return _Result;\r
+                },\r
+                nullptr);\r
+\r
+            // Step2: Combine and check tokens, and count elements in range.\r
+            if (_PTokenState)\r
+            {\r
+                _JoinAllTokens_Add(_MergedSource, _PTokenState);\r
+                _PParam->_Resize(static_cast<size_t>(std::distance(_Begin, _End)));\r
+            }\r
+            else\r
+            {\r
+                size_t _TaskNum = 0;\r
+                for (auto _PTask = _Begin; _PTask != _End; ++_PTask)\r
+                {\r
+                    _TaskNum++;\r
+                    _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState);\r
+                }\r
+                _PParam->_Resize(_TaskNum);\r
+            }\r
+\r
+            // Step3: Check states of previous tasks.\r
+            if (_Begin == _End)\r
+            {\r
+                _PParam->_M_completed.set(_Unit_type());\r
+                delete _PParam;\r
+            }\r
+            else\r
+            {\r
+                size_t _Index = 0;\r
+                for (auto _PTask = _Begin; _PTask != _End; ++_PTask)\r
+                {\r
+                    if (_PTask->is_apartment_aware())\r
+                    {\r
+                        _ReturnTask._SetAsync();\r
+                    }\r
+\r
+                    _PTask->_Then(\r
+                        [_PParam, _Index](task<std::vector<_ElementType>> _ResultTask) {\r
+                            auto _PParamCopy = _PParam;\r
+                            auto _IndexCopy = _Index;\r
+                            auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() {\r
+                                _PParamCopy->_M_vector[_IndexCopy].Set(_ResultTask._GetImpl()->_GetResult());\r
+                            };\r
+\r
+                            _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask);\r
+                        },\r
+                        _CancellationTokenState::_None());\r
+\r
+                    _Index++;\r
+                }\r
+            }\r
+\r
+            return _ReturnTask;\r
+        }\r
+    };\r
+\r
+    template<typename _Iterator>\r
+    struct _WhenAllImpl<void, _Iterator>\r
+    {\r
+        static task<void> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End)\r
+        {\r
+            _CancellationTokenState* _PTokenState =\r
+                _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;\r
+\r
+            auto _PParam = new _RunAllParam<_Unit_type>();\r
+            cancellation_token_source _MergedSource;\r
+\r
+            // Step1: Create task completion event.\r
+            task_options _Options(_TaskOptions);\r
+            _Options.set_cancellation_token(_MergedSource.get_token());\r
+            task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options);\r
+            // The return task must be created before step 3 to enforce inline execution.\r
+            auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) {}, nullptr);\r
+\r
+            // Step2: Combine and check tokens, and count elements in range.\r
+            if (_PTokenState)\r
+            {\r
+                _JoinAllTokens_Add(_MergedSource, _PTokenState);\r
+                _PParam->_Resize(static_cast<size_t>(std::distance(_Begin, _End)));\r
+            }\r
+            else\r
+            {\r
+                size_t _TaskNum = 0;\r
+                for (auto _PTask = _Begin; _PTask != _End; ++_PTask)\r
+                {\r
+                    _TaskNum++;\r
+                    _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState);\r
+                }\r
+                _PParam->_Resize(_TaskNum);\r
+            }\r
+\r
+            // Step3: Check states of previous tasks.\r
+            if (_Begin == _End)\r
+            {\r
+                _PParam->_M_completed.set(_Unit_type());\r
+                delete _PParam;\r
+            }\r
+            else\r
+            {\r
+                for (auto _PTask = _Begin; _PTask != _End; ++_PTask)\r
+                {\r
+                    if (_PTask->is_apartment_aware())\r
+                    {\r
+                        _ReturnTask._SetAsync();\r
+                    }\r
+\r
+                    _PTask->_Then(\r
+                        [_PParam](task<void> _ResultTask) {\r
+                            auto _Func = []() {};\r
+                            _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask);\r
+                        },\r
+                        _CancellationTokenState::_None());\r
+                }\r
+            }\r
+\r
+            return _ReturnTask;\r
+        }\r
+    };\r
+\r
+    template<typename _ReturnType>\r
+    task<std::vector<_ReturnType>> _WhenAllVectorAndValue(\r
+        const task<std::vector<_ReturnType>>& _VectorTask, const task<_ReturnType>& _ValueTask, bool _OutputVectorFirst)\r
+    {\r
+        auto _PParam = new _RunAllParam<_ReturnType>();\r
+        cancellation_token_source _MergedSource;\r
+\r
+        // Step1: Create task completion event.\r
+        task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token());\r
+        // The return task must be created before step 3 to enforce inline execution.\r
+        auto _ReturnTask = _All_tasks_completed._Then(\r
+            [=](_Unit_type) -> std::vector<_ReturnType> {\r
+                _ASSERTE(_PParam->_M_completeCount == 2);\r
+                auto _Result = _PParam->_M_vector.Get(); // copy by value\r
+                auto _mergeVal = _PParam->_M_mergeVal.Get();\r
+\r
+                if (_OutputVectorFirst == true)\r
+                {\r
+                    _Result.push_back(_mergeVal);\r
+                }\r
+                else\r
+                {\r
+                    _Result.insert(_Result.begin(), _mergeVal);\r
+                }\r
+                return _Result;\r
+            },\r
+            nullptr);\r
+\r
+        // Step2: Combine and check tokens.\r
+        _JoinAllTokens_Add(_MergedSource, _VectorTask._GetImpl()->_M_pTokenState);\r
+        _JoinAllTokens_Add(_MergedSource, _ValueTask._GetImpl()->_M_pTokenState);\r
+\r
+        // Step3: Check states of previous tasks.\r
+        _PParam->_Resize(2, true);\r
+\r
+        if (_VectorTask.is_apartment_aware() || _ValueTask.is_apartment_aware())\r
+        {\r
+            _ReturnTask._SetAsync();\r
+        }\r
+        _VectorTask._Then(\r
+            [_PParam](task<std::vector<_ReturnType>> _ResultTask) {\r
+                auto _PParamCopy = _PParam;\r
+                auto _Func = [_PParamCopy, &_ResultTask]() {\r
+                    auto _ResultLocal = _ResultTask._GetImpl()->_GetResult();\r
+                    _PParamCopy->_M_vector.Set(_ResultLocal);\r
+                };\r
+\r
+                _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask);\r
+            },\r
+            _CancellationTokenState::_None());\r
+        _ValueTask._Then(\r
+            [_PParam](task<_ReturnType> _ResultTask) {\r
+                auto _PParamCopy = _PParam;\r
+                auto _Func = [_PParamCopy, &_ResultTask]() {\r
+                    auto _ResultLocal = _ResultTask._GetImpl()->_GetResult();\r
+                    _PParamCopy->_M_mergeVal.Set(_ResultLocal);\r
+                };\r
+\r
+                _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask);\r
+            },\r
+            _CancellationTokenState::_None());\r
+\r
+        return _ReturnTask;\r
+    }\r
+} // namespace details\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when all of the tasks supplied as arguments complete\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_Iterator">\r
+///     The type of the input iterator.\r
+/// </typeparam>\r
+/// <param name="_Begin">\r
+///     The position of the first element in the range of elements to be combined into the resulting task.\r
+/// </param>\r
+/// <param name="_End">\r
+///     The position of the first element beyond the range of elements to be combined into the resulting task.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when all of the input tasks have completed successfully. If the input tasks\r
+///     are of type <c>T</c>, the output of this function will be a <c>task&lt;std::vector&lt;T&gt;&gt;</c>. If the\r
+///     input tasks are of type <c>void</c> the output task will also be a <c>task&lt;void&gt;</c>.\r
+/// </returns>\r
+/// <remarks>\r
+///     If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled\r
+///     state, and the exception, if one is encountered, will be thrown if you call <c>get()</c> or <c>wait()</c> on\r
+///     that task.\r
+/// </remarks>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _Iterator>\r
+auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options())\r
+    -> decltype(details::_WhenAllImpl<typename std::iterator_traits<_Iterator>::value_type::result_type,\r
+                                      _Iterator>::_Perform(_TaskOptions, _Begin, _End))\r
+{\r
+    typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType;\r
+    return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End);\r
+}\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when both of the tasks supplied as arguments complete\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The type of the returned task.\r
+/// </typeparam>\r
+/// <param name="_Lhs">\r
+///     The first task to combine into the resulting task.\r
+/// </param>\r
+/// <param name="_Rhs">\r
+///     The second task to combine into the resulting task.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when both of the input tasks have completed successfully. If the input tasks\r
+///     are of type <c>T</c>, the output of this function will be a <c>task&lt;std::vector&lt;T&gt;&gt;</c>. If the\r
+///     input tasks are of type <c>void</c> the output task will also be a <c>task&lt;void&gt;</c>. <para> To allow for\r
+///     a construct of the sort taskA &amp;&amp; taskB &amp;&amp; taskC, which are combined in pairs, the &amp;&amp;\r
+///     operator produces a <c>task&lt;std::vector&lt;T&gt;&gt;</c> if either one or both of the tasks are of type\r
+///     <c>task&lt;std::vector&lt;T&gt;&gt;</c>.</para>\r
+/// </returns>\r
+/// <remarks>\r
+///     If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled\r
+///     state, and the exception, if one is encountered, will be thrown if you call <c>get()</c> or <c>wait()</c> on\r
+///     that task.\r
+/// </remarks>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _ReturnType>\r
+auto operator&&(const task<_ReturnType>& _Lhs, const task<_ReturnType>& _Rhs) -> decltype(when_all(&_Lhs, &_Lhs))\r
+{\r
+    task<_ReturnType> _PTasks[2] = {_Lhs, _Rhs};\r
+    return when_all(_PTasks, _PTasks + 2);\r
+}\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when both of the tasks supplied as arguments complete\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The type of the returned task.\r
+/// </typeparam>\r
+/// <param name="_Lhs">\r
+///     The first task to combine into the resulting task.\r
+/// </param>\r
+/// <param name="_Rhs">\r
+///     The second task to combine into the resulting task.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when both of the input tasks have completed successfully. If the input tasks\r
+///     are of type <c>T</c>, the output of this function will be a <c>task&lt;std::vector&lt;T&gt;&gt;</c>. If the\r
+///     input tasks are of type <c>void</c> the output task will also be a <c>task&lt;void&gt;</c>. <para> To allow for\r
+///     a construct of the sort taskA &amp;&amp; taskB &amp;&amp; taskC, which are combined in pairs, the &amp;&amp;\r
+///     operator produces a <c>task&lt;std::vector&lt;T&gt;&gt;</c> if either one or both of the tasks are of type\r
+///     <c>task&lt;std::vector&lt;T&gt;&gt;</c>.</para>\r
+/// </returns>\r
+/// <remarks>\r
+///     If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled\r
+///     state, and the exception, if one is encountered, will be thrown if you call <c>get()</c> or <c>wait()</c> on\r
+///     that task.\r
+/// </remarks>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _ReturnType>\r
+auto operator&&(const task<std::vector<_ReturnType>>& _Lhs, const task<_ReturnType>& _Rhs)\r
+    -> decltype(details::_WhenAllVectorAndValue(_Lhs, _Rhs, true))\r
+{\r
+    return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true);\r
+}\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when both of the tasks supplied as arguments complete\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The type of the returned task.\r
+/// </typeparam>\r
+/// <param name="_Lhs">\r
+///     The first task to combine into the resulting task.\r
+/// </param>\r
+/// <param name="_Rhs">\r
+///     The second task to combine into the resulting task.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when both of the input tasks have completed successfully. If the input tasks\r
+///     are of type <c>T</c>, the output of this function will be a <c>task&lt;std::vector&lt;T&gt;&gt;</c>. If the\r
+///     input tasks are of type <c>void</c> the output task will also be a <c>task&lt;void&gt;</c>. <para> To allow for\r
+///     a construct of the sort taskA &amp;&amp; taskB &amp;&amp; taskC, which are combined in pairs, the &amp;&amp;\r
+///     operator produces a <c>task&lt;std::vector&lt;T&gt;&gt;</c> if either one or both of the tasks are of type\r
+///     <c>task&lt;std::vector&lt;T&gt;&gt;</c>.</para>\r
+/// </returns>\r
+/// <remarks>\r
+///     If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled\r
+///     state, and the exception, if one is encountered, will be thrown if you call <c>get()</c> or <c>wait()</c> on\r
+///     that task.\r
+/// </remarks>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _ReturnType>\r
+auto operator&&(const task<_ReturnType>& _Lhs, const task<std::vector<_ReturnType>>& _Rhs)\r
+    -> decltype(details::_WhenAllVectorAndValue(_Rhs, _Lhs, false))\r
+{\r
+    return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false);\r
+}\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when both of the tasks supplied as arguments complete\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The type of the returned task.\r
+/// </typeparam>\r
+/// <param name="_Lhs">\r
+///     The first task to combine into the resulting task.\r
+/// </param>\r
+/// <param name="_Rhs">\r
+///     The second task to combine into the resulting task.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when both of the input tasks have completed successfully. If the input tasks\r
+///     are of type <c>T</c>, the output of this function will be a <c>task&lt;std::vector&lt;T&gt;&gt;</c>. If the\r
+///     input tasks are of type <c>void</c> the output task will also be a <c>task&lt;void&gt;</c>. <para> To allow for\r
+///     a construct of the sort taskA &amp;&amp; taskB &amp;&amp; taskC, which are combined in pairs, the &amp;&amp;\r
+///     operator produces a <c>task&lt;std::vector&lt;T&gt;&gt;</c> if either one or both of the tasks are of type\r
+///     <c>task&lt;std::vector&lt;T&gt;&gt;</c>.</para>\r
+/// </returns>\r
+/// <remarks>\r
+///     If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled\r
+///     state, and the exception, if one is encountered, will be thrown if you call <c>get()</c> or <c>wait()</c> on\r
+///     that task.\r
+/// </remarks>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _ReturnType>\r
+auto operator&&(const task<std::vector<_ReturnType>>& _Lhs, const task<std::vector<_ReturnType>>& _Rhs)\r
+    -> decltype(when_all(&_Lhs, &_Lhs))\r
+{\r
+    task<std::vector<_ReturnType>> _PTasks[2] = {_Lhs, _Rhs};\r
+    return when_all(_PTasks, _PTasks + 2);\r
+}\r
+\r
+namespace details\r
+{\r
+// Helper struct for when_any operators to know when tasks have completed\r
+template<typename _CompletionType>\r
+struct _RunAnyParam\r
+{\r
+    _RunAnyParam() : _M_exceptionRelatedToken(nullptr), _M_completeCount(0), _M_numTasks(0), _M_fHasExplicitToken(false)\r
+    {\r
+    }\r
+    ~_RunAnyParam()\r
+    {\r
+        if (_CancellationTokenState::_IsValid(_M_exceptionRelatedToken)) _M_exceptionRelatedToken->_Release();\r
+    }\r
+    task_completion_event<_CompletionType> _M_Completed;\r
+    cancellation_token_source _M_cancellationSource;\r
+    _CancellationTokenState* _M_exceptionRelatedToken;\r
+    atomic_size_t _M_completeCount;\r
+    size_t _M_numTasks;\r
+    bool _M_fHasExplicitToken;\r
+};\r
+\r
+template<typename _CompletionType, typename _Function, typename _TaskType>\r
+void _WhenAnyContinuationWrapper(_RunAnyParam<_CompletionType>* _PParam, const _Function& _Func, task<_TaskType>& _Task)\r
+{\r
+    bool _IsTokenCancled = !_PParam->_M_fHasExplicitToken &&\r
+                           _Task._GetImpl()->_M_pTokenState != _CancellationTokenState::_None() &&\r
+                           _Task._GetImpl()->_M_pTokenState->_IsCanceled();\r
+    if (_Task._GetImpl()->_IsCompleted() && !_IsTokenCancled)\r
+    {\r
+        _Func();\r
+        if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks)\r
+        {\r
+            delete _PParam;\r
+        }\r
+    }\r
+    else\r
+    {\r
+        _ASSERTE(_Task._GetImpl()->_IsCanceled() || _IsTokenCancled);\r
+        if (_Task._GetImpl()->_HasUserException() && !_IsTokenCancled)\r
+        {\r
+            if (_PParam->_M_Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder()))\r
+            {\r
+                // This can only enter once.\r
+                _PParam->_M_exceptionRelatedToken = _Task._GetImpl()->_M_pTokenState;\r
+                _ASSERTE(_PParam->_M_exceptionRelatedToken);\r
+                // Deref token will be done in the _PParam destructor.\r
+                if (_PParam->_M_exceptionRelatedToken != _CancellationTokenState::_None())\r
+                {\r
+                    _PParam->_M_exceptionRelatedToken->_Reference();\r
+                }\r
+            }\r
+        }\r
+\r
+        if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks)\r
+        {\r
+            // If no one has be completed so far, we need to make some final cancellation decision.\r
+            if (!_PParam->_M_Completed._IsTriggered())\r
+            {\r
+                // If we already explicit token, we can skip the token join part.\r
+                if (!_PParam->_M_fHasExplicitToken)\r
+                {\r
+                    if (_PParam->_M_exceptionRelatedToken)\r
+                    {\r
+                        _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PParam->_M_exceptionRelatedToken);\r
+                    }\r
+                    else\r
+                    {\r
+                        // If haven't captured any exception token yet, there was no exception for all those tasks,\r
+                        // so just pick a random token (current one) for normal cancellation.\r
+                        _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Task._GetImpl()->_M_pTokenState);\r
+                    }\r
+                }\r
+                // Do exception cancellation or normal cancellation based on whether it has stored exception.\r
+                _PParam->_M_Completed._Cancel();\r
+            }\r
+            delete _PParam;\r
+        }\r
+    }\r
+}\r
+\r
+template<typename _ElementType, typename _Iterator>\r
+struct _WhenAnyImpl\r
+{\r
+    static task<std::pair<_ElementType, size_t>> _Perform(const task_options& _TaskOptions,\r
+                                                          _Iterator _Begin,\r
+                                                          _Iterator _End)\r
+    {\r
+        if (_Begin == _End)\r
+        {\r
+            throw invalid_operation("when_any(begin, end) cannot be called on an empty container.");\r
+        }\r
+        _CancellationTokenState* _PTokenState =\r
+            _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;\r
+        auto _PParam = new _RunAnyParam<std::pair<std::pair<_ElementType, size_t>, _CancellationTokenState*>>();\r
+\r
+        if (_PTokenState)\r
+        {\r
+            _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState);\r
+            _PParam->_M_fHasExplicitToken = true;\r
+        }\r
+\r
+        task_options _Options(_TaskOptions);\r
+        _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token());\r
+        task<std::pair<std::pair<_ElementType, size_t>, _CancellationTokenState*>> _Any_tasks_completed(\r
+            _PParam->_M_Completed, _Options);\r
+\r
+        // Keep a copy ref to the token source\r
+        auto _CancellationSource = _PParam->_M_cancellationSource;\r
+\r
+        _PParam->_M_numTasks = static_cast<size_t>(std::distance(_Begin, _End));\r
+        size_t _Index = 0;\r
+        for (auto _PTask = _Begin; _PTask != _End; ++_PTask)\r
+        {\r
+            if (_PTask->is_apartment_aware())\r
+            {\r
+                _Any_tasks_completed._SetAsync();\r
+            }\r
+\r
+            _PTask->_Then(\r
+                [_PParam, _Index](task<_ElementType> _ResultTask) {\r
+                    auto _PParamCopy = _PParam; // Dev10\r
+                    auto _IndexCopy = _Index;   // Dev10\r
+                    auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() {\r
+                        _PParamCopy->_M_Completed.set(\r
+                            std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _IndexCopy),\r
+                                           _ResultTask._GetImpl()->_M_pTokenState));\r
+                    };\r
+\r
+                    _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);\r
+                },\r
+                _CancellationTokenState::_None());\r
+\r
+            _Index++;\r
+        }\r
+\r
+        // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created.\r
+        return _Any_tasks_completed._Then(\r
+            [=](std::pair<std::pair<_ElementType, size_t>, _CancellationTokenState*> _Result)\r
+                -> std::pair<_ElementType, size_t> {\r
+                _ASSERTE(_Result.second);\r
+                if (!_PTokenState)\r
+                {\r
+                    _JoinAllTokens_Add(_CancellationSource, _Result.second);\r
+                }\r
+                return _Result.first;\r
+            },\r
+            nullptr);\r
+    }\r
+};\r
+\r
+template<typename _Iterator>\r
+struct _WhenAnyImpl<void, _Iterator>\r
+{\r
+    static task<size_t> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End)\r
+    {\r
+        if (_Begin == _End)\r
+        {\r
+            throw invalid_operation("when_any(begin, end) cannot be called on an empty container.");\r
+        }\r
+\r
+        _CancellationTokenState* _PTokenState =\r
+            _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;\r
+        auto _PParam = new _RunAnyParam<std::pair<size_t, _CancellationTokenState*>>();\r
+\r
+        if (_PTokenState)\r
+        {\r
+            _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState);\r
+            _PParam->_M_fHasExplicitToken = true;\r
+        }\r
+\r
+        task_options _Options(_TaskOptions);\r
+        _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token());\r
+        task<std::pair<size_t, _CancellationTokenState*>> _Any_tasks_completed(_PParam->_M_Completed, _Options);\r
+\r
+        // Keep a copy ref to the token source\r
+        auto _CancellationSource = _PParam->_M_cancellationSource;\r
+\r
+        _PParam->_M_numTasks = static_cast<size_t>(std::distance(_Begin, _End));\r
+        size_t _Index = 0;\r
+        for (auto _PTask = _Begin; _PTask != _End; ++_PTask)\r
+        {\r
+            if (_PTask->is_apartment_aware())\r
+            {\r
+                _Any_tasks_completed._SetAsync();\r
+            }\r
+\r
+            _PTask->_Then(\r
+                [_PParam, _Index](task<void> _ResultTask) {\r
+                    auto _PParamCopy = _PParam; // Dev10\r
+                    auto _IndexCopy = _Index;   // Dev10\r
+                    auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() {\r
+                        _PParamCopy->_M_Completed.set(\r
+                            std::make_pair(_IndexCopy, _ResultTask._GetImpl()->_M_pTokenState));\r
+                    };\r
+                    _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);\r
+                },\r
+                _CancellationTokenState::_None());\r
+\r
+            _Index++;\r
+        }\r
+\r
+        // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created.\r
+        return _Any_tasks_completed._Then(\r
+            [=](std::pair<size_t, _CancellationTokenState*> _Result) -> size_t {\r
+                _ASSERTE(_Result.second);\r
+                if (!_PTokenState)\r
+                {\r
+                    _JoinAllTokens_Add(_CancellationSource, _Result.second);\r
+                }\r
+                return _Result.first;\r
+            },\r
+            nullptr);\r
+    }\r
+};\r
+} // namespace details\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when any of the tasks supplied as arguments completes\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_Iterator">\r
+///     The type of the input iterator.\r
+/// </typeparam>\r
+/// <param name="_Begin">\r
+///     The position of the first element in the range of elements to be combined into the resulting task.\r
+/// </param>\r
+/// <param name="_End">\r
+///     The position of the first element beyond the range of elements to be combined into the resulting task.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when any one of the input tasks has completed successfully. If the input\r
+///     tasks are of type <c>T</c>, the output of this function will be a <c>task&lt;std::pair&lt;T,\r
+///     size_t&gt;&gt;></c>, where the first element of the pair is the result of the completing task, and the second\r
+///     element is the index of the task that finished. If the input tasks are of type <c>void</c> the output is a\r
+///     <c>task&lt;size_t&gt;</c>, where the result is the index of the completing task.\r
+/// </returns>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _Iterator>\r
+auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options())\r
+    -> decltype(details::_WhenAnyImpl<typename std::iterator_traits<_Iterator>::value_type::result_type,\r
+                                      _Iterator>::_Perform(_TaskOptions, _Begin, _End))\r
+{\r
+    typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType;\r
+    return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End);\r
+}\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when any of the tasks supplied as arguments completes\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_Iterator">\r
+///     The type of the input iterator.\r
+/// </typeparam>\r
+/// <param name="_Begin">\r
+///     The position of the first element in the range of elements to be combined into the resulting task.\r
+/// </param>\r
+/// <param name="_End">\r
+///     The position of the first element beyond the range of elements to be combined into the resulting task.\r
+/// </param>\r
+/// <param name="_CancellationToken">\r
+///     The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation\r
+///     token, the resulting task will receive the cancellation token of the task that causes it to complete.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when any one of the input tasks has completed successfully. If the input\r
+///     tasks are of type <c>T</c>, the output of this function will be a <c>task&lt;std::pair&lt;T,\r
+///     size_t&gt;&gt;></c>, where the first element of the pair is the result of the completing task, and the second\r
+///     element is the index of the task that finished. If the input tasks are of type <c>void</c> the output is a\r
+///     <c>task&lt;size_t&gt;</c>, where the result is the index of the completing task.\r
+/// </returns>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _Iterator>\r
+auto when_any(_Iterator _Begin, _Iterator _End, cancellation_token _CancellationToken)\r
+    -> decltype(details::_WhenAnyImpl<typename std::iterator_traits<_Iterator>::value_type::result_type,\r
+                                      _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End))\r
+{\r
+    typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType;\r
+    return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End);\r
+}\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when either of the tasks supplied as arguments completes\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The type of the returned task.\r
+/// </typeparam>\r
+/// <param name="_Lhs">\r
+///     The first task to combine into the resulting task.\r
+/// </param>\r
+/// <param name="_Rhs">\r
+///     The second task to combine into the resulting task.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when either of the input tasks has completed successfully. If the input tasks\r
+///     are of type <c>T</c>, the output of this function will be a <c>task&lt;std::vector&lt;T&gt;</c>. If the input\r
+///     tasks are of type <c>void</c> the output task will also be a <c>task&lt;void&gt;</c>. <para> To allow for a\r
+///     construct of the sort taskA || taskB &amp;&amp; taskC, which are combined in pairs, with &amp;&amp; taking\r
+///     precedence over ||, the operator|| produces a <c>task&lt;std::vector&lt;T&gt;&gt;</c> if one of the tasks is of\r
+///     type <c>task&lt;std::vector&lt;T&gt;&gt;</c> and the other one is of type <c>task&lt;T&gt;.</c></para>\r
+/// </returns>\r
+/// <remarks>\r
+///     If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state,\r
+///     and one of the exceptions, if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on\r
+///     that task.\r
+/// </remarks>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _ReturnType>\r
+task<_ReturnType> operator||(const task<_ReturnType>& _Lhs, const task<_ReturnType>& _Rhs)\r
+{\r
+    auto _PParam = new details::_RunAnyParam<std::pair<_ReturnType, size_t>>();\r
+\r
+    task<std::pair<_ReturnType, size_t>> _Any_tasks_completed(_PParam->_M_Completed,\r
+                                                              _PParam->_M_cancellationSource.get_token());\r
+    // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called,\r
+    // So that _PParam can be used before it getting deleted.\r
+    auto _ReturnTask = _Any_tasks_completed._Then(\r
+        [=](std::pair<_ReturnType, size_t> _Ret) -> _ReturnType {\r
+            _ASSERTE(_Ret.second);\r
+            _JoinAllTokens_Add(_PParam->_M_cancellationSource,\r
+                               reinterpret_cast<details::_CancellationTokenState*>(_Ret.second));\r
+            return _Ret.first;\r
+        },\r
+        nullptr);\r
+\r
+    if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware())\r
+    {\r
+        _ReturnTask._SetAsync();\r
+    }\r
+\r
+    _PParam->_M_numTasks = 2;\r
+    auto _Continuation = [_PParam](task<_ReturnType> _ResultTask) {\r
+        //  Dev10 compiler bug\r
+        auto _PParamCopy = _PParam;\r
+        auto _Func = [&_ResultTask, _PParamCopy]() {\r
+            _PParamCopy->_M_Completed.set(\r
+                std::make_pair(_ResultTask._GetImpl()->_GetResult(),\r
+                               reinterpret_cast<size_t>(_ResultTask._GetImpl()->_M_pTokenState)));\r
+        };\r
+        _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);\r
+    };\r
+\r
+    _Lhs._Then(_Continuation, details::_CancellationTokenState::_None());\r
+    _Rhs._Then(_Continuation, details::_CancellationTokenState::_None());\r
+\r
+    return _ReturnTask;\r
+}\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when any of the tasks supplied as arguments completes\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The type of the returned task.\r
+/// </typeparam>\r
+/// <param name="_Lhs">\r
+///     The first task to combine into the resulting task.\r
+/// </param>\r
+/// <param name="_Rhs">\r
+///     The second task to combine into the resulting task.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when either of the input tasks has completed successfully. If the input tasks\r
+///     are of type <c>T</c>, the output of this function will be a <c>task&lt;std::vector&lt;T&gt;</c>. If the input\r
+///     tasks are of type <c>void</c> the output task will also be a <c>task&lt;void&gt;</c>. <para> To allow for a\r
+///     construct of the sort taskA || taskB &amp;&amp; taskC, which are combined in pairs, with &amp;&amp; taking\r
+///     precedence over ||, the operator|| produces a <c>task&lt;std::vector&lt;T&gt;&gt;</c> if one of the tasks is of\r
+///     type <c>task&lt;std::vector&lt;T&gt;&gt;</c> and the other one is of type <c>task&lt;T&gt;.</c></para>\r
+/// </returns>\r
+/// <remarks>\r
+///     If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state,\r
+///     and one of the exceptions, if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on\r
+///     that task.\r
+/// </remarks>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _ReturnType>\r
+task<std::vector<_ReturnType>> operator||(const task<std::vector<_ReturnType>>& _Lhs, const task<_ReturnType>& _Rhs)\r
+{\r
+    auto _PParam = new details::_RunAnyParam<std::pair<std::vector<_ReturnType>, details::_CancellationTokenState*>>();\r
+\r
+    task<std::pair<std::vector<_ReturnType>, details::_CancellationTokenState*>> _Any_tasks_completed(\r
+        _PParam->_M_Completed, _PParam->_M_cancellationSource.get_token());\r
+\r
+    // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called,\r
+    // So that _PParam can be used before it getting deleted.\r
+    auto _ReturnTask = _Any_tasks_completed._Then(\r
+        [=](std::pair<std::vector<_ReturnType>, details::_CancellationTokenState*> _Ret) -> std::vector<_ReturnType> {\r
+            _ASSERTE(_Ret.second);\r
+            _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second);\r
+            return _Ret.first;\r
+        },\r
+        nullptr);\r
+\r
+    if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware())\r
+    {\r
+        _ReturnTask._SetAsync();\r
+    }\r
+\r
+    _PParam->_M_numTasks = 2;\r
+    _Lhs._Then(\r
+        [_PParam](task<std::vector<_ReturnType>> _ResultTask) {\r
+            //  Dev10 compiler bug\r
+            auto _PParamCopy = _PParam;\r
+            auto _Func = [&_ResultTask, _PParamCopy]() {\r
+                auto _Result = _ResultTask._GetImpl()->_GetResult();\r
+                _PParamCopy->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState));\r
+            };\r
+            _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);\r
+        },\r
+        details::_CancellationTokenState::_None());\r
+\r
+    _Rhs._Then(\r
+        [_PParam](task<_ReturnType> _ResultTask) {\r
+            auto _PParamCopy = _PParam;\r
+            auto _Func = [&_ResultTask, _PParamCopy]() {\r
+                auto _Result = _ResultTask._GetImpl()->_GetResult();\r
+\r
+                std::vector<_ReturnType> _Vec;\r
+                _Vec.push_back(_Result);\r
+                _PParamCopy->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState));\r
+            };\r
+            _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);\r
+        },\r
+        details::_CancellationTokenState::_None());\r
+\r
+    return _ReturnTask;\r
+}\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when any of the tasks supplied as arguments completes\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The type of the returned task.\r
+/// </typeparam>\r
+/// <param name="_Lhs">\r
+///     The first task to combine into the resulting task.\r
+/// </param>\r
+/// <param name="_Rhs">\r
+///     The second task to combine into the resulting task.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when either of the input tasks has completed successfully. If the input tasks\r
+///     are of type <c>T</c>, the output of this function will be a <c>task&lt;std::vector&lt;T&gt;</c>. If the input\r
+///     tasks are of type <c>void</c> the output task will also be a <c>task&lt;void&gt;</c>. <para> To allow for a\r
+///     construct of the sort taskA || taskB &amp;&amp; taskC, which are combined in pairs, with &amp;&amp; taking\r
+///     precedence over ||, the operator|| produces a <c>task&lt;std::vector&lt;T&gt;&gt;</c> if one of the tasks is of\r
+///     type <c>task&lt;std::vector&lt;T&gt;&gt;</c> and the other one is of type <c>task&lt;T&gt;.</c></para>\r
+/// </returns>\r
+/// <remarks>\r
+///     If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state,\r
+///     and one of the exceptions, if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on\r
+///     that task.\r
+/// </remarks>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _ReturnType>\r
+auto operator||(const task<_ReturnType>& _Lhs, const task<std::vector<_ReturnType>>& _Rhs) -> decltype(_Rhs || _Lhs)\r
+{\r
+    return _Rhs || _Lhs;\r
+}\r
+\r
+/// <summary>\r
+///     Creates a task that will complete successfully when any of the tasks supplied as arguments completes\r
+///     successfully.\r
+/// </summary>\r
+/// <typeparam name="_ReturnType">\r
+///     The type of the returned task.\r
+/// </typeparam>\r
+/// <param name="_Lhs">\r
+///     The first task to combine into the resulting task.\r
+/// </param>\r
+/// <param name="_Rhs">\r
+///     The second task to combine into the resulting task.\r
+/// </param>\r
+/// <returns>\r
+///     A task that completes successfully when either of the input tasks has completed successfully. If the input tasks\r
+///     are of type <c>T</c>, the output of this function will be a <c>task&lt;std::vector&lt;T&gt;</c>. If the input\r
+///     tasks are of type <c>void</c> the output task will also be a <c>task&lt;void&gt;</c>. <para> To allow for a\r
+///     construct of the sort taskA || taskB &amp;&amp; taskC, which are combined in pairs, with &amp;&amp; taking\r
+///     precedence over ||, the operator|| produces a <c>task&lt;std::vector&lt;T&gt;&gt;</c> if one of the tasks is of\r
+///     type <c>task&lt;std::vector&lt;T&gt;&gt;</c> and the other one is of type <c>task&lt;T&gt;.</c></para>\r
+/// </returns>\r
+/// <remarks>\r
+///     If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state,\r
+///     and one of the exceptions, if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on\r
+///     that task.\r
+/// </remarks>\r
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>\r
+/**/\r
+template<typename _Ty = task<void>, typename _Pair = std::pair<details::_Unit_type, details::_CancellationTokenState*>>\r
+_Ty operator||(const task<void>& _Lhs_arg, const task<void>& _Rhs_arg)\r
+{\r
+    const _Ty& _Lhs = _Lhs_arg;\r
+    const _Ty& _Rhs = _Rhs_arg;\r
+    auto _PParam = new details::_RunAnyParam<_Pair>();\r
+\r
+    task<std::pair<details::_Unit_type, details::_CancellationTokenState*>> _Any_task_completed(\r
+        _PParam->_M_Completed, _PParam->_M_cancellationSource.get_token());\r
+    // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called,\r
+    // So that _PParam can be used before it getting deleted.\r
+    auto _ReturnTask = _Any_task_completed._Then(\r
+        [=](_Pair _Ret) {\r
+            _ASSERTE(_Ret.second);\r
+            details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second);\r
+        },\r
+        nullptr);\r
+\r
+    if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware())\r
+    {\r
+        _ReturnTask._SetAsync();\r
+    }\r
+\r
+    _PParam->_M_numTasks = 2;\r
+    auto _Continuation = [_PParam](_Ty _ResultTask) mutable {\r
+        //  Dev10 compiler needs this.\r
+        auto _PParam1 = _PParam;\r
+        auto _Func = [&_ResultTask, _PParam1]() {\r
+            _PParam1->_M_Completed.set(std::make_pair(details::_Unit_type(), _ResultTask._GetImpl()->_M_pTokenState));\r
+        };\r
+        _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);\r
+    };\r
+\r
+    _Lhs._Then(_Continuation, details::_CancellationTokenState::_None());\r
+    _Rhs._Then(_Continuation, details::_CancellationTokenState::_None());\r
+\r
+    return _ReturnTask;\r
+}\r
+\r
+template<typename _Ty>\r
+task<_Ty> task_from_result(_Ty _Param, const task_options& _TaskOptions = task_options())\r
+{\r
+    task_completion_event<_Ty> _Tce;\r
+    _Tce.set(_Param);\r
+    return create_task(_Tce, _TaskOptions);\r
+}\r
+\r
+template<class _Ty = void>\r
+inline task<_Ty> task_from_result(const task_options& _TaskOptions = task_options())\r
+{\r
+    task_completion_event<_Ty> _Tce;\r
+    _Tce.set();\r
+    return create_task(_Tce, _TaskOptions);\r
+}\r
+\r
+template<typename _TaskType, typename _ExType>\r
+task<_TaskType> task_from_exception(_ExType _Exception, const task_options& _TaskOptions = task_options())\r
+{\r
+    task_completion_event<_TaskType> _Tce;\r
+    _Tce.set_exception(_Exception);\r
+    return create_task(_Tce, _TaskOptions);\r
+}\r
+\r
+} // namespace pplx\r
+\r
+#pragma pop_macro("new")\r
+\r
+#if defined(_MSC_VER)\r
+#pragma warning(pop)\r
+#endif\r
+#pragma pack(pop)\r
+\r
+#endif // (defined(_MSC_VER) && (_MSC_VER >= 1800))\r
+\r
+#ifndef _CONCRT_H\r
+#ifndef _LWRCASE_CNCRRNCY\r
+#define _LWRCASE_CNCRRNCY\r
+// Note to reader: we're using lower-case namespace names everywhere, but the 'Concurrency' namespace\r
+// is capitalized for historical reasons. The alias let's us pretend that style issue doesn't exist.\r
+namespace Concurrency\r
+{\r
+}\r
+namespace concurrency = Concurrency;\r
+#endif\r
+#endif\r
+\r
+#endif // PPLXTASKS_H\r
diff --git a/Release/include/pplx/pplxwin.h b/Release/include/pplx/pplxwin.h
new file mode 100644 (file)
index 0000000..95a23b3
--- /dev/null
@@ -0,0 +1,268 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Windows specific pplx implementations
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if !defined(_WIN32) || _MSC_VER < 1800 || CPPREST_FORCE_PPLX
+
+#include "cpprest/details/cpprest_compat.h"
+#include "pplx/pplxinterface.h"
+
+namespace pplx
+{
+namespace details
+{
+namespace platform
+{
+/// <summary>
+/// Returns a unique identifier for the execution thread where this routine in invoked
+/// </summary>
+_PPLXIMP long __cdecl GetCurrentThreadId();
+
+/// <summary>
+/// Yields the execution of the current execution thread - typically when spin-waiting
+/// </summary>
+_PPLXIMP void __cdecl YieldExecution();
+
+/// <summary>
+/// Captures the callstack
+/// </summary>
+__declspec(noinline) _PPLXIMP size_t __cdecl CaptureCallstack(void**, size_t, size_t);
+
+#if defined(__cplusplus_winrt)
+/// <summary>
+// Internal API which retrieves the next async id.
+/// </summary>
+_PPLXIMP unsigned int __cdecl GetNextAsyncId();
+#endif
+} // namespace platform
+
+/// <summary>
+/// Manual reset event
+/// </summary>
+class event_impl
+{
+public:
+    static const unsigned int timeout_infinite = 0xFFFFFFFF;
+
+    _PPLXIMP event_impl();
+
+    _PPLXIMP ~event_impl();
+
+    _PPLXIMP void set();
+
+    _PPLXIMP void reset();
+
+    _PPLXIMP unsigned int wait(unsigned int timeout);
+
+    unsigned int wait() { return wait(event_impl::timeout_infinite); }
+
+private:
+    // Windows events
+    void* _M_impl;
+
+    event_impl(const event_impl&);                  // no copy constructor
+    event_impl const& operator=(const event_impl&); // no assignment operator
+};
+
+/// <summary>
+/// Mutex - lock for mutual exclusion
+/// </summary>
+class critical_section_impl
+{
+public:
+    _PPLXIMP critical_section_impl();
+
+    _PPLXIMP ~critical_section_impl();
+
+    _PPLXIMP void lock();
+
+    _PPLXIMP void unlock();
+
+private:
+    typedef void* _PPLX_BUFFER;
+
+    // Windows critical section
+    _PPLX_BUFFER _M_impl[8];
+
+    critical_section_impl(const critical_section_impl&);                  // no copy constructor
+    critical_section_impl const& operator=(const critical_section_impl&); // no assignment operator
+};
+
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/// <summary>
+/// Reader writer lock
+/// </summary>
+class reader_writer_lock_impl
+{
+public:
+    class scoped_lock_read
+    {
+    public:
+        explicit scoped_lock_read(reader_writer_lock_impl& _Reader_writer_lock)
+            : _M_reader_writer_lock(_Reader_writer_lock)
+        {
+            _M_reader_writer_lock.lock_read();
+        }
+
+        ~scoped_lock_read() { _M_reader_writer_lock.unlock(); }
+
+    private:
+        reader_writer_lock_impl& _M_reader_writer_lock;
+        scoped_lock_read(const scoped_lock_read&);                  // no copy constructor
+        scoped_lock_read const& operator=(const scoped_lock_read&); // no assignment operator
+    };
+
+    _PPLXIMP reader_writer_lock_impl();
+
+    _PPLXIMP void lock();
+
+    _PPLXIMP void lock_read();
+
+    _PPLXIMP void unlock();
+
+private:
+    // Windows slim reader writer lock
+    void* _M_impl;
+
+    // Slim reader writer lock doesn't have a general 'unlock' method.
+    // We need to track how it was acquired and release accordingly.
+    // true - lock exclusive
+    // false - lock shared
+    bool m_locked_exclusive;
+};
+#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
+
+/// <summary>
+/// Recursive mutex
+/// </summary>
+class recursive_lock_impl
+{
+public:
+    recursive_lock_impl() : _M_owner(-1), _M_recursionCount(0) {}
+
+    ~recursive_lock_impl()
+    {
+        _ASSERTE(_M_owner == -1);
+        _ASSERTE(_M_recursionCount == 0);
+    }
+
+    void lock()
+    {
+        auto id = ::pplx::details::platform::GetCurrentThreadId();
+
+        if (_M_owner == id)
+        {
+            _M_recursionCount++;
+        }
+        else
+        {
+            _M_cs.lock();
+            _M_owner = id;
+            _M_recursionCount = 1;
+        }
+    }
+
+    void unlock()
+    {
+        _ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId());
+        _ASSERTE(_M_recursionCount >= 1);
+
+        _M_recursionCount--;
+
+        if (_M_recursionCount == 0)
+        {
+            _M_owner = -1;
+            _M_cs.unlock();
+        }
+    }
+
+private:
+    pplx::details::critical_section_impl _M_cs;
+    long _M_recursionCount;
+    volatile long _M_owner;
+};
+
+class windows_scheduler : public pplx::scheduler_interface
+{
+public:
+    _PPLXIMP virtual void schedule(TaskProc_t proc, _In_ void* param);
+};
+
+} // namespace details
+
+/// <summary>
+///  A generic RAII wrapper for locks that implement the critical_section interface
+///  std::lock_guard
+/// </summary>
+template<class _Lock>
+class scoped_lock
+{
+public:
+    explicit scoped_lock(_Lock& _Critical_section) : _M_critical_section(_Critical_section)
+    {
+        _M_critical_section.lock();
+    }
+
+    ~scoped_lock() { _M_critical_section.unlock(); }
+
+private:
+    _Lock& _M_critical_section;
+
+    scoped_lock(const scoped_lock&);                  // no copy constructor
+    scoped_lock const& operator=(const scoped_lock&); // no assignment operator
+};
+
+// The extensibility namespace contains the type definitions that are used internally
+namespace extensibility
+{
+typedef ::pplx::details::event_impl event_t;
+
+typedef ::pplx::details::critical_section_impl critical_section_t;
+typedef scoped_lock<critical_section_t> scoped_critical_section_t;
+
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+typedef ::pplx::details::reader_writer_lock_impl reader_writer_lock_t;
+typedef scoped_lock<reader_writer_lock_t> scoped_rw_lock_t;
+typedef reader_writer_lock_t::scoped_lock_read scoped_read_lock_t;
+#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
+
+typedef ::pplx::details::recursive_lock_impl recursive_lock_t;
+typedef scoped_lock<recursive_lock_t> scoped_recursive_lock_t;
+} // namespace extensibility
+
+/// <summary>
+/// Default scheduler type
+/// </summary>
+typedef details::windows_scheduler default_scheduler_t;
+
+namespace details
+{
+/// <summary>
+/// Terminate the process due to unhandled exception
+/// </summary>
+
+#ifndef _REPORT_PPLTASK_UNOBSERVED_EXCEPTION
+#define _REPORT_PPLTASK_UNOBSERVED_EXCEPTION()                                                                         \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        __debugbreak();                                                                                                \
+        std::terminate();                                                                                              \
+    } while (false)
+#endif // _REPORT_PPLTASK_UNOBSERVED_EXCEPTION
+
+} // namespace details
+
+} // namespace pplx
+
+#endif
diff --git a/Release/include/pplx/threadpool.h b/Release/include/pplx/threadpool.h
new file mode 100644 (file)
index 0000000..b297ff6
--- /dev/null
@@ -0,0 +1,83 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ *
+ * Simple Linux implementation of a static thread pool.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ ***/
+#pragma once
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+#endif
+#include "boost/asio.hpp"
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#if defined(__ANDROID__)
+#include "pplx/pplx.h"
+#include <atomic>
+#include <jni.h>
+#endif
+
+#include "cpprest/details/cpprest_compat.h"
+
+namespace crossplat
+{
+#if defined(__ANDROID__)
+// IDEA: Break this section into a separate android/jni header
+extern std::atomic<JavaVM*> JVM;
+JNIEnv* get_jvm_env();
+
+struct java_local_ref_deleter
+{
+    void operator()(jobject lref) const { crossplat::get_jvm_env()->DeleteLocalRef(lref); }
+};
+
+template<class T>
+using java_local_ref = std::unique_ptr<typename std::remove_pointer<T>::type, java_local_ref_deleter>;
+#endif
+
+class threadpool
+{
+public:
+    _ASYNCRTIMP static threadpool& shared_instance();
+    _ASYNCRTIMP static std::unique_ptr<threadpool> __cdecl construct(size_t num_threads);
+
+    virtual ~threadpool() = default;
+
+    /// <summary>
+    /// Initializes the cpprestsdk threadpool with a custom number of threads
+    /// </summary>
+    /// <remarks>
+    /// This function allows an application (in their main function) to initialize the cpprestsdk
+    /// threadpool with a custom threadcount. Libraries should avoid calling this function to avoid
+    /// a diamond problem with multiple consumers attempting to customize the pool.
+    /// </remarks>
+    /// <exception cref="std::exception">Thrown if the threadpool has already been initialized</exception>
+    static void initialize_with_threads(size_t num_threads);
+
+    template<typename T>
+    CASABLANCA_DEPRECATED("Use `.service().post(task)` directly.")
+    void schedule(T task)
+    {
+        service().post(task);
+    }
+
+    boost::asio::io_service& service() { return m_service; }
+
+protected:
+    threadpool(size_t num_threads) : m_service(static_cast<int>(num_threads)) {}
+
+    boost::asio::io_service m_service;
+};
+
+} // namespace crossplat
diff --git a/Release/public_apis_doxyfile b/Release/public_apis_doxyfile
new file mode 100644 (file)
index 0000000..dcd0f85
--- /dev/null
@@ -0,0 +1,2330 @@
+# Doxyfile 1.8.7
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "C++ Rest SDK"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = .\include\
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                = include/cpprest/details/SafeInt3.hpp
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        = *::details::* _*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# compiled with the --with-libclang option.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+# These are defined to make sure the WebSocket APIs are picked up.
+PREDEFINED             = __cplusplus_winrt _NOT_PHONE8_
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/Release/samples/.gitignore b/Release/samples/.gitignore
new file mode 100644 (file)
index 0000000..211686a
--- /dev/null
@@ -0,0 +1 @@
+*.cmake
diff --git a/Release/samples/BingRequest/CMakeLists.txt b/Release/samples/BingRequest/CMakeLists.txt
new file mode 100644 (file)
index 0000000..41dc733
--- /dev/null
@@ -0,0 +1,4 @@
+if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE)
+  add_executable(BingRequest bingrequest.cpp)
+  target_link_libraries(BingRequest cpprest)
+endif()
\ No newline at end of file
diff --git a/Release/samples/BingRequest/bingrequest.cpp b/Release/samples/BingRequest/bingrequest.cpp
new file mode 100644 (file)
index 0000000..22625d7
--- /dev/null
@@ -0,0 +1,91 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * bingrequest.cpp - Simple cmd line application that makes an HTTP GET request to bing searching and outputting
+ *       the resulting HTML response body into a file.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include <cpprest/filestream.h>
+#include <cpprest/http_client.h>
+
+using namespace utility;
+using namespace web::http;
+using namespace web::http::client;
+using namespace concurrency::streams;
+
+/* Can pass proxy information via environment variable http_proxy.
+   Example:
+   Linux:   export http_proxy=http://192.1.8.1:8080
+ */
+web::http::client::http_client_config client_config_for_proxy()
+{
+    web::http::client::http_client_config client_config;
+#ifdef _WIN32
+    wchar_t* pValue = nullptr;
+    std::unique_ptr<wchar_t, void (*)(wchar_t*)> holder(nullptr, [](wchar_t* p) { free(p); });
+    size_t len = 0;
+    auto err = _wdupenv_s(&pValue, &len, L"http_proxy");
+    if (pValue) holder.reset(pValue);
+    if (!err && pValue && len)
+    {
+        std::wstring env_http_proxy_string(pValue, len - 1);
+#else
+    if (const char* env_http_proxy = std::getenv("http_proxy"))
+    {
+        std::string env_http_proxy_string(env_http_proxy);
+#endif
+        if (env_http_proxy_string == U("auto"))
+            client_config.set_proxy(web::web_proxy::use_auto_discovery);
+        else
+            client_config.set_proxy(web::web_proxy(env_http_proxy_string));
+    }
+
+    return client_config;
+}
+
+#ifdef _WIN32
+int wmain(int argc, wchar_t* args[])
+#else
+int main(int argc, char* args[])
+#endif
+{
+    if (argc != 3)
+    {
+        printf("Usage: BingRequest.exe search_term output_file\n");
+        return -1;
+    }
+    const string_t searchTerm = args[1];
+    const string_t outputFileName = args[2];
+
+    // Open a stream to the file to write the HTTP response body into.
+    auto fileBuffer = std::make_shared<streambuf<uint8_t>>();
+    file_buffer<uint8_t>::open(outputFileName, std::ios::out)
+        .then([=](streambuf<uint8_t> outFile) -> pplx::task<http_response> {
+            *fileBuffer = outFile;
+
+            // Create an HTTP request.
+            // Encode the URI query since it could contain special characters like spaces.
+            http_client client(U("http://www.bing.com/"), client_config_for_proxy());
+            return client.request(methods::GET, uri_builder(U("/search")).append_query(U("q"), searchTerm).to_string());
+        })
+
+        // Write the response body into the file buffer.
+        .then([=](http_response response) -> pplx::task<size_t> {
+            printf("Response status code %u returned.\n", response.status_code());
+
+            return response.body().read_to_end(*fileBuffer);
+        })
+
+        // Close the file buffer.
+        .then([=](size_t) { return fileBuffer->close(); })
+
+        // Wait for the entire response body to be written into the file.
+        .wait();
+
+    return 0;
+}
diff --git a/Release/samples/BlackJack/BlackJack_Client/BlackJackClient.cpp b/Release/samples/BlackJack/BlackJack_Client/BlackJackClient.cpp
new file mode 100644 (file)
index 0000000..184ab42
--- /dev/null
@@ -0,0 +1,303 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * BlackJackClient.cpp : Defines the entry point for the console application
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#ifdef _WIN32
+#include <SDKDDKVer.h>
+#include <stdio.h>
+#include <tchar.h>
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <objbase.h>
+#include <winsock2.h>
+
+#include <windows.h>
+
+// ws2tcpip.h - isn't warning clean.
+#pragma warning(push)
+#pragma warning(disable : 6386)
+#include <ws2tcpip.h>
+#pragma warning(pop)
+
+#include <iphlpapi.h>
+#endif
+
+#include "../BlackJack_Server/messagetypes.h"
+#include "cpprest/http_client.h"
+#include <exception>
+#include <fstream>
+#include <iostream>
+#include <locale>
+#include <map>
+#include <sstream>
+#include <streambuf>
+#include <string>
+#include <vector>
+
+using namespace std;
+using namespace web;
+using namespace utility;
+using namespace http;
+using namespace http::client;
+
+http_response CheckResponse(const std::string& url, const http_response& response)
+{
+    ucout << response.to_string() << endl;
+    return response;
+}
+
+http_response CheckResponse(const std::string& url, const http_response& response, bool& refresh)
+{
+    ucout << response.to_string() << endl;
+    BJPutResponse answer = BJPutResponse::FromJSON(response.extract_json().get());
+    refresh = answer.Status == ST_Refresh;
+    return response;
+}
+
+void PrintResult(BJHandResult result)
+{
+    switch (result)
+    {
+        case HR_PlayerBlackJack: ucout << "Black Jack"; break;
+        case HR_PlayerWin: ucout << "Player wins"; break;
+        case HR_ComputerWin: ucout << "Computer Wins"; break;
+        case HR_Push: ucout << "Push"; break;
+    }
+}
+
+void PrintCard(const Card& card)
+{
+    switch (card.value)
+    {
+        case CV_King: ucout << "K"; break;
+        case CV_Queen: ucout << "Q"; break;
+        case CV_Jack: ucout << "J"; break;
+        case CV_Ace: ucout << "A"; break;
+        default: ucout << (int)card.value; break;
+    }
+    switch (card.suit)
+    {
+        case CS_Club: ucout << "C"; break;
+        case CS_Spade: ucout << "S"; break;
+        case CS_Heart: ucout << "H"; break;
+        case CS_Diamond: ucout << "D"; break;
+    }
+}
+
+void PrintHand(bool suppress_bet, const BJHand& hand)
+{
+    if (!suppress_bet)
+    {
+        if (hand.insurance > 0)
+            ucout << "Bet: " << hand.bet << "Insurance: " << hand.insurance << " Hand: ";
+        else
+            ucout << "Bet: " << hand.bet << " Hand: ";
+    }
+    for (auto iter = hand.cards.begin(); iter != hand.cards.end(); iter++)
+    {
+        PrintCard(*iter);
+        ucout << " ";
+    }
+    PrintResult(hand.result);
+}
+
+void PrintTable(const http_response& response, bool& refresh)
+{
+    BJHand hand;
+
+    refresh = false;
+
+    if (response.status_code() == status_codes::OK)
+    {
+        if (response.headers().content_type() == U("application/json"))
+        {
+            BJPutResponse answer = BJPutResponse::FromJSON(response.extract_json().get());
+            json::value players = answer.Data[PLAYERS];
+
+            refresh = answer.Status == ST_Refresh;
+
+            for (auto iter = players.as_array().begin(); iter != players.as_array().end(); ++iter)
+            {
+                auto& player = *iter;
+
+                json::value name = player[NAME];
+                json::value bet = player[BALANCE];
+
+                bool suppressMoney = iter == players.as_array().begin();
+
+                if (suppressMoney)
+                    ucout << "'" << name.as_string() << "'";
+                else
+                    ucout << "'" << name.as_string() << "' Balance = $" << bet.as_double() << " ";
+
+                PrintHand(suppressMoney, BJHand::FromJSON(player[HAND].as_object()));
+                ucout << std::endl;
+            }
+
+            switch (answer.Status)
+            {
+                case ST_PlaceBet: ucout << "Place your bet!\n"; break;
+                case ST_YourTurn: ucout << "Your turn!\n"; break;
+            }
+        }
+    }
+}
+
+//
+// Entry point for the blackjack client.
+// Arguments: BlackJack_Client.exe <port>
+// If port is not specified, client will assume that the server is listening on port 34568
+//
+#ifdef _WIN32
+int wmain(int argc, wchar_t* argv[])
+#else
+int main(int argc, char* argv[])
+#endif
+{
+    utility::string_t port = U("34568");
+    if (argc == 2)
+    {
+        port = argv[1];
+    }
+
+    utility::string_t address = U("http://localhost:");
+    address.append(port);
+
+    http::uri uri = http::uri(address);
+
+    http_client bjDealer(http::uri_builder(uri).append_path(U("/blackjack/dealer")).to_uri());
+
+    utility::string_t userName;
+    utility::string_t table;
+
+    json::value availableTables = json::value::array();
+
+    bool was_refresh = false;
+
+    while (true)
+    {
+        while (was_refresh)
+        {
+            was_refresh = false;
+            utility::ostringstream_t buf;
+            buf << table << U("?request=refresh&name=") << userName;
+            PrintTable(CheckResponse("blackjack/dealer", bjDealer.request(methods::PUT, buf.str()).get()), was_refresh);
+        }
+
+        std::string method;
+        ucout << "Enter method:";
+        cin >> method;
+
+        const auto methodFirst = &method[0];
+        const auto methodLast = methodFirst + method.size();
+        std::use_facet<std::ctype<char>>(std::locale::classic()).tolower(methodFirst, methodLast);
+
+        if (method == "quit")
+        {
+            if (!userName.empty() && !table.empty())
+            {
+                utility::ostringstream_t buf;
+                buf << table << U("?name=") << userName;
+                CheckResponse("blackjack/dealer", bjDealer.request(methods::DEL, buf.str()).get());
+            }
+            break;
+        }
+
+        if (method == "name")
+        {
+            ucout << "Enter user name:";
+            ucin >> userName;
+        }
+        else if (method == "join")
+        {
+            ucout << "Enter table name:";
+            ucin >> table;
+
+            if (userName.empty())
+            {
+                ucout << "Must have a name first!\n";
+                continue;
+            }
+
+            utility::ostringstream_t buf;
+            buf << table << U("?name=") << userName;
+            CheckResponse("blackjack/dealer", bjDealer.request(methods::POST, buf.str()).get(), was_refresh);
+        }
+        else if (method == "hit" || method == "stay" || method == "double")
+        {
+            utility::ostringstream_t buf;
+            buf << table << U("?request=") << utility::conversions::to_string_t(method) << U("&name=") << userName;
+            PrintTable(CheckResponse("blackjack/dealer", bjDealer.request(methods::PUT, buf.str()).get()), was_refresh);
+        }
+        else if (method == "bet" || method == "insure")
+        {
+            utility::string_t bet;
+            ucout << "Enter bet:";
+            ucin >> bet;
+
+            if (userName.empty())
+            {
+                ucout << "Must have a name first!\n";
+                continue;
+            }
+
+            utility::ostringstream_t buf;
+            buf << table << U("?request=") << utility::conversions::to_string_t(method) << U("&name=") << userName
+                << U("&amount=") << bet;
+            PrintTable(CheckResponse("blackjack/dealer", bjDealer.request(methods::PUT, buf.str()).get()), was_refresh);
+        }
+        else if (method == "newtbl")
+        {
+            CheckResponse("blackjack/dealer", bjDealer.request(methods::POST).get(), was_refresh);
+        }
+        else if (method == "leave")
+        {
+            ucout << "Enter table:";
+            ucin >> table;
+
+            if (userName.empty())
+            {
+                ucout << "Must have a name first!\n";
+                continue;
+            }
+
+            utility::ostringstream_t buf;
+            buf << table << U("?name=") << userName;
+            CheckResponse("blackjack/dealer", bjDealer.request(methods::DEL, buf.str()).get(), was_refresh);
+        }
+        else if (method == "list")
+        {
+            was_refresh = false;
+            http_response response = CheckResponse("blackjack/dealer", bjDealer.request(methods::GET).get());
+
+            if (response.status_code() == status_codes::OK)
+            {
+                availableTables = response.extract_json().get();
+                for (auto iter = availableTables.as_array().begin(); iter != availableTables.as_array().end(); ++iter)
+                {
+                    BJTable bj_table = BJTable::FromJSON(iter->as_object());
+                    json::value id = json::value::number(bj_table.Id);
+
+                    ucout << "table " << bj_table.Id << ": {capacity: " << (long unsigned int)bj_table.Capacity
+                          << " no. players: " << (long unsigned int)bj_table.Players.size() << " }\n";
+                }
+                ucout << std::endl;
+            }
+        }
+        else
+        {
+            ucout << utility::conversions::to_string_t(method) << ": not understood\n";
+        }
+    }
+
+    return 0;
+}
diff --git a/Release/samples/BlackJack/BlackJack_Client/CMakeLists.txt b/Release/samples/BlackJack/BlackJack_Client/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e5845aa
--- /dev/null
@@ -0,0 +1,9 @@
+if (UNIX)
+  add_definitions(-Wno-switch)
+endif()
+
+add_executable(blackjackclient
+  BlackJackClient.cpp
+  )
+
+target_link_libraries(blackjackclient cpprest)
diff --git a/Release/samples/BlackJack/BlackJack_Server/BlackJack_Server.cpp b/Release/samples/BlackJack/BlackJack_Server/BlackJack_Server.cpp
new file mode 100644 (file)
index 0000000..72918ea
--- /dev/null
@@ -0,0 +1,89 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * BlackJack_Servr.cpp - Simple server application for blackjack
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace http;
+using namespace utility;
+using namespace http::experimental::listener;
+
+class BlackJackDealer
+{
+public:
+    BlackJackDealer() {}
+    BlackJackDealer(utility::string_t url);
+
+    pplx::task<void> open() { return m_listener.open(); }
+    pplx::task<void> close() { return m_listener.close(); }
+
+private:
+    void handle_get(http_request message);
+    void handle_put(http_request message);
+    void handle_post(http_request message);
+    void handle_delete(http_request message);
+
+    http_listener m_listener;
+};
+
+std::unique_ptr<BlackJackDealer> g_httpDealer;
+
+void on_initialize(const string_t& address)
+{
+    // Build our listener's URI from the configured address and the hard-coded path "blackjack/dealer"
+
+    uri_builder uri(address);
+    uri.append_path(U("blackjack/dealer"));
+
+    auto addr = uri.to_uri().to_string();
+    g_httpDealer = std::unique_ptr<BlackJackDealer>(new BlackJackDealer(addr));
+    g_httpDealer->open().wait();
+
+    ucout << utility::string_t(U("Listening for requests at: ")) << addr << std::endl;
+
+    return;
+}
+
+void on_shutdown()
+{
+    g_httpDealer->close().wait();
+    return;
+}
+
+//
+// To start the server, run the below command with admin privileges:
+// BlackJack_Server.exe <port>
+// If port is not specified, will listen on 34568
+//
+#ifdef _WIN32
+int wmain(int argc, wchar_t* argv[])
+#else
+int main(int argc, char* argv[])
+#endif
+{
+    utility::string_t port = U("34568");
+    if (argc == 2)
+    {
+        port = argv[1];
+    }
+
+    utility::string_t address = U("http://localhost:");
+    address.append(port);
+
+    on_initialize(address);
+    std::cout << "Press ENTER to exit." << std::endl;
+
+    std::string line;
+    std::getline(std::cin, line);
+
+    on_shutdown();
+    return 0;
+}
diff --git a/Release/samples/BlackJack/BlackJack_Server/CMakeLists.txt b/Release/samples/BlackJack/BlackJack_Server/CMakeLists.txt
new file mode 100644 (file)
index 0000000..25d8259
--- /dev/null
@@ -0,0 +1,13 @@
+if (UNIX)
+  add_definitions(-Wno-sign-compare -Wno-enum-compare)
+endif()
+
+add_executable(blackjackserver
+  BlackJack_Server.cpp
+  Dealer.cpp
+  Table.cpp
+  )
+
+target_link_libraries(blackjackserver cpprest)
+
+configure_pch(blackjackserver stdafx.h stdafx.cpp /Zm120)
diff --git a/Release/samples/BlackJack/BlackJack_Server/Dealer.cpp b/Release/samples/BlackJack/BlackJack_Server/Dealer.cpp
new file mode 100644 (file)
index 0000000..2fce239
--- /dev/null
@@ -0,0 +1,249 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Dealer.cpp : Contains the main logic of the black jack dealer
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "Table.h"
+#include "messagetypes.h"
+
+using namespace std;
+using namespace web;
+using namespace utility;
+using namespace http;
+using namespace web::http::experimental::listener;
+
+map<utility::string_t, std::shared_ptr<BJTable>> s_tables;
+int nextId = 1;
+
+class BlackJackDealer
+{
+public:
+    BlackJackDealer() {}
+    BlackJackDealer(utility::string_t url);
+
+    pplx::task<void> open() { return m_listener.open(); }
+    pplx::task<void> close() { return m_listener.close(); }
+
+private:
+    void handle_get(http_request message);
+    void handle_put(http_request message);
+    void handle_post(http_request message);
+    void handle_delete(http_request message);
+
+    http_listener m_listener;
+};
+
+BlackJackDealer::BlackJackDealer(utility::string_t url) : m_listener(url)
+{
+    m_listener.support(methods::GET, std::bind(&BlackJackDealer::handle_get, this, std::placeholders::_1));
+    m_listener.support(methods::PUT, std::bind(&BlackJackDealer::handle_put, this, std::placeholders::_1));
+    m_listener.support(methods::POST, std::bind(&BlackJackDealer::handle_post, this, std::placeholders::_1));
+    m_listener.support(methods::DEL, std::bind(&BlackJackDealer::handle_delete, this, std::placeholders::_1));
+
+    std::shared_ptr<DealerTable> tbl = std::make_shared<DealerTable>(nextId, 8, 6);
+    s_tables[conversions::to_string_t(std::to_string(nextId))] = tbl;
+    nextId += 1;
+}
+
+//
+// A GET of the dealer resource produces a list of existing tables.
+//
+void BlackJackDealer::handle_get(http_request message)
+{
+    ucout << message.to_string() << endl;
+
+    auto paths = http::uri::split_path(http::uri::decode(message.relative_uri().path()));
+    if (paths.empty())
+    {
+        message.reply(status_codes::OK, TablesAsJSON(U("Available Tables"), s_tables));
+        return;
+    }
+
+    utility::string_t wtable_id = paths[0];
+    const utility::string_t table_id = wtable_id;
+
+    // Get information on a specific table.
+    auto found = s_tables.find(table_id);
+    if (found == s_tables.end())
+    {
+        message.reply(status_codes::NotFound);
+    }
+    else
+    {
+        message.reply(status_codes::OK, found->second->AsJSON());
+    }
+};
+
+//
+// A POST of the dealer resource creates a new table and returns a resource for
+// that table.
+//
+void BlackJackDealer::handle_post(http_request message)
+{
+    ucout << message.to_string() << endl;
+
+    auto paths = uri::split_path(uri::decode(message.relative_uri().path()));
+
+    if (paths.empty())
+    {
+        utility::ostringstream_t nextIdString;
+        nextIdString << nextId;
+
+        std::shared_ptr<DealerTable> tbl = std::make_shared<DealerTable>(nextId, 8, 6);
+        s_tables[nextIdString.str()] = tbl;
+        nextId += 1;
+
+        message.reply(status_codes::OK, BJPutResponse(ST_PlaceBet, tbl->AsJSON()).AsJSON());
+        return;
+    }
+    utility::string_t wtable_id = paths[0];
+    const utility::string_t table_id = wtable_id;
+
+    // Join an existing table.
+    auto found = s_tables.find(table_id);
+    if (found == s_tables.end())
+    {
+        message.reply(status_codes::NotFound);
+        return;
+    }
+
+    auto table = std::static_pointer_cast<DealerTable>(found->second);
+
+    if (table->Players.size() < table->Capacity)
+    {
+        std::map<utility::string_t, utility::string_t> query =
+            uri::split_query(uri::decode(message.request_uri().query()));
+
+        auto cntEntry = query.find(QUERY_NAME);
+
+        if (cntEntry != query.end() && !cntEntry->second.empty())
+        {
+            table->AddPlayer(Player(cntEntry->second));
+            message.reply(status_codes::OK, BJPutResponse(ST_PlaceBet, table->AsJSON()).AsJSON());
+        }
+        else
+        {
+            message.reply(status_codes::Forbidden, U("Player name is required in query"));
+        }
+    }
+    else
+    {
+        utility::ostringstream_t os;
+        os << U("Table ") << table->Id << U(" is full");
+        message.reply(status_codes::Forbidden, os.str());
+    }
+};
+
+//
+// A DELETE of the player resource leaves the table.
+//
+void BlackJackDealer::handle_delete(http_request message)
+{
+    ucout << message.to_string() << endl;
+
+    auto paths = uri::split_path(uri::decode(message.relative_uri().path()));
+
+    if (paths.empty())
+    {
+        message.reply(status_codes::Forbidden, U("TableId is required."));
+        return;
+    }
+    utility::string_t wtable_id = paths[0];
+
+    const utility::string_t table_id = wtable_id;
+
+    // Get information on a specific table.
+    auto found = s_tables.find(table_id);
+    if (found == s_tables.end())
+    {
+        message.reply(status_codes::NotFound);
+        return;
+    }
+
+    auto table = std::static_pointer_cast<DealerTable>(found->second);
+
+    std::map<utility::string_t, utility::string_t> query = uri::split_query(uri::decode(message.request_uri().query()));
+
+    auto cntEntry = query.find(QUERY_NAME);
+
+    if (cntEntry != query.end())
+    {
+        if (table->RemovePlayer(cntEntry->second))
+        {
+            message.reply(status_codes::OK);
+        }
+        else
+        {
+            message.reply(status_codes::NotFound);
+        }
+    }
+    else
+    {
+        message.reply(status_codes::Forbidden, U("Player name is required in query"));
+    }
+};
+
+//
+// A PUT to a table resource makes a card request (hit / stay).
+//
+void BlackJackDealer::handle_put(http_request message)
+{
+    ucout << message.to_string() << endl;
+
+    auto paths = uri::split_path(uri::decode(message.relative_uri().path()));
+    auto query = uri::split_query(uri::decode(message.relative_uri().query()));
+    auto queryItr = query.find(REQUEST);
+    if (paths.empty() || queryItr == query.end())
+    {
+        message.reply(status_codes::Forbidden, U("TableId and request are required."));
+    }
+    utility::string_t wtable_id = paths[0];
+    utility::string_t request = queryItr->second;
+    const utility::string_t table_id = wtable_id;
+
+    // Get information on a specific table.
+    auto found = s_tables.find(table_id);
+    if (found == s_tables.end())
+    {
+        message.reply(status_codes::NotFound);
+    }
+
+    auto table = std::static_pointer_cast<DealerTable>(found->second);
+
+    if (request == BET)
+    {
+        table->Bet(message);
+    }
+    else if (request == DOUBLE)
+    {
+        table->DoubleDown(message);
+    }
+    else if (request == INSURE)
+    {
+        table->Insure(message);
+    }
+    else if (request == HIT)
+    {
+        table->Hit(message);
+    }
+    else if (request == STAY)
+    {
+        table->Stay(message);
+    }
+    else if (request == REFRESH)
+    {
+        table->Wait(message);
+    }
+    else
+    {
+        message.reply(status_codes::Forbidden, U("Unrecognized request"));
+    }
+};
diff --git a/Release/samples/BlackJack/BlackJack_Server/Table.cpp b/Release/samples/BlackJack/BlackJack_Server/Table.cpp
new file mode 100644 (file)
index 0000000..588f8e6
--- /dev/null
@@ -0,0 +1,529 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Table.cpp : Contains the main logic of game
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "Table.h"
+
+#include "messagetypes.h"
+
+using namespace std;
+using namespace web;
+using namespace utility;
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+void DealerTable::Deal()
+{
+    //
+    // Give everyone two cards.
+    //
+    Players[0].Hand.revealBoth = false;
+    m_currentPlayer = 0;
+    m_betting = false;
+
+    for (size_t i = 0; i < Players.size(); i++)
+    {
+        Players[i].Hand.Clear();
+        if (i == 0 || Players[i].Hand.bet > 0)
+        {
+            Card card = m_shoe.front();
+            m_shoe.pop();
+            Players[i].Hand.cards.push_back(card);
+        }
+    }
+    for (size_t i = 0; i < Players.size(); i++)
+    {
+        if (i == 0 || Players[i].Hand.bet > 0)
+        {
+            Card card = m_shoe.front();
+            m_shoe.pop();
+            Players[i].Hand.AddCard(card);
+        }
+    }
+
+    pplx::extensibility::scoped_critical_section_t lck(m_resplock);
+
+    for (size_t player = m_currentPlayer + 1; player < Players.size(); player++)
+    {
+        m_currentPlayer = (int)player;
+
+        if (Players[player].Hand.bet == 0) continue;
+
+        if (Players[player].Hand.state == HR_Active)
+        {
+            m_responses[player]->reply(status_codes::OK, BJPutResponse(ST_YourTurn, this->AsJSON()).AsJSON());
+            m_responses[player].reset();
+            break;
+        }
+    }
+
+    for (size_t i = 1; i < Players.size(); i++)
+    {
+        if (i != m_currentPlayer)
+        {
+            if (m_responses[i])
+            {
+                m_responses[i]->reply(status_codes::OK, BJPutResponse(ST_Refresh, this->AsJSON()).AsJSON());
+                m_responses[i].reset();
+            }
+            else
+            {
+                m_pendingrefresh[i] = ST_Refresh;
+            }
+        }
+    }
+}
+
+void DealerTable::Hit(http_request message)
+{
+    Card card = m_shoe.front();
+    m_shoe.pop();
+    Players[m_currentPlayer].Hand.AddCard(card);
+
+    if (Players[m_currentPlayer].Hand.state == HR_BlackJack || Players[m_currentPlayer].Hand.state == HR_Busted)
+    {
+        Stay(message);
+    }
+    else
+    {
+        message.reply(status_codes::OK, BJPutResponse(ST_YourTurn, this->AsJSON()).AsJSON());
+
+        pplx::extensibility::scoped_critical_section_t lck(m_resplock);
+
+        for (size_t i = 1; i < Players.size(); i++)
+        {
+            if (i != m_currentPlayer)
+            {
+                if (m_responses[i])
+                {
+                    m_responses[i]->reply(status_codes::OK, BJPutResponse(ST_Refresh, this->AsJSON()).AsJSON());
+                    m_responses[i].reset();
+                }
+                else
+                {
+                    m_pendingrefresh[i] = ST_Refresh;
+                }
+            }
+        }
+    }
+}
+
+void DealerTable::DoubleDown(http_request message)
+{
+    if (m_currentPlayer == 0 || Players[m_currentPlayer].Hand.state != HR_Active)
+    {
+        message.reply(status_codes::Forbidden, U("Not your turn"));
+        return;
+    }
+
+    Player& current = Players[m_currentPlayer];
+
+    if (current.Balance < current.Hand.bet)
+    {
+        message.reply(status_codes::Forbidden, U("Not enough money"));
+        return;
+    }
+    if (current.Hand.cards.size() > 2)
+    {
+        message.reply(status_codes::Forbidden, U("Too many cards"));
+        return;
+    }
+
+    // Double the bet
+
+    current.Balance -= current.Hand.bet;
+    current.Hand.bet *= 2;
+
+    // Take one card and then stay
+
+    Card card = m_shoe.front();
+    m_shoe.pop();
+    Players[m_currentPlayer].Hand.AddCard(card);
+
+    Stay(message);
+}
+
+void DealerTable::Wait(http_request message)
+{
+    utility::string_t name;
+
+    auto query = http::uri::split_query(http::uri::decode(message.relative_uri().query()));
+    auto itr = query.find(QUERY_NAME);
+    if (itr == query.end())
+    {
+        message.reply(status_codes::Forbidden, U("name and amount are required in query"));
+        return;
+    }
+    else
+        name = itr->second;
+
+    int playerIdx = FindPlayer(name);
+
+    if (playerIdx > 0)
+    {
+        pplx::extensibility::scoped_critical_section_t lck(m_resplock);
+
+        if (m_pendingrefresh[playerIdx] != ST_None)
+        {
+            message.reply(status_codes::OK, BJPutResponse(m_pendingrefresh[playerIdx], this->AsJSON()).AsJSON());
+            m_pendingrefresh[playerIdx] = ST_None;
+        }
+        else
+        {
+            m_responses[playerIdx] = message_wrapper(new http_request(message));
+        }
+    }
+}
+
+void DealerTable::Bet(http_request message)
+{
+    int amount;
+    utility::string_t name;
+
+    auto query = http::uri::split_query(http::uri::decode(message.relative_uri().query()));
+
+    auto itrAmount = query.find(AMOUNT), itrName = query.find(QUERY_NAME);
+    if (itrAmount == query.end() || itrName == query.end())
+    {
+        message.reply(status_codes::Forbidden, U("name and amount are required in query"));
+        return;
+    }
+    utility::istringstream_t ss(itrAmount->second);
+    ss >> amount;
+    name = itrName->second;
+
+    int playerIdx = FindPlayer(name);
+
+    if (playerIdx > 0)
+    {
+        Players[playerIdx].Balance -= amount;
+        Players[playerIdx].Hand.bet += amount;
+    }
+
+    m_betsMade += 1;
+
+    m_responses[playerIdx] = message_wrapper(new http_request(message));
+
+    if (m_betsMade == Players.size() - 1)
+    {
+        Deal();
+    }
+
+    // It is **possible** that all players have Blackjack, in which case
+    // the round is over. It's especially likely when there's only one
+    // player at the table.
+
+    if (m_currentPlayer == Players.size())
+    {
+        m_currentPlayer = 0;
+        DealerDeal();
+    }
+}
+
+void DealerTable::Insure(http_request message)
+{
+    int amount;
+    utility::string_t name;
+
+    auto query = http::uri::split_query(http::uri::decode(message.relative_uri().query()));
+
+    auto itrAmount = query.find(AMOUNT), itrName = query.find(QUERY_NAME);
+    if (itrAmount == query.end() || itrName == query.end())
+    {
+        message.reply(status_codes::Forbidden, U("name and amount are required in query"));
+        return;
+    }
+    utility::istringstream_t ss(itrAmount->second);
+    ss >> amount;
+    name = itrName->second;
+    int playerIdx = FindPlayer(name);
+
+    if (playerIdx > 0)
+    {
+        const BJHand& dealer = Players[0].Hand;
+        if (Players[playerIdx].Hand.insurance > 0.0)
+        {
+            message.reply(status_codes::Forbidden, U("Already insured"));
+            return;
+        }
+
+        if (dealer.cards.size() < 1 || dealer.revealBoth || dealer.cards[0].value != CV_Ace)
+        {
+            message.reply(status_codes::Forbidden, U("Dealer is not showing an Ace"));
+            return;
+        }
+
+        Players[playerIdx].Balance -= amount;
+        Players[playerIdx].Hand.insurance += amount;
+    }
+
+    message.reply(status_codes::OK, BJPutResponse(ST_YourTurn, this->AsJSON()).AsJSON());
+}
+
+void DealerTable::Stay(http_request message)
+{
+    if (m_currentPlayer == 0) return;
+
+    if (Players[m_currentPlayer].Hand.state == HR_Active) Players[m_currentPlayer].Hand.state = HR_Held;
+
+    // int idx = m_currentPlayer;
+
+    message.reply(status_codes::OK, BJPutResponse(ST_Refresh, this->AsJSON()).AsJSON());
+
+    NextPlayer(message);
+}
+
+void DealerTable::NextPlayer(http_request message)
+{
+    size_t player = m_currentPlayer + 1;
+
+    for (; player < Players.size(); player++)
+    {
+        pplx::extensibility::scoped_critical_section_t lck(m_resplock);
+
+        if (Players[player].Hand.bet > 0 && Players[player].Hand.state == HR_Active)
+        {
+            m_responses[player]->reply(status_codes::OK, BJPutResponse(ST_YourTurn, this->AsJSON()).AsJSON());
+            m_responses[player].reset();
+            break;
+        }
+    }
+
+    m_currentPlayer = (int)player;
+
+    if (m_currentPlayer == Players.size()) m_currentPlayer = 0;
+
+    if (m_currentPlayer == 0) DealerDeal();
+}
+
+void DealerTable::PayUp(size_t idx)
+{
+    Player& player = Players[idx];
+    if (player.Hand.result == HR_PlayerWin)
+    {
+        player.Balance += player.Hand.bet * 2;
+        player.Hand.bet = 0.0;
+    }
+    else if (player.Hand.result == HR_ComputerWin)
+    {
+        player.Hand.bet = 0.0;
+    }
+    else if (player.Hand.result == HR_PlayerBlackJack)
+    {
+        player.Balance += player.Hand.bet * 2.5;
+        player.Hand.bet = 0.0;
+    }
+    else if (player.Hand.result == HR_Push)
+    {
+        player.Balance += player.Hand.bet;
+        player.Hand.bet = 0.0;
+    }
+
+    // Handle insurance
+
+    if (player.Hand.insurance > 0 && Players[0].Hand.state == HR_PlayerBlackJack)
+    {
+        player.Balance += player.Hand.insurance * 3;
+    }
+    player.Hand.insurance = 0;
+
+    pplx::extensibility::scoped_critical_section_t lck(m_resplock);
+
+    if (m_responses[idx])
+    {
+        m_responses[idx]->reply(status_codes::OK, BJPutResponse(ST_PlaceBet, this->AsJSON()).AsJSON());
+        m_responses[idx].reset();
+    }
+    else
+    {
+        m_pendingrefresh[idx] = ST_PlaceBet;
+    }
+}
+
+void DealerTable::DealerDeal()
+{
+    BJHand& dealersHand = Players[0].Hand;
+
+    dealersHand.revealBoth = true;
+
+    NumericHandValues handValue = dealersHand.GetNumericValues();
+    while (handValue.high < 17 || (handValue.high > 21 && handValue.low < 17))
+    {
+        Card card = m_shoe.front();
+        m_shoe.pop();
+        dealersHand.AddCard(card);
+
+        if (dealersHand.state == HR_BlackJack || Players[m_currentPlayer].Hand.state == HR_Busted) break;
+
+        handValue = dealersHand.GetNumericValues();
+    }
+
+    for (size_t i = 1; i < Players.size(); i++)
+    {
+        Player& player = Players[i];
+
+        if (player.Hand.bet == 0.0)
+        {
+            player.Hand.result = HR_None;
+            continue;
+        }
+
+        if (player.Hand.state == HR_Busted)
+        {
+            player.Hand.result = HR_ComputerWin;
+            continue;
+        }
+
+        if (player.Hand.state == HR_BlackJack)
+        {
+            player.Hand.result = (dealersHand.state == HR_BlackJack) ? HR_Push : HR_PlayerBlackJack;
+            continue;
+        }
+
+        if (dealersHand.state == HR_Busted)
+        {
+            player.Hand.result = HR_PlayerWin;
+            continue;
+        }
+
+        if (dealersHand.state == HR_BlackJack)
+        {
+            player.Hand.result = HR_ComputerWin;
+            continue;
+        }
+
+        NumericHandValues value = player.Hand.GetNumericValues();
+        player.Hand.result = (value.Best() < handValue.Best())
+                                 ? HR_ComputerWin
+                                 : ((value.Best() == handValue.Best()) ? HR_Push : HR_PlayerWin);
+    }
+
+    m_betting = true;
+
+    for (size_t i = 1; i < Players.size(); i++)
+    {
+        PayUp(i);
+    }
+
+    m_betsMade = 0;
+}
+
+// TODO: Right now, players must make sure to get in when the dealer is taking bets.
+//       Change the logic to anticipate that a new player may come to the table at
+//       any point, and wait until the next round.
+bool DealerTable::AddPlayer(const Player& player)
+{
+    pplx::extensibility::scoped_critical_section_t lck(m_resplock);
+
+    int idx = FindPlayer(player.Name);
+
+    if (idx > 0) return false;
+
+    Players.push_back(player);
+    m_responses.push_back(message_wrapper());
+    m_pendingrefresh.push_back(ST_None);
+
+    return true;
+}
+
+bool DealerTable::RemovePlayer(const utility::string_t& name)
+{
+    pplx::extensibility::scoped_critical_section_t lck(m_resplock);
+
+    auto evnts = m_responses.begin();
+    auto pends = m_pendingrefresh.begin();
+
+    for (auto iter = Players.begin(); iter != Players.end(); iter++, evnts++, pends++)
+    {
+        if (iter->Name == name)
+        {
+            Players.erase(iter);
+            m_responses.erase(evnts);
+            m_pendingrefresh.erase(pends);
+            return true;
+        }
+    }
+    return false;
+}
+
+void DealerTable::FillShoe(size_t decks)
+{
+    //
+    // Value == suit*16+(facevalue-1)
+    //
+
+    //
+    // Stack the decks.
+    //
+    std::vector<int> shoe(decks * 52);
+
+    for (size_t d = 0; d < decks; d++)
+    {
+        for (int suit = 0; suit < 4; suit++)
+        {
+            for (int fv = 0; fv < 13; fv++)
+            {
+                shoe[d * 52 + suit * 13 + fv] = suit * 16 + (fv + 1);
+            }
+        }
+    }
+
+    // Randomize the shoe
+
+#ifdef _WIN32
+    LARGE_INTEGER li;
+    QueryPerformanceCounter(&li);
+    mt19937 eng(li.LowPart);
+#else
+    struct timeval val;
+    gettimeofday(&val, nullptr);
+    mt19937 eng(val.tv_usec);
+#endif
+    uniform_int_distribution<int> dist(0, (int)(decks * 52 - 1));
+
+#define ITER 4
+
+    for (size_t r = 0; r < ITER; r++)
+    {
+        for (size_t i = 0; i < decks * 52; i++)
+        {
+            int other = dist(eng);
+            swap(shoe[i], shoe[other]);
+        }
+    }
+
+    //
+    // Convert to the other format
+    //
+    for (size_t i = 0; i < decks * 52; i++)
+    {
+        Card card;
+        card.suit = (CardSuit)(shoe[i] / 16);
+        card.value = (CardValue)(shoe[i] % 16);
+        m_shoe.push(card);
+    }
+
+    m_stopAt = uniform_int_distribution<int>(26, 52)(eng);
+}
+
+int DealerTable::FindPlayer(const utility::string_t& name)
+{
+    int idx = 0;
+    for (auto iter = Players.begin(); iter != Players.end(); iter++, idx++)
+    {
+        if (iter->Name == name)
+        {
+            return idx;
+        }
+    }
+    return -1;
+}
diff --git a/Release/samples/BlackJack/BlackJack_Server/Table.h b/Release/samples/BlackJack/BlackJack_Server/Table.h
new file mode 100644 (file)
index 0000000..69f1d11
--- /dev/null
@@ -0,0 +1,79 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Table.h
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+#include "stdafx.h"
+
+#ifdef _WIN32
+#include <concrt.h>
+#endif
+#include "messagetypes.h"
+#include <queue>
+#include <string>
+#include <vector>
+
+class DealerTable : public BJTable
+{
+public:
+    DealerTable() : m_currentPlayer(0), m_betsMade(0), m_betting(true)
+    {
+        _init();
+        FillShoe(6);
+    }
+    DealerTable(int id, size_t capacity, int decks)
+        : BJTable(id, capacity), m_currentPlayer(0), m_betsMade(0), m_betting(true)
+    {
+        _init();
+        FillShoe(decks);
+    }
+
+    void Deal();
+    void Hit(web::http::http_request);
+    void Stay(web::http::http_request); // True if it's now your turn again, such as when you're the only player...
+
+    void DoubleDown(web::http::http_request);
+    void Bet(web::http::http_request);
+    void Insure(web::http::http_request);
+
+    void Wait(web::http::http_request);
+
+    bool AddPlayer(const Player& player);
+    bool RemovePlayer(const utility::string_t& name);
+
+private:
+    void FillShoe(size_t decks);
+    void DealerDeal();
+    void PayUp(size_t playerId);
+    void NextPlayer(web::http::http_request);
+
+    int FindPlayer(const utility::string_t& name);
+
+    void _init()
+    {
+        m_responses.push_back(message_wrapper());
+        m_pendingrefresh.push_back(ST_None);
+    }
+
+    int m_stopAt;
+    std::queue<Card> m_shoe;
+
+    bool m_betting;
+
+    pplx::extensibility::critical_section_t m_resplock;
+
+    typedef std::shared_ptr<web::http::http_request> message_wrapper;
+
+    std::vector<BJStatus> m_pendingrefresh;
+    std::vector<message_wrapper> m_responses;
+
+    int m_currentPlayer;
+    int m_betsMade;
+};
diff --git a/Release/samples/BlackJack/BlackJack_Server/messagetypes.h b/Release/samples/BlackJack/BlackJack_Server/messagetypes.h
new file mode 100644 (file)
index 0000000..f38556d
--- /dev/null
@@ -0,0 +1,403 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * messagetypes.h
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+#include "stdafx.h"
+
+enum BJHandResult
+{
+    HR_None,
+    HR_PlayerBlackJack,
+    HR_PlayerWin,
+    HR_ComputerWin,
+    HR_Push
+};
+
+enum BJHandState
+{
+    HR_Empty,
+    HR_BlackJack,
+    HR_Active,
+    HR_Held,
+    HR_Busted
+};
+
+enum BJPossibleMoves
+{
+    PM_None = 0x0,
+    PM_Hit = 0x1,
+    PM_DoubleDown = 0x2,
+    PM_Split = 0x4,
+    PM_All = 0x7
+};
+
+enum CardSuit
+{
+    CS_Heart,
+    CS_Diamond,
+    CS_Club,
+    CS_Spade
+};
+
+enum CardValue
+{
+    CV_None,
+    CV_Ace,
+    CV_Two,
+    CV_Three,
+    CV_Four,
+    CV_Five,
+    CV_Six,
+    CV_Seven,
+    CV_Eight,
+    CV_Nine,
+    CV_Ten,
+    CV_Jack,
+    CV_Queen,
+    CV_King
+};
+
+enum BJStatus
+{
+    ST_PlaceBet,
+    ST_Refresh,
+    ST_YourTurn,
+    ST_None
+};
+
+#define STATE U("state")
+#define BET U("bet")
+#define DOUBLE U("double")
+#define INSURE U("insure")
+#define HIT U("hit")
+#define STAY U("stay")
+#define REFRESH U("refresh")
+#define INSURANCE U("insurance")
+#define RESULT U("result")
+#define NAME U("Name")
+#define BALANCE U("Balance")
+#define HAND U("Hand")
+#define SUIT U("suit")
+#define VALUE U("value")
+#define CARDS U("cards")
+#define CAPACITY U("Capacity")
+#define ID U("Id")
+#define PLAYERS U("Players")
+#define DEALER U("DEALER")
+#define DATA U("Data")
+#define STATUS U("Status")
+#define REQUEST U("request")
+#define AMOUNT U("amount")
+#define QUERY_NAME U("name")
+
+struct Card
+{
+    CardSuit suit;
+    CardValue value;
+
+    static Card FromJSON(const web::json::object& object)
+    {
+        Card result;
+        result.suit = (CardSuit)object.at(SUIT).as_integer();
+        result.value = (CardValue)object.at(VALUE).as_integer();
+        return result;
+    }
+
+    web::json::value AsJSON() const
+    {
+        web::json::value result = web::json::value::object();
+        result[SUIT] = web::json::value::number(suit);
+        result[VALUE] = web::json::value::number(value);
+        return result;
+    }
+};
+
+struct NumericHandValues
+{
+    int low;
+    int high;
+
+    int Best() { return (high < 22) ? high : low; }
+};
+
+struct BJHand
+{
+    bool revealBoth;
+
+    std::vector<Card> cards;
+    double bet;
+    double insurance;
+    BJHandState state;
+    BJHandResult result;
+
+    BJHand() : state(HR_Empty), result(HR_None), bet(0.0), insurance(0), revealBoth(true) {}
+
+    void Clear()
+    {
+        cards.clear();
+        state = HR_Empty;
+        result = HR_None;
+        insurance = 0.0;
+    }
+
+    void AddCard(Card card)
+    {
+        cards.push_back(card);
+        NumericHandValues value = GetNumericValues();
+
+        if (cards.size() == 2 && value.high == 21)
+        {
+            state = HR_BlackJack;
+        }
+        else if (value.low > 21)
+        {
+            state = HR_Busted;
+        }
+        else
+        {
+            state = HR_Active;
+        }
+    }
+
+    NumericHandValues GetNumericValues()
+    {
+        NumericHandValues res;
+        res.low = 0;
+        res.low = 0;
+
+        bool hasAces = false;
+
+        for (auto iter = cards.begin(); iter != cards.end(); ++iter)
+        {
+            if (iter->value == CV_Ace) hasAces = true;
+
+            res.low += (std::min)((int)iter->value, 10);
+        }
+        res.high = hasAces ? res.low + 10 : res.low;
+        return res;
+    }
+
+    static BJHand FromJSON(const web::json::object& object)
+    {
+        BJHand res;
+
+        web::json::value cs = object.at(CARDS);
+
+        for (auto iter = cs.as_array().begin(); iter != cs.as_array().end(); ++iter)
+        {
+            if (!iter->is_null())
+            {
+                Card card;
+                card = Card::FromJSON(iter->as_object());
+                res.cards.push_back(card);
+            }
+        }
+
+        auto iState = object.find(STATE);
+        if (iState == object.end())
+        {
+            throw web::json::json_exception(U("STATE key not found"));
+        }
+        res.state = (BJHandState)iState->second.as_integer();
+        auto iBet = object.find(BET);
+        if (iBet == object.end())
+        {
+            throw web::json::json_exception(U("BET key not found"));
+        }
+        res.bet = iBet->second.as_double();
+        auto iInsurance = object.find(INSURANCE);
+        if (iInsurance == object.end())
+        {
+            throw web::json::json_exception(U("INSURANCE key not found"));
+        }
+        res.insurance = iInsurance->second.as_double();
+        auto iResult = object.find(RESULT);
+        if (iResult == object.end())
+        {
+            throw web::json::json_exception(U("RESULT key not found"));
+        }
+        res.result = (BJHandResult)object.find(RESULT)->second.as_integer();
+        return res;
+    }
+
+    web::json::value AsJSON() const
+    {
+        web::json::value res = web::json::value::object();
+        res[STATE] = web::json::value::number(state);
+        res[RESULT] = web::json::value::number(this->result);
+        res[BET] = web::json::value::number(bet);
+        res[INSURANCE] = web::json::value::number(insurance);
+
+        web::json::value jCards = web::json::value::array(cards.size());
+
+        if (revealBoth)
+        {
+            int idx = 0;
+            for (auto iter = cards.begin(); iter != cards.end(); ++iter)
+            {
+                jCards[idx++] = iter->AsJSON();
+            }
+        }
+        else
+        {
+            int idx = 0;
+            for (auto iter = cards.begin(); iter != cards.end();)
+            {
+                jCards[idx++] = iter->AsJSON();
+                break;
+            }
+        }
+        res[CARDS] = jCards;
+        return res;
+    }
+};
+
+struct Player
+{
+    utility::string_t Name;
+    BJHand Hand;
+    double Balance;
+
+    Player() {}
+    Player(const utility::string_t& name) : Name(name), Balance(1000.0) {}
+
+    static Player FromJSON(const web::json::object& object)
+    {
+        Player result(utility::string_t{});
+
+        auto iName = object.find(NAME);
+        if (iName == object.end())
+        {
+            throw web::json::json_exception(U("NAME key not found"));
+        }
+        const web::json::value& name = iName->second;
+        auto iBalance = object.find(BALANCE);
+        if (iBalance == object.end())
+        {
+            throw web::json::json_exception(U("BALANCE key not found"));
+        }
+        const web::json::value& balance = iBalance->second;
+        auto iHand = object.find(HAND);
+        if (iHand == object.end())
+        {
+            throw web::json::json_exception(U("HAND key not found"));
+        }
+        const web::json::value& hand = iHand->second;
+
+        result.Name = name.as_string();
+        result.Balance = balance.as_double();
+        result.Hand = BJHand::FromJSON(hand.as_object());
+        return result;
+    }
+
+    web::json::value AsJSON() const
+    {
+        web::json::value result = web::json::value::object();
+        result[NAME] = web::json::value::string(Name);
+        result[BALANCE] = web::json::value::number(Balance);
+        result[HAND] = Hand.AsJSON();
+        return result;
+    }
+};
+
+struct BJTable
+{
+    int Id;
+    size_t Capacity;
+    std::vector<Player> Players;
+
+    BJTable() : Capacity(0) {}
+    BJTable(int id, size_t capacity) : Id(id), Capacity(capacity) { Players.push_back(Player(DEALER)); }
+
+    static BJTable FromJSON(const web::json::object& object)
+    {
+        BJTable result;
+        auto iID = object.find(ID);
+        if (iID == object.end())
+        {
+            throw web::json::json_exception(U("ID key not found"));
+        }
+        result.Id = (int)iID->second.as_double();
+        auto iCapacity = object.find(CAPACITY);
+        if (iCapacity == object.end())
+        {
+            throw web::json::json_exception(U("CAPACITY key not found"));
+        }
+        result.Capacity = (size_t)iCapacity->second.as_double();
+
+        auto iPlayers = object.find(PLAYERS);
+        if (iPlayers == object.end())
+        {
+            throw web::json::json_exception(U("PLAYTERS key not found"));
+        }
+        web::json::value players = iPlayers->second;
+        int i = 0;
+
+        for (auto iter = players.as_array().begin(); iter != players.as_array().end(); ++iter, i++)
+        {
+            result.Players.push_back(Player::FromJSON(iter->as_object()));
+        }
+
+        return result;
+    }
+
+    web::json::value AsJSON() const
+    {
+        web::json::value result = web::json::value::object();
+        result[ID] = web::json::value::number((double)Id);
+        result[CAPACITY] = web::json::value::number((double)Capacity);
+
+        web::json::value jPlayers = web::json::value::array(Players.size());
+
+        size_t idx = 0;
+        for (auto iter = Players.begin(); iter != Players.end(); ++iter)
+        {
+            jPlayers[idx++] = iter->AsJSON();
+        }
+        result[PLAYERS] = jPlayers;
+        return result;
+    }
+};
+
+struct BJPutResponse
+{
+    BJStatus Status;
+    web::json::value Data;
+
+    BJPutResponse() {}
+    BJPutResponse(BJStatus status, web::json::value data) : Status(status), Data(data) {}
+
+    static BJPutResponse FromJSON(web::json::value object)
+    {
+        return BJPutResponse((BJStatus)(int)object[STATUS].as_double(), object[DATA]);
+    }
+
+    web::json::value AsJSON() const
+    {
+        web::json::value result = web::json::value::object();
+        result[STATUS] = web::json::value::number((double)Status);
+        result[DATA] = Data;
+        return result;
+    }
+};
+
+inline web::json::value TablesAsJSON(const utility::string_t& name,
+                                     const std::map<utility::string_t, std::shared_ptr<BJTable>>& tables)
+{
+    web::json::value result = web::json::value::array();
+
+    size_t idx = 0;
+    for (auto tbl = tables.begin(); tbl != tables.end(); tbl++)
+    {
+        result[idx++] = tbl->second->AsJSON();
+    }
+    return result;
+}
diff --git a/Release/samples/BlackJack/BlackJack_Server/stdafx.cpp b/Release/samples/BlackJack/BlackJack_Server/stdafx.cpp
new file mode 100644 (file)
index 0000000..516f368
--- /dev/null
@@ -0,0 +1,12 @@
+/***
+ * 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 : source file that includes just the standard includes
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
diff --git a/Release/samples/BlackJack/BlackJack_Server/stdafx.h b/Release/samples/BlackJack/BlackJack_Server/stdafx.h
new file mode 100644 (file)
index 0000000..d9bef58
--- /dev/null
@@ -0,0 +1,32 @@
+/***
+ * 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 : include file for standard system include files,
+ * or project specific include files that are used frequently,
+ * but are changed infrequently
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/http_listener.h"
+#include "cpprest/json.h"
+#include "cpprest/uri.h"
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <random>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <sys/time.h>
+#endif
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/App.xaml b/Release/samples/BlackJack/BlackJack_UIClient/App.xaml
new file mode 100644 (file)
index 0000000..2122064
--- /dev/null
@@ -0,0 +1,20 @@
+ļ»æ<Application
+    x:Class="BlackjackClient.App"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:BlackjackClient">
+
+    <Application.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+
+                <!-- 
+                    Styles that define common aspects of the platform look and feel
+                    Required by Visual Studio project and item templates
+                 -->
+                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
+            </ResourceDictionary.MergedDictionaries>
+
+        </ResourceDictionary>
+    </Application.Resources>
+</Application>
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/App.xaml.cpp b/Release/samples/BlackJack/BlackJack_UIClient/App.xaml.cpp
new file mode 100644 (file)
index 0000000..648d778
--- /dev/null
@@ -0,0 +1,113 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * App.xaml.cpp - Implementation of the App class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "pch.h"
+
+#include "PlayingTable.xaml.h"
+
+using namespace BlackjackClient;
+
+using namespace Platform;
+using namespace Windows::ApplicationModel;
+using namespace Windows::ApplicationModel::Activation;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Interop;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227
+
+/// <summary>
+/// Initializes the singleton application object.  This is the first line of authored code
+/// executed, and as such is the logical equivalent of main() or WinMain().
+/// </summary>
+App::App()
+{
+    InitializeComponent();
+    Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);
+}
+
+/// <summary>
+/// Invoked when the application is launched normally by the end user.  Other entry points
+/// will be used when the application is launched to open a specific file, to display
+/// search results, and so forth.
+/// </summary>
+/// <param name="args">Details about the launch request and process.</param>
+void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^ args)
+{
+    auto rootFrame = dynamic_cast<Frame ^>(Window::Current->Content);
+
+    // Do not repeat app initialization when the Window already has content,
+    // just ensure that the window is active
+    if (rootFrame == nullptr)
+    {
+        // Create a Frame to act as the navigation context and associate it with
+        // a SuspensionManager key
+        rootFrame = ref new Frame();
+
+        if (args->PreviousExecutionState == ApplicationExecutionState::Terminated)
+        {
+            // TODO: Restore the saved session state only when appropriate, scheduling the
+            // final launch steps after the restore is complete
+        }
+
+        if (rootFrame->Content == nullptr)
+        {
+            // When the navigation stack isn't restored navigate to the first page,
+            // configuring the new page by passing required information as a navigation
+            // parameter
+            if (!rootFrame->Navigate(TypeName(PlayingTable::typeid), args->Arguments))
+            {
+                throw ref new FailureException("Failed to create initial page");
+            }
+        }
+        // Place the frame in the current Window
+        Window::Current->Content = rootFrame;
+        // Ensure the current window is active
+        Window::Current->Activate();
+    }
+    else
+    {
+        if (rootFrame->Content == nullptr)
+        {
+            // When the navigation stack isn't restored navigate to the first page,
+            // configuring the new page by passing required information as a navigation
+            // parameter
+            if (!rootFrame->Navigate(TypeName(PlayingTable::typeid), args->Arguments))
+            {
+                throw ref new FailureException("Failed to create initial page");
+            }
+        }
+        // Ensure the current window is active
+        Window::Current->Activate();
+    }
+}
+
+/// <summary>
+/// Invoked when application execution is being suspended.  Application state is saved
+/// without knowing whether the application will be terminated or resumed with the contents
+/// of memory still intact.
+/// </summary>
+/// <param name="sender">The source of the suspend request.</param>
+/// <param name="e">Details about the suspend request.</param>
+void App::OnSuspending(Object ^ sender, SuspendingEventArgs ^ e)
+{
+    (void)sender; // Unused parameter
+    (void)e;      // Unused parameter
+
+    // TODO: Save application state and stop any background activity
+}
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/App.xaml.h b/Release/samples/BlackJack/BlackJack_UIClient/App.xaml.h
new file mode 100644 (file)
index 0000000..df0bbb4
--- /dev/null
@@ -0,0 +1,30 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * App.xaml.h - Declaration of the App class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "App.g.h"
+
+namespace BlackjackClient
+{
+/// <summary>
+/// Provides application-specific behavior to supplement the default Application class.
+/// </summary>
+ref class App sealed
+{
+public:
+    App();
+    virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^ args) override;
+
+private:
+    void OnSuspending(Platform::Object ^ sender, Windows::ApplicationModel::SuspendingEventArgs ^ e);
+};
+} // namespace BlackjackClient
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Assets/Logo.png b/Release/samples/BlackJack/BlackJack_UIClient/Assets/Logo.png
new file mode 100644 (file)
index 0000000..b437e8f
Binary files /dev/null and b/Release/samples/BlackJack/BlackJack_UIClient/Assets/Logo.png differ
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Assets/SmallLogo.png b/Release/samples/BlackJack/BlackJack_UIClient/Assets/SmallLogo.png
new file mode 100644 (file)
index 0000000..333f34b
Binary files /dev/null and b/Release/samples/BlackJack/BlackJack_UIClient/Assets/SmallLogo.png differ
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Assets/SplashScreen.png b/Release/samples/BlackJack/BlackJack_UIClient/Assets/SplashScreen.png
new file mode 100644 (file)
index 0000000..0466830
Binary files /dev/null and b/Release/samples/BlackJack/BlackJack_UIClient/Assets/SplashScreen.png differ
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Assets/StoreLogo.png b/Release/samples/BlackJack/BlackJack_UIClient/Assets/StoreLogo.png
new file mode 100644 (file)
index 0000000..898d374
Binary files /dev/null and b/Release/samples/BlackJack/BlackJack_UIClient/Assets/StoreLogo.png differ
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/CardShape.xaml b/Release/samples/BlackJack/BlackJack_UIClient/CardShape.xaml
new file mode 100644 (file)
index 0000000..466dfdf
--- /dev/null
@@ -0,0 +1,33 @@
+ļ»æ<UserControl
+    x:Class="BlackjackClient.CardShape"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:BlackjackClient"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d"
+    d:DesignHeight="97" d:DesignWidth="72">
+    <Canvas x:Name="LayoutRoot">
+        <Canvas x:Name="CardCanvas" Height="96" Width="71" RenderTransformOrigin="0.5,0.5">
+            <Canvas.RenderTransform>
+                <TransformGroup>
+                    <ScaleTransform/>
+                    <SkewTransform/>
+                    <RotateTransform/>
+                    <TranslateTransform/>
+                </TransformGroup>
+            </Canvas.RenderTransform>
+            <Image HorizontalAlignment="Left" Source="Cards.PNG" Stretch="None" VerticalAlignment="Top" RenderTransformOrigin="0.05,0.08" x:Name="imgCard">
+                <Image.RenderTransform> 
+                          <TransformGroup>
+                        <ScaleTransform/>
+                        <TranslateTransform X="0" Y="0"/>
+                    </TransformGroup>
+                </Image.RenderTransform>
+                <Image.Clip>
+                    <RectangleGeometry Rect="0,0,72,97"/>
+                </Image.Clip>
+            </Image>
+        </Canvas>
+    </Canvas>
+</UserControl>
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/CardShape.xaml.cpp b/Release/samples/BlackJack/BlackJack_UIClient/CardShape.xaml.cpp
new file mode 100644 (file)
index 0000000..188583d
--- /dev/null
@@ -0,0 +1,74 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * CardShape.xaml.cpp - Implementation of the CardShape class
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "pch.h"
+
+#include "CardShape.xaml.h"
+
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Media;
+using namespace BlackjackClient;
+
+CardShape::CardShape() : _suit(CS_Heart), _value(CV_Ace), _visible(true) { InitializeComponent(); }
+
+void CardShape::adjust()
+{
+    // Adjust the clipping of the cards image to reflect the current card
+    double x = 0;
+    double y = 0;
+
+    if (_visible)
+    {
+        x = (double)(int)(_value - 1);
+        y = (double)(int)_suit;
+    }
+    else
+    {
+        // Show back of the card
+        x = 0;
+        y = 4;
+    }
+
+    RectangleGeometry ^ clip = imgCard->Clip;
+    Windows::Foundation::Rect rect = clip->Rect;
+    rect.X = float(x * CardWidthRect);
+    rect.Y = float(y * CardHeightRect);
+
+    clip->Rect = rect;
+
+    TransformGroup ^ group = dynamic_cast<TransformGroup ^>(imgCard->RenderTransform);
+
+    if (group != nullptr)
+    {
+        for (unsigned int idx = 0; idx < group->Children->Size; idx++)
+        {
+            TranslateTransform ^ tfrm = dynamic_cast<TranslateTransform ^>(group->Children->GetAt(idx));
+            if (tfrm != nullptr)
+            {
+                tfrm->X = -x * CardWidthRect * SCALE_FACTOR;
+                tfrm->Y = -y * CardHeightRect * SCALE_FACTOR;
+            }
+
+            ScaleTransform ^ scale = dynamic_cast<ScaleTransform ^>(group->Children->GetAt(idx));
+            if (scale != nullptr)
+            {
+                scale->ScaleX = SCALE_FACTOR;
+                scale->ScaleY = SCALE_FACTOR;
+                scale->CenterX = -CardWidthRect / SCALE_FACTOR;
+                scale->CenterY = -CardHeightRect / SCALE_FACTOR;
+            }
+        }
+    }
+}
+
+CardShape::~CardShape() {}
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/CardShape.xaml.h b/Release/samples/BlackJack/BlackJack_UIClient/CardShape.xaml.h
new file mode 100644 (file)
index 0000000..db77540
--- /dev/null
@@ -0,0 +1,44 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * CardShape.xaml.h - Declaration of the CardShape class
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "pch.h"
+
+#include "CardShape.g.h"
+
+#define CardWidth 71
+#define CardHeight 96
+#define CardWidthRect 72
+#define CardHeightRect 97
+
+#define SCALE_FACTOR 1.80
+
+namespace BlackjackClient
+{
+public
+ref class CardShape sealed
+{
+public:
+    CardShape();
+    virtual ~CardShape();
+
+private:
+    friend ref class PlayingTable;
+    friend ref class PlayerSpace;
+
+    void adjust();
+
+    int _suit;
+    int _value;
+    Platform::Boolean _visible;
+};
+} // namespace BlackjackClient
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Cards.PNG b/Release/samples/BlackJack/BlackJack_UIClient/Cards.PNG
new file mode 100644 (file)
index 0000000..92e6afa
Binary files /dev/null and b/Release/samples/BlackJack/BlackJack_UIClient/Cards.PNG differ
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Common/StandardStyles.xaml b/Release/samples/BlackJack/BlackJack_UIClient/Common/StandardStyles.xaml
new file mode 100644 (file)
index 0000000..85f4ed6
--- /dev/null
@@ -0,0 +1,1829 @@
+ļ»æ<!--
+    This file contains XAML styles that simplify application development.
+
+    These are not merely convenient, but are required by most Visual Studio project and item templates.
+    Removing, renaming, or otherwise modifying the content of these files may result in a project that
+    does not build, or that will not build once additional pages are added.  If variations on these
+    styles are desired it is recommended that you copy the content under a new name and modify your
+    private copy.
+-->
+
+<ResourceDictionary
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+
+    <!-- Non-brush values that vary across themes -->
+
+    <ResourceDictionary.ThemeDictionaries>
+        <ResourceDictionary x:Key="Default">
+            <x:String x:Key="BackButtonGlyph">&#xE071;</x:String>
+            <x:String x:Key="BackButtonSnappedGlyph">&#xE0BA;</x:String>
+        </ResourceDictionary>
+
+        <ResourceDictionary x:Key="HighContrast">
+            <x:String x:Key="BackButtonGlyph">&#xE071;</x:String>
+            <x:String x:Key="BackButtonSnappedGlyph">&#xE0C4;</x:String>
+        </ResourceDictionary>
+    </ResourceDictionary.ThemeDictionaries>
+
+    <x:String x:Key="ChevronGlyph">&#xE26B;</x:String>
+
+    <!-- RichTextBlock styles -->
+
+    <Style x:Key="BasicRichTextStyle" TargetType="RichTextBlock">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationForegroundThemeBrush}"/>
+        <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>
+        <Setter Property="TextWrapping" Value="Wrap"/>
+        <Setter Property="Typography.StylisticSet20" Value="True"/>
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>
+    </Style>
+
+    <Style x:Key="BaselineRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BasicRichTextStyle}">
+        <Setter Property="LineHeight" Value="20"/>
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
+        <!-- Properly align text along its baseline -->
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="4"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="ItemRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaselineRichTextStyle}"/>
+
+    <Style x:Key="BodyRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaselineRichTextStyle}">
+        <Setter Property="FontWeight" Value="SemiLight"/>
+    </Style>
+
+    <!-- TextBlock styles -->
+
+    <Style x:Key="BasicTextStyle" TargetType="TextBlock">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationForegroundThemeBrush}"/>
+        <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>
+        <Setter Property="TextWrapping" Value="Wrap"/>
+        <Setter Property="Typography.StylisticSet20" Value="True"/>
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>
+    </Style>
+
+    <Style x:Key="BaselineTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BasicTextStyle}">
+        <Setter Property="LineHeight" Value="20"/>
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
+        <!-- Properly align text along its baseline -->
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="4"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="HeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontSize" Value="56"/>
+        <Setter Property="FontWeight" Value="Light"/>
+        <Setter Property="LineHeight" Value="40"/>
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-2" Y="8"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="SubheaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontSize" Value="26.667"/>
+        <Setter Property="FontWeight" Value="Light"/>
+        <Setter Property="LineHeight" Value="30"/>
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="6"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="TitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontWeight" Value="SemiBold"/>
+    </Style>
+
+    <Style x:Key="SubtitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontWeight" Value="Normal"/>
+    </Style>
+
+    <Style x:Key="ItemTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}"/>
+
+    <Style x:Key="BodyTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontWeight" Value="SemiLight"/>
+    </Style>
+
+    <Style x:Key="CaptionTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontSize" Value="12"/>
+        <Setter Property="Foreground" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>
+    </Style>
+
+    <Style x:Key="GroupHeaderTextStyle" TargetType="TextBlock">
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>
+        <Setter Property="TextWrapping" Value="NoWrap"/>
+        <Setter Property="Typography.StylisticSet20" Value="True"/>
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>
+        <Setter Property="FontSize" Value="26.667"/>
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
+        <Setter Property="FontWeight" Value="Light"/>
+        <Setter Property="LineHeight" Value="30"/>
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="6"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!-- Button styles -->
+    
+    <!--
+        TextButtonStyle is used to style a Button using subheader-styled text with no other adornment.  There
+        are two styles that are based on TextButtonStyle (TextPrimaryButtonStyle and TextSecondaryButtonStyle)
+        which are used in the GroupedItemsPage as a group header and in the FileOpenPickerPage for triggering
+        commands.
+    -->
+    <Style x:Key="TextButtonStyle" TargetType="ButtonBase">
+        <Setter Property="MinWidth" Value="0"/>
+        <Setter Property="MinHeight" Value="0"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="ButtonBase">
+                    <Grid Background="Transparent">
+                        <ContentPresenter x:Name="Text" Content="{TemplateBinding Content}" />
+                        <Rectangle
+                            x:Name="FocusVisualWhite"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="1.5"/>
+                        <Rectangle
+                            x:Name="FocusVisualBlack"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="0.5"/>
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal"/>
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetName="FocusVisualWhite" Storyboard.TargetProperty="Opacity"/>
+                                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetName="FocusVisualBlack" Storyboard.TargetProperty="Opacity"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused"/>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="CheckStates">
+                                <VisualState x:Name="Checked"/>
+                                <VisualState x:Name="Unchecked">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Indeterminate"/>
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="TextPrimaryButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource TextButtonStyle}">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationHeaderForegroundThemeBrush}"/>
+    </Style>
+
+    <Style x:Key="TextSecondaryButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource TextButtonStyle}">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>
+    </Style>
+
+    <!--
+        TextRadioButtonStyle is used to style a RadioButton using subheader-styled text with no other adornment.
+        This style is used in the SearchResultsPage to allow selection among filters.
+    -->
+    <Style x:Key="TextRadioButtonStyle" TargetType="RadioButton" BasedOn="{StaticResource TextButtonStyle}">
+        <Setter Property="Margin" Value="0,0,30,0"/>
+    </Style>
+
+    <!--
+        AppBarButtonStyle is used to style a Button (or ToggleButton) for use in an App Bar.  Content will be centered 
+        and should fit within the 40 pixel radius glyph provided.  16-point Segoe UI Symbol is used for content text 
+        to simplify the use of glyphs from that font.  AutomationProperties.Name is used for the text below the glyph.
+    -->
+    <Style x:Key="AppBarButtonStyle" TargetType="ButtonBase">
+        <Setter Property="Foreground" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+        <Setter Property="VerticalAlignment" Value="Stretch"/>
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
+        <Setter Property="FontWeight" Value="Normal"/>
+        <Setter Property="FontSize" Value="20"/>
+        <Setter Property="AutomationProperties.ItemType" Value="App Bar Button"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="ButtonBase">
+                    <Grid x:Name="RootGrid" Width="100" Background="Transparent">
+                        <StackPanel VerticalAlignment="Top" Margin="0,12,0,11">
+                            <Grid Width="40" Height="40" Margin="0,0,0,5" HorizontalAlignment="Center">
+                                <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0" Foreground="{StaticResource AppBarItemBackgroundThemeBrush}"/>
+                                <TextBlock x:Name="OutlineGlyph" Text="&#xE0A7;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0"/>
+                                <ContentPresenter x:Name="Content" HorizontalAlignment="Center" Margin="-1,-1,0,0" VerticalAlignment="Center"/>
+                            </Grid>
+                            <TextBlock
+                                x:Name="TextLabel"
+                                Text="{TemplateBinding AutomationProperties.Name}"
+                                Foreground="{StaticResource AppBarItemForegroundThemeBrush}"
+                                Margin="0,0,2,0"
+                                FontSize="12"
+                                TextAlignment="Center"
+                                Width="88"
+                                MaxHeight="32"
+                                TextTrimming="WordEllipsis"
+                                Style="{StaticResource BasicTextStyle}"/>
+                        </StackPanel>
+                        <Rectangle
+                                x:Name="FocusVisualWhite"
+                                IsHitTestVisible="False"
+                                Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                                StrokeEndLineCap="Square"
+                                StrokeDashArray="1,1"
+                                Opacity="0"
+                                StrokeDashOffset="1.5"/>
+                        <Rectangle
+                                x:Name="FocusVisualBlack"
+                                IsHitTestVisible="False"
+                                Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                                StrokeEndLineCap="Square"
+                                StrokeDashArray="1,1"
+                                Opacity="0"
+                                StrokeDashOffset="0.5"/>
+
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="ApplicationViewStates">
+                                <VisualState x:Name="FullScreenLandscape"/>
+                                <VisualState x:Name="Filled"/>
+                                <VisualState x:Name="FullScreenPortrait">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Width">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="60"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Snapped">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Width">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="60"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal"/>
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPointerOverBackgroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation
+                                                Storyboard.TargetName="FocusVisualWhite"
+                                                Storyboard.TargetProperty="Opacity"
+                                                To="1"
+                                                Duration="0"/>
+                                        <DoubleAnimation
+                                                Storyboard.TargetName="FocusVisualBlack"
+                                                Storyboard.TargetProperty="Opacity"
+                                                To="1"
+                                                Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused" />
+                                <VisualState x:Name="PointerFocused" />
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="CheckStates">
+                                <VisualState x:Name="Checked">
+                                    <Storyboard>
+                                        <DoubleAnimation Duration="0" To="0" Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Opacity"/>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundCheckedGlyph" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unchecked"/>
+                                <VisualState x:Name="Indeterminate"/>
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!-- 
+        Standard AppBarButton Styles for use with Button and ToggleButton
+    
+        An AppBarButton Style is provided for each of the glyphs in the Segoe UI Symbol font.  
+        Uncomment any style you reference (as not all may be required).
+    -->
+
+    <!--
+    
+    <Style x:Key="SkipBackAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SkipBackAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Skip Back"/>
+        <Setter Property="Content" Value="&#xE100;"/>
+    </Style>
+    <Style x:Key="SkipAheadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SkipAheadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Skip Ahead"/>
+        <Setter Property="Content" Value="&#xE101;"/>
+    </Style>
+    <Style x:Key="PlayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PlayAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Play"/>
+        <Setter Property="Content" Value="&#xE102;"/>
+    </Style>
+    <Style x:Key="PauseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PauseAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Pause"/>
+        <Setter Property="Content" Value="&#xE103;"/>
+    </Style>
+    <Style x:Key="EditAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="EditAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Edit"/>
+        <Setter Property="Content" Value="&#xE104;"/>
+    </Style>
+    <Style x:Key="SaveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SaveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Save"/>
+        <Setter Property="Content" Value="&#xE105;"/>
+    </Style>
+    <Style x:Key="DeleteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DeleteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Delete"/>
+        <Setter Property="Content" Value="&#xE106;"/>
+    </Style>
+    <Style x:Key="DiscardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DiscardAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Discard"/>
+        <Setter Property="Content" Value="&#xE107;"/>
+    </Style>
+    <Style x:Key="RemoveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RemoveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Remove"/>
+        <Setter Property="Content" Value="&#xE108;"/>
+    </Style>
+    <Style x:Key="AddAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AddAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Add"/>
+        <Setter Property="Content" Value="&#xE109;"/>
+    </Style>
+    <Style x:Key="NoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="No"/>
+        <Setter Property="Content" Value="&#xE10A;"/>
+    </Style>
+    <Style x:Key="YesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="YesAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Yes"/>
+        <Setter Property="Content" Value="&#xE10B;"/>
+    </Style>
+    <Style x:Key="MoreAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MoreAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="More"/>
+        <Setter Property="Content" Value="&#xE10C;"/>
+    </Style>
+    <Style x:Key="RedoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RedoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Redo"/>
+        <Setter Property="Content" Value="&#xE10D;"/>
+    </Style>
+    <Style x:Key="UndoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UndoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Undo"/>
+        <Setter Property="Content" Value="&#xE10E;"/>
+    </Style>
+    <Style x:Key="HomeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HomeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Home"/>
+        <Setter Property="Content" Value="&#xE10F;"/>
+    </Style>
+    <Style x:Key="OutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OutAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Out"/>
+        <Setter Property="Content" Value="&#xE110;"/>
+    </Style>
+    <Style x:Key="NextAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NextAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Next"/>
+        <Setter Property="Content" Value="&#xE111;"/>
+    </Style>
+    <Style x:Key="PreviousAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PreviousAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Previous"/>
+        <Setter Property="Content" Value="&#xE112;"/>
+    </Style>
+    <Style x:Key="FavoriteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FavoriteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Favorite"/>
+        <Setter Property="Content" Value="&#xE113;"/>
+    </Style>
+    <Style x:Key="PhotoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PhotoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Photo"/>
+        <Setter Property="Content" Value="&#xE114;"/>
+    </Style>
+    <Style x:Key="SettingsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SettingsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Settings"/>
+        <Setter Property="Content" Value="&#xE115;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="VideoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="VideoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Video"/>
+        <Setter Property="Content" Value="&#xE116;"/>
+    </Style>
+    <Style x:Key="RefreshAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RefreshAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Refresh"/>
+        <Setter Property="Content" Value="&#xE117;"/>
+    </Style>
+    <Style x:Key="DownloadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DownloadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Download"/>
+        <Setter Property="Content" Value="&#xE118;"/>
+    </Style>
+    <Style x:Key="MailAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Mail"/>
+        <Setter Property="Content" Value="&#xE119;"/>
+    </Style>
+    <Style x:Key="SearchAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SearchAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Search"/>
+        <Setter Property="Content" Value="&#xE11A;"/>
+    </Style>
+    <Style x:Key="HelpAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HelpAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Help"/>
+        <Setter Property="Content" Value="&#xE11B;"/>
+    </Style>
+    <Style x:Key="UploadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UploadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Upload"/>
+        <Setter Property="Content" Value="&#xE11C;"/>
+    </Style>
+    <Style x:Key="EmojiAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="EmojiAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Emoji"/>
+        <Setter Property="Content" Value="&#xE11D;"/>
+    </Style>
+    <Style x:Key="TwoPageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TwoPageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Two Page"/>
+        <Setter Property="Content" Value="&#xE11E;"/>
+    </Style>
+    <Style x:Key="LeaveChatAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LeaveChatAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Upload"/>
+        <Setter Property="Content" Value="&#xE11F;"/>
+    </Style>
+    <Style x:Key="MailForwardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailForwardAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Forward Mail"/>
+        <Setter Property="Content" Value="&#xE120;"/>
+    </Style>
+    <Style x:Key="ClockAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ClockAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Clock"/>
+        <Setter Property="Content" Value="&#xE121;"/>
+    </Style>
+    <Style x:Key="SendAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SendAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Send"/>
+        <Setter Property="Content" Value="&#xE122;"/>
+    </Style>
+    <Style x:Key="CropAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CropAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Crop"/>
+        <Setter Property="Content" Value="&#xE123;"/>
+    </Style>
+    <Style x:Key="RotateCameraAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RotateCameraAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Rotate Camera"/>
+        <Setter Property="Content" Value="&#xE124;"/>
+    </Style>
+    <Style x:Key="PeopleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PeopleAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="People"/>
+        <Setter Property="Content" Value="&#xE125;"/>
+    </Style>
+    <Style x:Key="ClosePaneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ClosePaneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Close Pane"/>
+        <Setter Property="Content" Value="&#xE126;"/>
+    </Style>
+    <Style x:Key="OpenPaneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenPaneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open Pane"/>
+        <Setter Property="Content" Value="&#xE127;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="WorldAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="WorldAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="World"/>
+        <Setter Property="Content" Value="&#xE128;"/>
+    </Style>
+    <Style x:Key="FlagAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FlagAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Flag"/>
+        <Setter Property="Content" Value="&#xE129;"/>
+    </Style>
+    <Style x:Key="PreviewLinkAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PreviewLinkAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Preview Link"/>
+        <Setter Property="Content" Value="&#xE12A;"/>
+    </Style>
+    <Style x:Key="GlobeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GlobeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Globe"/>
+        <Setter Property="Content" Value="&#xE12B;"/>
+    </Style>
+    <Style x:Key="TrimAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TrimAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Trim"/>
+        <Setter Property="Content" Value="&#xE12C;"/>
+    </Style>
+    <Style x:Key="AttachCameraAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AttachCameraAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Attach Camera"/>
+        <Setter Property="Content" Value="&#xE12D;"/>
+    </Style>
+    <Style x:Key="ZoomInAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomInAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zoom In"/>
+        <Setter Property="Content" Value="&#xE12E;"/>
+    </Style>
+    <Style x:Key="BookmarksAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BookmarksAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Bookmarks"/>
+        <Setter Property="Content" Value="&#xE12F;"/>
+    </Style>
+    <Style x:Key="DocumentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DocumentAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Document"/>
+        <Setter Property="Content" Value="&#xE130;"/>
+    </Style>
+    <Style x:Key="ProtectedDocumentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ProtectedDocumentAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Protected Document"/>
+        <Setter Property="Content" Value="&#xE131;"/>
+    </Style>
+    <Style x:Key="PageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Page"/>
+        <Setter Property="Content" Value="&#xE132;"/>
+    </Style>
+    <Style x:Key="BulletsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BulletsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Bullets"/>
+        <Setter Property="Content" Value="&#xE133;"/>
+    </Style>
+    <Style x:Key="CommentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CommentAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Comment"/>
+        <Setter Property="Content" Value="&#xE134;"/>
+    </Style>
+    <Style x:Key="Mail2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Mail2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Mail2"/>
+        <Setter Property="Content" Value="&#xE135;"/>
+    </Style>
+    <Style x:Key="ContactInfoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactInfoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Contact Info"/>
+        <Setter Property="Content" Value="&#xE136;"/>
+    </Style>
+    <Style x:Key="HangUpAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HangUpAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Hang Up"/>
+        <Setter Property="Content" Value="&#xE137;"/>
+    </Style>
+    <Style x:Key="ViewAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ViewAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="View All"/>
+        <Setter Property="Content" Value="&#xE138;"/>
+    </Style>
+    <Style x:Key="MapPinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MapPinAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Map Pin"/>
+        <Setter Property="Content" Value="&#xE139;"/>
+    </Style>
+    <Style x:Key="PhoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PhoneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Phone"/>
+        <Setter Property="Content" Value="&#xE13A;"/>
+    </Style>
+    <Style x:Key="VideoChatAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="VideoChatAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Video Chat"/>
+        <Setter Property="Content" Value="&#xE13B;"/>
+    </Style>
+    <Style x:Key="SwitchAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SwitchAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Switch"/>
+        <Setter Property="Content" Value="&#xE13C;"/>
+    </Style>
+    <Style x:Key="ContactAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Contact"/>
+        <Setter Property="Content" Value="&#xE13D;"/>
+    </Style>
+
+    -->
+
+    <!--
+
+    <Style x:Key="RenameAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RenameAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Rename"/>
+        <Setter Property="Content" Value="&#xE13E;"/>
+    </Style>
+    <Style x:Key="PinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PinAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Pin"/>
+        <Setter Property="Content" Value="&#xE141;"/>
+    </Style>
+    <Style x:Key="MusicInfoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MusicInfoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Music Info"/>
+        <Setter Property="Content" Value="&#xE142;"/>
+    </Style>
+    <Style x:Key="GoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Go"/>
+        <Setter Property="Content" Value="&#xE143;"/>
+    </Style>
+    <Style x:Key="KeyboardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="KeyboardAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Keyboard"/>
+        <Setter Property="Content" Value="&#xE144;"/>
+    </Style>
+    <Style x:Key="DockLeftAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DockLeftAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dock Left"/>
+        <Setter Property="Content" Value="&#xE145;"/>
+    </Style>
+    <Style x:Key="DockRightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DockRightAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dock Right"/>
+        <Setter Property="Content" Value="&#xE146;"/>
+    </Style>
+    <Style x:Key="DockBottomAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DockBottomAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dock Bottom"/>
+        <Setter Property="Content" Value="&#xE147;"/>
+    </Style>
+    <Style x:Key="RemoteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RemoteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Remote"/>
+        <Setter Property="Content" Value="&#xE148;"/>
+    </Style>
+    <Style x:Key="SyncAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SyncAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Sync"/>
+        <Setter Property="Content" Value="&#xE149;"/>
+    </Style>
+    <Style x:Key="RotateAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RotateAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Rotate"/>
+        <Setter Property="Content" Value="&#xE14A;"/>
+    </Style>
+    <Style x:Key="ShuffleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShuffleAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Shuffle"/>
+        <Setter Property="Content" Value="&#xE14B;"/>
+    </Style>
+    <Style x:Key="ListAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ListAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="List"/>
+        <Setter Property="Content" Value="&#xE14C;"/>
+    </Style>
+    <Style x:Key="ShopAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShopAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Shop"/>
+        <Setter Property="Content" Value="&#xE14D;"/>
+    </Style>
+    <Style x:Key="SelectAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SelectAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Select All"/>
+        <Setter Property="Content" Value="&#xE14E;"/>
+    </Style>
+    <Style x:Key="OrientationAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OrientationAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Orientation"/>
+        <Setter Property="Content" Value="&#xE14F;"/>
+    </Style>
+    <Style x:Key="ImportAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Import"/>
+        <Setter Property="Content" Value="&#xE150;"/>
+    </Style>
+    <Style x:Key="ImportAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Import All"/>
+        <Setter Property="Content" Value="&#xE151;"/>
+    </Style>
+    <Style x:Key="BrowsePhotosAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BrowsePhotosAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Browse Photos"/>
+        <Setter Property="Content" Value="&#xE155;"/>
+    </Style>
+    <Style x:Key="WebcamAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="WebcamAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Webcam"/>
+        <Setter Property="Content" Value="&#xE156;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="PicturesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PicturesAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Pictures"/>
+        <Setter Property="Content" Value="&#xE158;"/>
+    </Style>
+    <Style x:Key="SaveLocalAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SaveLocalAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Save Local"/>
+        <Setter Property="Content" Value="&#xE159;"/>
+    </Style>
+    <Style x:Key="CaptionAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CaptionAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Caption"/>
+        <Setter Property="Content" Value="&#xE15A;"/>
+    </Style>
+    <Style x:Key="StopAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="StopAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Stop"/>
+        <Setter Property="Content" Value="&#xE15B;"/>
+    </Style>
+    <Style x:Key="ShowResultsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShowResultsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Show Results"/>
+        <Setter Property="Content" Value="&#xE15C;"/>
+    </Style>
+    <Style x:Key="VolumeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="VolumeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Volume"/>
+        <Setter Property="Content" Value="&#xE15D;"/>
+    </Style>
+    <Style x:Key="RepairAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RepairAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Repair"/>
+        <Setter Property="Content" Value="&#xE15E;"/>
+    </Style>
+    <Style x:Key="MessageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MessageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Message"/>
+        <Setter Property="Content" Value="&#xE15F;"/>
+    </Style>
+    <Style x:Key="Page2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Page2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Page2"/>
+        <Setter Property="Content" Value="&#xE160;"/>
+    </Style>
+    <Style x:Key="CalendarDayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarDayAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Day"/>
+        <Setter Property="Content" Value="&#xE161;"/>
+    </Style>
+    <Style x:Key="CalendarWeekAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarWeekAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Week"/>
+        <Setter Property="Content" Value="&#xE162;"/>
+    </Style>
+    <Style x:Key="CalendarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Calendar"/>
+        <Setter Property="Content" Value="&#xE163;"/>
+    </Style>
+    <Style x:Key="CharactersAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CharactersAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Characters"/>
+        <Setter Property="Content" Value="&#xE164;"/>
+    </Style>
+    <Style x:Key="MailReplyAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailReplyAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Reply All"/>
+        <Setter Property="Content" Value="&#xE165;"/>
+    </Style>
+    <Style x:Key="ReadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ReadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Read"/>
+        <Setter Property="Content" Value="&#xE166;"/>
+    </Style>
+    <Style x:Key="LinkAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LinkAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Link"/>
+        <Setter Property="Content" Value="&#xE167;"/>
+    </Style>
+    <Style x:Key="AccountsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AccountsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Accounts"/>
+        <Setter Property="Content" Value="&#xE168;"/>
+    </Style>
+    <Style x:Key="ShowBccAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShowBccAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Show Bcc"/>
+        <Setter Property="Content" Value="&#xE169;"/>
+    </Style>
+    <Style x:Key="HideBccAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HideBccAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Hide Bcc"/>
+        <Setter Property="Content" Value="&#xE16A;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="CutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CutAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Cut"/>
+        <Setter Property="Content" Value="&#xE16B;"/>
+    </Style>
+    <Style x:Key="AttachAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AttachAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Attach"/>
+        <Setter Property="Content" Value="&#xE16C;"/>
+    </Style>
+    <Style x:Key="PasteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PasteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Paste"/>
+        <Setter Property="Content" Value="&#xE16D;"/>
+    </Style>
+    <Style x:Key="FilterAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FilterAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Filter"/>
+        <Setter Property="Content" Value="&#xE16E;"/>
+    </Style>
+    <Style x:Key="CopyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CopyAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Copy"/>
+        <Setter Property="Content" Value="&#xE16F;"/>
+    </Style>
+    <Style x:Key="Emoji2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Emoji2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Emoji2"/>
+        <Setter Property="Content" Value="&#xE170;"/>
+    </Style>
+    <Style x:Key="ImportantAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportantAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Important"/>
+        <Setter Property="Content" Value="&#xE171;"/>
+    </Style>
+    <Style x:Key="MailReplyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailReplyAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Reply"/>
+        <Setter Property="Content" Value="&#xE172;"/>
+    </Style>
+    <Style x:Key="SlideShowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SlideShowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Slideshow"/>
+        <Setter Property="Content" Value="&#xE173;"/>
+    </Style>
+    <Style x:Key="SortAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SortAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Sort"/>
+        <Setter Property="Content" Value="&#xE174;"/>
+    </Style>
+    <Style x:Key="ManageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ManageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Manage"/>
+        <Setter Property="Content" Value="&#xE178;"/>
+    </Style>
+    <Style x:Key="AllAppsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AllAppsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="All Apps"/>
+        <Setter Property="Content" Value="&#xE179;"/>
+    </Style>
+    <Style x:Key="DisconnectDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DisconnectDriveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Disconnect Drive"/>
+        <Setter Property="Content" Value="&#xE17A;"/>
+    </Style>
+    <Style x:Key="MapDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MapDriveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Map Drive"/>
+        <Setter Property="Content" Value="&#xE17B;"/>
+    </Style>
+    <Style x:Key="NewWindowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NewWindowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="New Window"/>
+        <Setter Property="Content" Value="&#xE17C;"/>
+    </Style>
+    <Style x:Key="OpenWithAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenWithAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open With"/>
+        <Setter Property="Content" Value="&#xE17D;"/>
+    </Style>
+    <Style x:Key="ContactPresenceAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactPresenceAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Presence"/>
+        <Setter Property="Content" Value="&#xE181;"/>
+    </Style>
+    <Style x:Key="PriorityAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PriorityAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Priority"/>
+        <Setter Property="Content" Value="&#xE182;"/>
+    </Style>
+    <Style x:Key="UploadSkyDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UploadSkyDriveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Skydrive"/>
+        <Setter Property="Content" Value="&#xE183;"/>
+    </Style>
+    <Style x:Key="GoToTodayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GoToTodayAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Today"/>
+        <Setter Property="Content" Value="&#xE184;"/>
+    </Style>
+    <Style x:Key="FontAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Font"/>
+        <Setter Property="Content" Value="&#xE185;"/>
+    </Style>
+
+    -->
+
+    <!--
+
+    <Style x:Key="FontColorAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontColorAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Font Color"/>
+        <Setter Property="Content" Value="&#xE186;"/>
+    </Style>
+    <Style x:Key="Contact2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Contact2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Contact"/>
+        <Setter Property="Content" Value="&#xE187;"/>
+    </Style>
+    <Style x:Key="FolderppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Folder"/>
+        <Setter Property="Content" Value="&#xE188;"/>
+    </Style>
+    <Style x:Key="AudioAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AudioAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Audio"/>
+        <Setter Property="Content" Value="&#xE189;"/>
+    </Style>
+    <Style x:Key="PlaceholderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PlaceholderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Placeholder"/>
+        <Setter Property="Content" Value="&#xE18A;"/>
+    </Style>
+    <Style x:Key="ViewAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ViewAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="View"/>
+        <Setter Property="Content" Value="&#xE18B;"/>
+    </Style>
+    <Style x:Key="SetLockScreenAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SetLockscreenAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Set Lockscreen"/>
+        <Setter Property="Content" Value="&#xE18C;"/>
+    </Style>
+    <Style x:Key="SetTitleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SetTitleAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Set Title"/>
+        <Setter Property="Content" Value="&#xE18D;"/>
+    </Style>
+    <Style x:Key="CcAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CcAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Cc"/>
+        <Setter Property="Content" Value="&#xE190;"/>
+    </Style>
+    <Style x:Key="StopSlideShowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="StopSlideshowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Stop Slideshow"/>
+        <Setter Property="Content" Value="&#xE191;"/>
+    </Style>
+    <Style x:Key="PermissionsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PermissionsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Permisions"/>
+        <Setter Property="Content" Value="&#xE192;"/>
+    </Style>
+    <Style x:Key="HighlightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HighlightAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Highlight"/>
+        <Setter Property="Content" Value="&#xE193;"/>
+    </Style>
+    <Style x:Key="DisableUpdatesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DisableUpdatesAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Disable Updates"/>
+        <Setter Property="Content" Value="&#xE194;"/>
+    </Style>
+    <Style x:Key="UnfavoriteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnfavoriteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Unfavorite"/>
+        <Setter Property="Content" Value="&#xE195;"/>
+    </Style>
+    <Style x:Key="UnPinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnPinAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Unpin"/>
+        <Setter Property="Content" Value="&#xE196;"/>
+    </Style>
+    <Style x:Key="OpenLocalAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenLocalAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open Loal"/>
+        <Setter Property="Content" Value="&#xE197;"/>
+    </Style>
+    <Style x:Key="MuteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MuteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Mute"/>
+        <Setter Property="Content" Value="&#xE198;"/>
+    </Style>
+    <Style x:Key="ItalicAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ItalicAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Italic"/>
+        <Setter Property="Content" Value="&#xE199;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="UnderlineAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnderlineAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Underline"/>
+        <Setter Property="Content" Value="&#xE19A;"/>
+    </Style>
+    <Style x:Key="BoldAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BoldAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Bold"/>
+        <Setter Property="Content" Value="&#xE19B;"/>
+    </Style>
+    <Style x:Key="MoveToFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MoveToFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Move to Folder"/>
+        <Setter Property="Content" Value="&#xE19C;"/>
+    </Style>
+    <Style x:Key="LikeDislikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LikeDislikeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Like/Dislike"/>
+        <Setter Property="Content" Value="&#xE19D;"/>
+    </Style>
+    <Style x:Key="DislikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DislikeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dislike"/>
+        <Setter Property="Content" Value="&#xE19E;"/>
+    </Style>
+    <Style x:Key="LikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LikeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Like"/>
+        <Setter Property="Content" Value="&#xE19F;"/>
+    </Style>
+    <Style x:Key="AlignRightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignRightAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Align Right"/>
+        <Setter Property="Content" Value="&#xE1A0;"/>
+    </Style>
+    <Style x:Key="AlignCenterAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignCenterAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Align Center"/>
+        <Setter Property="Content" Value="&#xE1A1;"/>
+    </Style>
+    <Style x:Key="AlignLeftAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignLeftAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Align Left"/>
+        <Setter Property="Content" Value="&#xE1A2;"/>
+    </Style>
+    <Style x:Key="ZoomAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zoom"/>
+        <Setter Property="Content" Value="&#xE1A3;"/>
+    </Style>
+    <Style x:Key="ZoomOutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomOutAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zoom Out"/>
+        <Setter Property="Content" Value="&#xE1A4;"/>
+    </Style>
+    <Style x:Key="OpenFileAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenFileAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open File"/>
+        <Setter Property="Content" Value="&#xE1A5;"/>
+    </Style>
+    <Style x:Key="OtherUserAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OtherUserAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Other User"/>
+        <Setter Property="Content" Value="&#xE1A6;"/>
+    </Style>
+    <Style x:Key="AdminAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AdminAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Admin"/>
+        <Setter Property="Content" Value="&#xE1A7;"/>
+    </Style>
+    <Style x:Key="StreetAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="StreetAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Street"/>
+        <Setter Property="Content" Value="&#xE1C3;"/>
+    </Style>
+    <Style x:Key="MapAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MapAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Map"/>
+        <Setter Property="Content" Value="&#xE1C4;"/>
+    </Style>
+    <Style x:Key="ClearSelectionAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ClearSelectionAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Clear Selection"/>
+        <Setter Property="Content" Value="&#xE1C5;"/>
+    </Style>
+    <Style x:Key="FontDecreaseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontDecreaseAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Decrease Font"/>
+        <Setter Property="Content" Value="&#xE1C6;"/>
+    </Style>
+    <Style x:Key="FontIncreaseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontIncreaseAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Increase Font"/>
+        <Setter Property="Content" Value="&#xE1C7;"/>
+    </Style>
+    <Style x:Key="FontSizeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontSizeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Font Size"/>
+        <Setter Property="Content" Value="&#xE1C8;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="CellphoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CellphoneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Cellphone"/>
+        <Setter Property="Content" Value="&#xE1C9;"/>
+    </Style>
+    <Style x:Key="ReshareAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ReshareAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Reshare"/>
+        <Setter Property="Content" Value="&#xE1CA;"/>
+    </Style>
+    <Style x:Key="TagAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TagAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Tag"/>
+        <Setter Property="Content" Value="&#xE1CB;"/>
+    </Style>
+    <Style x:Key="RepeatOneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RepeatOneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Repeat Once"/>
+        <Setter Property="Content" Value="&#xE1CC;"/>
+    </Style>
+    <Style x:Key="RepeatAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RepeatAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Repeat All"/>
+        <Setter Property="Content" Value="&#xE1CD;"/>
+    </Style>
+    <Style x:Key="OutlineStarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OutlineStarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Outline Star"/>
+        <Setter Property="Content" Value="&#xE1CE;"/>
+    </Style>
+    <Style x:Key="SolidStarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SolidStarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Solid Star"/>
+        <Setter Property="Content" Value="&#xE1CF;"/>
+    </Style>
+    <Style x:Key="CalculatorAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalculatorAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Calculator"/>
+        <Setter Property="Content" Value="&#xE1D0;"/>
+    </Style>
+    <Style x:Key="DirectionsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DirectionsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Directions"/>
+        <Setter Property="Content" Value="&#xE1D1;"/>
+    </Style>
+    <Style x:Key="TargetAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TargetAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Target"/>
+        <Setter Property="Content" Value="&#xE1D2;"/>
+    </Style>
+    <Style x:Key="LibraryAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LibraryAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Library"/>
+        <Setter Property="Content" Value="&#xE1D3;"/>
+    </Style>
+    <Style x:Key="PhonebookAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PhonebookAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Phonebook"/>
+        <Setter Property="Content" Value="&#xE1D4;"/>
+    </Style>
+    <Style x:Key="MemoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MemoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Memo"/>
+        <Setter Property="Content" Value="&#xE1D5;"/>
+    </Style>
+    <Style x:Key="MicrophoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MicrophoneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Microphone"/>
+        <Setter Property="Content" Value="&#xE1D6;"/>
+    </Style>
+    <Style x:Key="PostUpdateAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PostUpdateAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Post Update"/>
+        <Setter Property="Content" Value="&#xE1D7;"/>
+    </Style>
+    <Style x:Key="BackToWindowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BackToWindowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Back to Window"/>
+        <Setter Property="Content" Value="&#xE1D8;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="FullScreenAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FullScreenAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Full Screen"/>
+        <Setter Property="Content" Value="&#xE1D9;"/>
+    </Style>
+    <Style x:Key="NewFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NewFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="New Folder"/>
+        <Setter Property="Content" Value="&#xE1DA;"/>
+    </Style>
+    <Style x:Key="CalendarReplyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarReplyAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Calendar Reply"/>
+        <Setter Property="Content" Value="&#xE1DB;"/>
+    </Style>
+    <Style x:Key="UnsyncFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnsyncFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Unsync Folder"/>
+        <Setter Property="Content" Value="&#xE1DD;"/>
+    </Style>
+    <Style x:Key="ReportHackedAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ReportHackedAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Report Hacked"/>
+        <Setter Property="Content" Value="&#xE1DE;"/>
+    </Style>
+    <Style x:Key="SyncFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SyncFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Sync Folder"/>
+        <Setter Property="Content" Value="&#xE1DF;"/>
+    </Style>
+    <Style x:Key="BlockContactAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Block ContactAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="BlockContact"/>
+        <Setter Property="Content" Value="&#xE1E0;"/>
+    </Style>
+    <Style x:Key="SwitchAppsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SwitchAppsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Switch Apps"/>
+        <Setter Property="Content" Value="&#xE1E1;"/>
+    </Style>
+    <Style x:Key="AddFriendAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AddFriendAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Add Friend"/>
+        <Setter Property="Content" Value="&#xE1E2;"/>
+    </Style>
+    <Style x:Key="TouchPointerAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TouchPointerAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Touch Pointer"/>
+        <Setter Property="Content" Value="&#xE1E3;"/>
+    </Style>
+    <Style x:Key="GoToStartAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GoToStartAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Go to Start"/>
+        <Setter Property="Content" Value="&#xE1E4;"/>
+    </Style>
+    <Style x:Key="ZeroBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZeroBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zero Bars"/>
+        <Setter Property="Content" Value="&#xE1E5;"/>
+    </Style>
+    <Style x:Key="OneBarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OneBarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="One Bar"/>
+        <Setter Property="Content" Value="&#xE1E6;"/>
+    </Style>
+    <Style x:Key="TwoBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TwoBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Two Bars"/>
+        <Setter Property="Content" Value="&#xE1E7;"/>
+    </Style>
+    <Style x:Key="ThreeBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ThreeBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Three Bars"/>
+        <Setter Property="Content" Value="&#xE1E8;"/>
+    </Style>
+    <Style x:Key="FourBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FourBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Four Bars"/>
+        <Setter Property="Content" Value="&#xE1E9;"/>
+    </Style>
+
+    -->
+
+    <!-- Title area styles -->
+
+    <Style x:Key="PageHeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource HeaderTextStyle}">
+        <Setter Property="TextWrapping" Value="NoWrap"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="Margin" Value="0,0,30,40"/>
+    </Style>
+
+    <Style x:Key="PageSubheaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource SubheaderTextStyle}">
+        <Setter Property="TextWrapping" Value="NoWrap"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="Margin" Value="0,0,0,40"/>
+    </Style>
+
+    <Style x:Key="SnappedPageHeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource PageSubheaderTextStyle}">
+        <Setter Property="Margin" Value="0,0,18,40"/>
+    </Style>
+
+    <!--
+        BackButtonStyle is used to style a Button for use in the title area of a page.  Margins appropriate for
+        the conventional page layout are included as part of the style.
+    -->
+    <Style x:Key="BackButtonStyle" TargetType="Button">
+        <Setter Property="MinWidth" Value="0"/>
+        <Setter Property="Width" Value="48"/>
+        <Setter Property="Height" Value="48"/>
+        <Setter Property="Margin" Value="36,0,36,36"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
+        <Setter Property="FontWeight" Value="Normal"/>
+        <Setter Property="FontSize" Value="56"/>
+        <Setter Property="AutomationProperties.AutomationId" Value="BackButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Back"/>
+        <Setter Property="AutomationProperties.ItemType" Value="Navigation Button"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="Button">
+                    <Grid x:Name="RootGrid">
+                        <Grid Margin="-1,-16,0,0">
+                            <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" Foreground="{StaticResource BackButtonBackgroundThemeBrush}"/>
+                            <TextBlock x:Name="NormalGlyph" Text="{StaticResource BackButtonGlyph}" Foreground="{StaticResource BackButtonForegroundThemeBrush}"/>
+                            <TextBlock x:Name="ArrowGlyph" Text="&#xE0A6;" Foreground="{StaticResource BackButtonPressedForegroundThemeBrush}" Opacity="0"/>
+                        </Grid>
+                        <Rectangle
+                            x:Name="FocusVisualWhite"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="1.5"/>
+                        <Rectangle
+                            x:Name="FocusVisualBlack"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="0.5"/>
+
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal" />
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverBackgroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="ArrowGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="NormalGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="0"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualWhite"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualBlack"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused" />
+                                <VisualState x:Name="PointerFocused" />
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!--
+        PortraitBackButtonStyle is used to style a Button for use in the title area of a portrait page.  Margins appropriate
+        for the conventional page layout are included as part of the style.
+    -->
+    <Style x:Key="PortraitBackButtonStyle" TargetType="Button" BasedOn="{StaticResource BackButtonStyle}">
+        <Setter Property="Margin" Value="26,0,26,36"/>
+    </Style>
+
+    <!--
+        SnappedBackButtonStyle is used to style a Button for use in the title area of a snapped page.  Margins appropriate
+        for the conventional page layout are included as part of the style.
+        
+        The obvious duplication here is necessary as the glyphs used in snapped are not merely smaller versions of the same
+        glyph but are actually distinct.
+    -->
+    <Style x:Key="SnappedBackButtonStyle" TargetType="Button">
+        <Setter Property="MinWidth" Value="0"/>
+        <Setter Property="Margin" Value="20,0,0,0"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
+        <Setter Property="FontWeight" Value="Normal"/>
+        <Setter Property="FontSize" Value="26.66667"/>
+        <Setter Property="AutomationProperties.AutomationId" Value="BackButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Back"/>
+        <Setter Property="AutomationProperties.ItemType" Value="Navigation Button"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="Button">
+                    <Grid x:Name="RootGrid" Width="36" Height="36" Margin="-3,0,7,33">
+                        <Grid Margin="-1,-1,0,0">
+                            <TextBlock x:Name="BackgroundGlyph" Text="&#xE0D4;" Foreground="{StaticResource BackButtonBackgroundThemeBrush}"/>
+                            <TextBlock x:Name="NormalGlyph" Text="{StaticResource BackButtonSnappedGlyph}" Foreground="{StaticResource BackButtonForegroundThemeBrush}"/>
+                            <TextBlock x:Name="ArrowGlyph" Text="&#xE0C4;" Foreground="{StaticResource BackButtonPressedForegroundThemeBrush}" Opacity="0"/>
+                        </Grid>
+                        <Rectangle
+                            x:Name="FocusVisualWhite"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="1.5"/>
+                        <Rectangle
+                            x:Name="FocusVisualBlack"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="0.5"/>
+
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal" />
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverBackgroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="ArrowGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="NormalGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="0"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualWhite"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualBlack"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused" />
+                                <VisualState x:Name="PointerFocused" />
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!-- Item templates -->
+
+    <!-- Grid-appropriate 250 pixel square item template as seen in the GroupedItemsPage and ItemsPage -->
+    <DataTemplate x:Key="Standard250x250ItemTemplate">
+        <Grid HorizontalAlignment="Left" Width="250" Height="250">
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
+            </Border>
+            <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
+                <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
+                <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- Grid-appropriate 500 by 130 pixel item template as seen in the GroupDetailPage -->
+    <DataTemplate x:Key="Standard500x130ItemTemplate">
+        <Grid Height="110" Width="480" Margin="10">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
+            </Border>
+            <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- List-appropriate 130 pixel high item template as seen in the SplitPage -->
+    <DataTemplate x:Key="Standard130ItemTemplate">
+        <Grid Height="110" Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
+            </Border>
+            <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!--
+        List-appropriate 80 pixel high item template as seen in the SplitPage when Filled, and
+        the following pages when snapped: GroupedItemsPage, GroupDetailPage, and ItemsPage
+    -->
+    <DataTemplate x:Key="Standard80ItemTemplate">
+        <Grid Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="60" Height="60">
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>
+            </Border>
+            <StackPanel Grid.Column="1" Margin="10,0,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource ItemTextStyle}" MaxHeight="40"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- Grid-appropriate 300 by 70 pixel item template as seen in the SearchResultsPage -->
+    <DataTemplate x:Key="StandardSmallIcon300x70ItemTemplate">
+        <Grid Width="294" Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,0,0,10" Width="40" Height="40">
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>
+            </Border>
+            <StackPanel Grid.Column="1" Margin="10,-10,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource BodyTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- List-appropriate 70 pixel high item template as seen in the SearchResultsPage when Snapped -->
+    <DataTemplate x:Key="StandardSmallIcon70ItemTemplate">
+        <Grid Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,0,0,10" Width="40" Height="40">
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>
+            </Border>
+            <StackPanel Grid.Column="1" Margin="10,-10,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource BodyTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!--
+      190x130 pixel item template for displaying file previews as seen in the FileOpenPickerPage
+      Includes an elaborate tooltip to display title and description text
+  -->
+    <DataTemplate x:Key="StandardFileWithTooltip190x130ItemTemplate">
+        <Grid>
+            <Grid Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
+                <Image
+                    Source="{Binding Image}"
+                    Width="190"
+                    Height="130"
+                    HorizontalAlignment="Center"
+                    VerticalAlignment="Center"
+                    Stretch="Uniform"/>
+            </Grid>
+            <ToolTipService.Placement>Mouse</ToolTipService.Placement>
+            <ToolTipService.ToolTip>
+                <ToolTip>
+                    <ToolTip.Style>
+                        <Style TargetType="ToolTip">
+                            <Setter Property="BorderBrush" Value="{StaticResource ToolTipBackgroundThemeBrush}" />
+                            <Setter Property="Padding" Value="0" />
+                        </Style>
+                    </ToolTip.Style>
+
+                    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="Auto"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+
+                        <Grid Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="20">
+                            <Image
+                                Source="{Binding Image}"
+                                Width="160"
+                                Height="160"
+                                HorizontalAlignment="Center"
+                                VerticalAlignment="Center"
+                                Stretch="Uniform"/>
+                        </Grid>
+                        <StackPanel Width="200" Grid.Column="1" Margin="0,20,20,20">
+                            <TextBlock Text="{Binding Title}" TextWrapping="NoWrap" Style="{StaticResource BodyTextStyle}"/>
+                            <TextBlock Text="{Binding Description}" MaxHeight="140" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" Style="{StaticResource BodyTextStyle}"/>
+                        </StackPanel>
+                    </Grid>
+                </ToolTip>
+            </ToolTipService.ToolTip>
+        </Grid>
+    </DataTemplate>
+
+    <!-- ScrollViewer styles -->
+
+    <Style x:Key="HorizontalScrollViewerStyle" TargetType="ScrollViewer">
+        <Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
+        <Setter Property="VerticalScrollBarVisibility" Value="Disabled"/>
+        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Enabled" />
+        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" />
+        <Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
+    </Style>
+
+    <Style x:Key="VerticalScrollViewerStyle" TargetType="ScrollViewer">
+        <Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>
+        <Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
+        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
+        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Enabled" />
+        <Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
+    </Style>
+
+    <!-- Page layout roots typically use entrance animations and a theme-appropriate background color -->
+
+    <Style x:Key="LayoutRootStyle" TargetType="Panel">
+        <Setter Property="Background" Value="{StaticResource ApplicationPageBackgroundThemeBrush}"/>
+        <Setter Property="ChildrenTransitions">
+            <Setter.Value>
+                <TransitionCollection>
+                    <EntranceThemeTransition/>
+                </TransitionCollection>
+            </Setter.Value>
+        </Setter>
+    </Style>
+</ResourceDictionary>
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Package.appxmanifest b/Release/samples/BlackJack/BlackJack_UIClient/Package.appxmanifest
new file mode 100644 (file)
index 0000000..f56191f
--- /dev/null
@@ -0,0 +1,31 @@
+ļ»æ<?xml version="1.0" encoding="utf-8"?>
+<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
+  <Identity Name="64521d51-da30-445f-ba7b-d79f27cf8c7c" Publisher="CN=casaserv" Version="1.0.0.0" />
+  <Properties>
+    <DisplayName>Blackjack Client</DisplayName>
+    <PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
+    <Logo>Assets\StoreLogo.png</Logo>
+  </Properties>
+  <Prerequisites>
+    <OSMinVersion>6.2.1</OSMinVersion>
+    <OSMaxVersionTested>6.2.1</OSMaxVersionTested>
+  </Prerequisites>
+  <Resources>
+    <Resource Language="x-generate" />
+  </Resources>
+  <Applications>
+    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="Blackjack_Client.App">
+      <m2:VisualElements DisplayName="Blackjack Client" Description="Blackjack Client" BackgroundColor="#464646" ForegroundText="light" Square150x150Logo="Assets\Logo.png" Square30x30Logo="Assets\SmallLogo.png">
+        <m2:DefaultTile>
+          <m2:ShowNameOnTiles>
+            <m2:ShowOn Tile="square150x150Logo" />
+          </m2:ShowNameOnTiles>
+        </m2:DefaultTile>
+        <m2:SplashScreen BackgroundColor="#008024" Image="Assets\SplashScreen.png" />
+      </m2:VisualElements>
+    </Application>
+  </Applications>
+  <Capabilities>
+    <Capability Name="internetClient" />
+  </Capabilities>
+</Package>
\ No newline at end of file
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Package.uwp.appxmanifest b/Release/samples/BlackJack/BlackJack_UIClient/Package.uwp.appxmanifest
new file mode 100644 (file)
index 0000000..0ed1165
--- /dev/null
@@ -0,0 +1,34 @@
+ļ»æ<?xml version="1.0" encoding="utf-8"?>
+<Package
+  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
+  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
+  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
+  IgnorableNamespaces="uap mp">
+  <Identity Name="64521d51-da30-445f-ba7b-d79f27cf8c7c" Publisher="CN=casaserv" Version="1.0.0.0" />
+  <Properties>
+    <DisplayName>Blackjack Client</DisplayName>
+    <PublisherDisplayName>Andy</PublisherDisplayName>
+    <Logo>Assets\StoreLogo.png</Logo>
+  </Properties>
+
+  <mp:PhoneIdentity PhoneProductId="51cd24bd-4a31-4002-a587-b5ca8c380f24" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
+
+  <Dependencies>
+    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
+  </Dependencies>
+
+  <Resources>
+    <Resource Language="x-generate" />
+  </Resources>
+  <Applications>
+    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="Blackjack_Client.App">
+      <uap:VisualElements DisplayName="Blackjack Client" Description="Blackjack Client" BackgroundColor="#464646" Square150x150Logo="Assets\Logo.png" Square44x44Logo="Assets\SmallLogo.png">
+        <uap:SplashScreen Image="Assets\SplashScreen.png" />
+      </uap:VisualElements>
+    </Application>
+  </Applications>
+
+  <Capabilities>
+    <Capability Name="internetClient" />
+  </Capabilities>
+</Package>
\ No newline at end of file
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml b/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml
new file mode 100644 (file)
index 0000000..34d52bf
--- /dev/null
@@ -0,0 +1,34 @@
+ļ»æ<UserControl 
+    x:Class="BlackjackClient.PlayerSpace"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:BlackjackClient"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d"
+    d:DesignHeight="300"
+    d:DesignWidth="250">
+
+    <UserControl.Resources>
+        <Style x:Key="LabelStyle" TargetType="TextBlock">
+            <Setter Property="FontFamily" Value="Comic Sans MS"/>
+            <Setter Property="FontSize" Value="20"/>
+            <Setter Property="FontWeight" Value="Bold"/>
+            <Setter Property="Foreground" Value="Yellow"/>
+            <Setter Property="Height" Value="31"/>
+        </Style>
+    </UserControl.Resources>
+
+    <Grid x:Name="LayoutRoot" Background="#008024" Margin="0,0,0,0">
+        <Border BorderThickness="5" BorderBrush="Yellow" HorizontalAlignment="Center" Height="210" VerticalAlignment="Top" Width="150"/>
+        <Grid Name="playerCardGrid" Height="250" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,0" Width="240" Grid.ColumnSpan="2"></Grid>
+        <StackPanel Margin="0,0,0,0" Orientation="Vertical" d:LayoutOverrides="Width" VerticalAlignment="Bottom">
+            <TextBlock Text="Your Name" Style="{StaticResource LabelStyle}" HorizontalAlignment="Center" x:Name="playerName" Margin="90,0,93,0" Width="67" Visibility="Visible"/>
+            <StackPanel Orientation="Horizontal" d:LayoutOverrides="Width" VerticalAlignment="Bottom" HorizontalAlignment="Center">
+                <TextBlock Text="$100" Style="{StaticResource LabelStyle}" FontSize="16" HorizontalAlignment="Left" x:Name="playerBalance"  Margin="0,0,0,0" VerticalAlignment="Top" Visibility="Visible"/>
+                <TextBlock Text="$100" Style="{StaticResource LabelStyle}" FontSize="16" HorizontalAlignment="Center" x:Name="playerBet"  Margin="10,0,0,0" VerticalAlignment="Top" Visibility="Visible"/>
+                <TextBlock Text="$250" Style="{StaticResource LabelStyle}" FontSize="16" HorizontalAlignment="Right" x:Name="playerInsurance"  Margin="10,0,0,0" VerticalAlignment="Top" Visibility="Visible"/>
+            </StackPanel>
+        </StackPanel>
+    </Grid>
+</UserControl>
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.cpp b/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.cpp
new file mode 100644 (file)
index 0000000..394165a
--- /dev/null
@@ -0,0 +1,113 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Player.xaml.cpp: Implementation of the Player class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "pch.h"
+
+#include "Player.xaml.h"
+
+#include "CardShape.xaml.h"
+#include "cpprest/uri.h"
+
+using namespace BlackjackClient;
+
+using namespace Platform;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
+
+void PlayerSpace::_init()
+{
+    Thickness margin;
+    margin.Left = 10;
+    margin.Top = 0;
+    margin.Bottom = 0;
+    margin.Right = 0;
+
+    this->Margin = margin;
+    this->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
+    this->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
+    this->Width = 250;
+    this->Height = 300;
+
+    InitializeComponent();
+    Clear();
+}
+
+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();
+    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)
+{
+    Thickness margin;
+    margin.Left = 5 + m_cards.size() * (CardWidthRect * SCALE_FACTOR * .25 + 0);
+    margin.Top = 12;
+    margin.Bottom = 0;
+    margin.Right = 0;
+
+    auto crd = ref new CardShape();
+    crd->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
+    crd->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
+    crd->Margin = margin;
+    crd->_suit = (int)card.suit;
+    crd->_value = (int)card.value;
+    crd->_visible = (Platform::Boolean)card.visible;
+
+    crd->adjust();
+
+    m_cards.push_back(card);
+    playerCardGrid->Children->Append(crd);
+
+    UpdateLayout();
+}
+
+void PlayerSpace::ShowResult(BJHandResult result)
+{
+    switch (result)
+    {
+        case BJHandResult::HR_ComputerWin:
+            playerInsurance->Text = L"Dealer Wins";
+            playerBet->Text.clear();
+            break;
+        case BJHandResult::HR_PlayerWin:
+            playerInsurance->Text = L"Player Wins";
+            playerBet->Text.clear();
+            break;
+        case BJHandResult::HR_Push:
+            playerInsurance->Text = L"Push";
+            playerBet->Text.clear();
+            break;
+        case BJHandResult::HR_PlayerBlackJack:
+            playerInsurance->Text = L"Blackjack!";
+            playerBet->Text.clear();
+            break;
+        default: break;
+    }
+    UpdateLayout();
+}
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.h b/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.h
new file mode 100644 (file)
index 0000000..5b7ee74
--- /dev/null
@@ -0,0 +1,50 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Player.xaml.h:  Declaration of the Player class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "pch.h"
+
+#include "Player.g.h"
+
+namespace BlackjackClient
+{
+public
+ref class PlayerSpace sealed
+{
+public:
+    PlayerSpace() { _init(); }
+
+    void Clear()
+    {
+        playerBalance->Text.clear();
+        playerBet->Text.clear();
+        playerName->Text.clear();
+        playerInsurance->Text.clear();
+        m_cards.clear();
+        playerCardGrid->Children->Clear();
+    }
+
+    int CardsHeld() { return int(m_cards.size()); }
+
+private:
+    friend ref class PlayingTable;
+
+    void AddCard(Card card);
+    void Update(Player player);
+
+    void ShowResult(BJHandResult result);
+
+    void _init();
+
+    std::vector<Card> m_cards;
+};
+} // namespace BlackjackClient
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml b/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml
new file mode 100644 (file)
index 0000000..239cff1
--- /dev/null
@@ -0,0 +1,106 @@
+ļ»æ<Page
+    x:Class="BlackjackClient.PlayingTable"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:BlackjackClient"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d">
+
+    <Page.Resources>
+        <Style x:Key="AppBarStyle" TargetType="AppBar">
+            <Setter Property="Background" Value="Black"/>
+            <Setter Property="Padding" Value="40,0,40,0"/>
+        </Style>
+        <Style x:Key="ButtonStyle" TargetType="Button">
+            <Setter Property="FontFamily" Value="Comic Sans MS"/>
+            <Setter Property="BorderThickness" Value="0"/>
+            <Setter Property="Foreground" Value="Yellow"/>
+            <Setter Property="Height" Value="41"/>
+            <Setter Property="Width" Value="85"/>
+        </Style>
+        <Style x:Key="LabelStyle" TargetType="TextBlock">
+            <Setter Property="FontFamily" Value="Comic Sans MS"/>
+            <Setter Property="FontSize" Value="20"/>
+            <Setter Property="FontWeight" Value="Bold"/>
+            <Setter Property="Foreground" Value="Yellow"/>
+            <Setter Property="Height" Value="31"/>
+        </Style>
+    </Page.Resources>
+
+    <Grid  x:Name="LayoutRoot" Background="#008024" >
+
+        <StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="40,40,0,0">
+            <TextBlock Text="Hit" HorizontalAlignment="Left" Margin="0,0,0,0" x:Name="hitButton" VerticalAlignment="Top" Style="{StaticResource LabelStyle}" Tapped="HitButton_Click" Visibility="Visible"/>
+            <TextBlock Text="Stand" HorizontalAlignment="Left" Margin="0,30,0,0" x:Name="stayButton" VerticalAlignment="Top" Style="{StaticResource LabelStyle}" Tapped="StayButton_Click" Visibility="Visible"/>
+            <TextBlock Text="Double" HorizontalAlignment="Left" Margin="0,30,0,0" x:Name="doubleButton" VerticalAlignment="Top" Style="{StaticResource LabelStyle}" Tapped="DoubleButton_Click" Visibility="Visible"/>
+            <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="0,30,0,0">
+                <TextBlock Text="Buy Insurance" Height="40" HorizontalAlignment="Left" Margin="0,0,0,0" x:Name="buyInsuranceButton" VerticalAlignment="Top" Width="154" Style="{StaticResource LabelStyle}"  Tapped="InsuranceButton_Click" Visibility="Visible"/>
+                <TextBlock Text="Amount:" Style="{StaticResource LabelStyle}" HorizontalAlignment="Left" Margin="0,0,15,0" x:Name="buyInsuranceLabel" VerticalAlignment="Top" Width="84"  Visibility="Visible"/>
+                <TextBox AcceptsReturn="False" FontSize="14" Height="23" HorizontalAlignment="Left" Margin="0,0,0,0" x:Name="buyInsuranceTextBox" Text="100" VerticalAlignment="Top" Width="55" Visibility="Visible"/>
+            </StackPanel>
+            <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="0,0,0,0">
+                <TextBlock Text="Bet" Style="{StaticResource LabelStyle}" FontSize="20" HorizontalAlignment="Left" Margin="0,0,0,0" x:Name="label1" VerticalAlignment="Top" Width="39" />
+                <TextBox Text="100" FontSize="18" Height="25" HorizontalAlignment="Left" Margin="37,0,0,0" x:Name="betBox" VerticalAlignment="Top" Width="51"  AcceptsReturn="False" IsEnabled="False"/>
+            </StackPanel>
+            <TextBlock Text="Deal" FontSize="30" Margin="0,30,0,0" Height="40" HorizontalAlignment="Left" x:Name="button1" VerticalAlignment="Top" Style="{StaticResource LabelStyle}" Tapped="BetButton_Click" />
+        </StackPanel>
+
+        <!-- Dealer controls, displaying the dealer's cards-->
+
+        <Grid Name="dealerGrid" Height="150" HorizontalAlignment="Center" VerticalAlignment="Top" Width="242" Grid.ColumnSpan="2"></Grid>
+        <TextBlock Text="" Style="{StaticResource LabelStyle}" FontSize="28" Height="61" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="40,88,0,619" Name="resultLabel" Width="428"/>
+        <StackPanel Margin="0,256,0,0"></StackPanel>
+
+        <!-- Player controls, duplicated for each player at the table -->
+
+        <StackPanel Margin="0,256,0,0" VerticalAlignment="Bottom" >
+            <TextBlock Text="BLACKJACK PAYS 3 TO 2" Style="{StaticResource LabelStyle}" FontSize="60" FontFamily="Times New Roman" Height="61" HorizontalAlignment="Center" Width="744"/>
+            <TextBlock Text="Dealer must stand on 17 and draw to 16" FontFamily="Segoe UI Light" Style="{StaticResource LabelStyle}" FontSize="32" Height="61" HorizontalAlignment="Center" Margin="0,20,0,0" Width="650"/>
+            <Border BorderBrush="Yellow" BorderThickness="4" HorizontalAlignment="Center" Height="72" Margin="0,0,0,0" VerticalAlignment="Top" Width="1280">
+                <TextBlock FontSize="50" FontFamily="Times New Roman" Foreground="Yellow" Height="61" HorizontalAlignment="Center" TextWrapping="Wrap" Text="INSURANCE PAYS 2 TO 1" VerticalAlignment="Top"/>
+            </Border>
+            <Grid HorizontalAlignment="Stretch" Name="playerGrid" VerticalAlignment="Bottom" d:LayoutOverrides="GridBox" Margin="0,40,0,0" >
+                <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Width="1280" Margin="0,0,0,0" VerticalAlignment="Bottom" Name="playerPanel">
+                </StackPanel>
+            </Grid>
+        </StackPanel>
+        <!-- Betting controls, should display under current player -->
+
+
+        <!-- Insurance controls, used when dealer has an Ace -->
+
+
+        <!-- Application bars, used for game control -->
+
+        <AppBar Name="TopBar" Height="88" VerticalAlignment="Top" Opacity=".75" Style="{StaticResource AppBarStyle}">
+            <Grid HorizontalAlignment="Stretch">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="*"/>
+                    <ColumnDefinition Width="Auto"/>
+                </Grid.ColumnDefinitions>
+                <StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center" >
+                    <TextBlock Text="Your name" Style="{StaticResource LabelStyle}" FontSize="18" Foreground="White"/>
+                    <TextBox Text="" x:Name="playerName" AcceptsReturn="False"   FontSize="14" FontWeight="Bold" Foreground="Black" Height="28" Width="200" Margin="5,15,0,15"/>
+                </StackPanel>
+                <StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" >
+                    <TextBlock Text="Server address" Style="{StaticResource LabelStyle}" FontSize="18" Foreground="White" />
+                    <TextBox Text="http://localhost:2121/blackjack" x:Name="serverAddress" AcceptsReturn="False"   FontSize="14" FontWeight="Bold" Foreground="Black" Height="28" Width="450" Margin="5,15,0,15"/>
+                </StackPanel>
+            </Grid>
+        </AppBar>
+        <AppBar Name="BottomBar" Height="88" VerticalAlignment="Bottom" Opacity=".75" Style="{StaticResource AppBarStyle}" Grid.ColumnSpan="2">
+            <Grid HorizontalAlignment="Stretch">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="*"/>
+                    <ColumnDefinition Width="Auto"/>
+                </Grid.ColumnDefinitions>
+                <StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" >
+                    <Button Content="&#xE0F8;" AutomationProperties.Name="Exit Game" Name="exitButton" Click="ExitButton_Click" Style="{StaticResource AppBarButtonStyle}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
+                    <Button Content="&#xE125;" AutomationProperties.Name="Join Table" Name="joinButton" Click="JoinButton_Click" Style="{StaticResource AppBarButtonStyle}" VerticalAlignment="Center"/>
+                    <Button Content="&#xE106;" AutomationProperties.Name="Leave Table" Name="leaveButton" Click="LeaveButton_Click" Style="{StaticResource AppBarButtonStyle}" VerticalAlignment="Center"/>
+                </StackPanel>
+            </Grid>
+        </AppBar>
+    </Grid>
+</Page>
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.cpp b/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.cpp
new file mode 100644 (file)
index 0000000..5f835e1
--- /dev/null
@@ -0,0 +1,566 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Table.xaml.cpp: Implementation of the Table.xaml class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "pch.h"
+
+#include "PlayingTable.xaml.h"
+
+#include "cpprest/asyncrt_utils.h"
+
+using namespace BlackjackClient;
+
+using namespace Platform;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+using namespace Windows::Networking;
+
+#include "CardShape.xaml.h"
+#include "cpprest/json.h"
+#include <string>
+
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Data;
+using namespace BlackjackClient;
+
+PlayingTable::PlayingTable()
+    : m_dealerResource(L"dealer"), m_alreadyInsured(false), m_client(L"http://localhost:34568/blackjack/")
+{
+    InitializeComponent();
+
+    this->serverAddress->Text = ref new Platform::String(L"http://localhost:34568/blackjack/");
+
+    this->playerName->Text = L"Player";
+
+    size_t players = 5;
+
+    for (size_t i = 0; i < players; i++)
+    {
+        auto player = ref new PlayerSpace();
+        playerPanel->Children->Append(player);
+        _playerSpaces.push_back(player);
+    }
+
+    EnableExit();
+    EnableConnecting();
+
+    DisableDisconnecting();
+    DisableHit();
+    DisableBetting();
+    DisableInsurance();
+}
+
+PlayingTable::~PlayingTable() {}
+
+void BlackjackClient::PlayingTable::AddDealerCard(Card card)
+{
+    Thickness margin;
+    margin.Left = 5 + _dealerCards.size() * (CardWidthRect * SCALE_FACTOR * .25 + 0);
+    margin.Top = 12;
+    margin.Bottom = 0;
+    margin.Right = 0;
+
+    auto crd = ref new CardShape();
+    crd->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
+    crd->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
+    crd->Margin = margin;
+    crd->_suit = (int)card.suit;
+    crd->_value = (int)card.value;
+    crd->_visible = (Platform::Boolean)card.visible;
+
+    crd->adjust();
+
+    _dealerCards.push_back(card);
+    dealerGrid->Children->Append(crd);
+}
+
+bool BlackjackClient::PlayingTable::InterpretError(HRESULT hr)
+{
+    if (hr == S_OK) return false;
+
+    switch (hr)
+    {
+        case 0x800C0005: this->resultLabel->Text = L"Error communicating with server"; break;
+        default: this->resultLabel->Text = L"Internal error"; break;
+    }
+
+    return true;
+}
+
+void BlackjackClient::PlayingTable::Refresh()
+{
+    this->resultLabel->Text = L"Waiting for other players";
+
+    std::wostringstream buf;
+    buf << _tableId << L"?request=refresh&name=" << uri::encode_uri(m_name, uri::components::path);
+
+    auto request = m_client.request(methods::PUT, buf.str());
+
+    auto ctx = pplx::task_continuation_context::use_current();
+
+    request.then(
+        [this](pplx::task<http_response> tsk) {
+            this->resultLabel->Text.clear();
+
+            try
+            {
+                auto response = tsk.get();
+                InterpretResponse(response);
+            }
+            catch (const http_exception& exc)
+            {
+                InterpretError(exc.error_code().value());
+                this->resultLabel->Text = L"Waiting for other players";
+                Refresh();
+            }
+        },
+        ctx);
+}
+
+void BlackjackClient::PlayingTable::InterpretResponse(http_response& response)
+{
+    if (response.headers().content_type() != L"application/json") return;
+
+    this->resultLabel->Text.clear();
+
+    response.extract_json().then(
+        [this, response](json::value jsonResponse) {
+            BlackjackClient::PlayingTable ^ _this = this;
+            http_response r = response;
+            _this->InterpretResponse(jsonResponse);
+        },
+        pplx::task_continuation_context::use_current());
+}
+
+void BlackjackClient::PlayingTable::InterpretResponse(json::value jsonResponse)
+{
+    BJPutResponse answer = BJPutResponse::FromJSON(jsonResponse);
+    json::value players = answer.Data[PLAYERS];
+
+    bool allow_double = false;
+    bool allow_insurance = false;
+
+    size_t i = 0;
+
+    for (auto iter = players.as_array().begin(); iter != players.as_array().end() && i < _playerSpaces.size();
+         ++iter, i++)
+    {
+        Player player = Player::FromJSON(*iter);
+
+        if (player.Name == DEALER)
+        {
+            if (player.Hand.cards.size() == 1 && _dealerCards.size() == 2)
+            {
+                // The cards are already shown.
+                continue;
+            }
+
+            ClearDealerCards();
+
+            for (size_t j = 0; j < player.Hand.cards.size(); j++)
+            {
+                AddDealerCard(player.Hand.cards[j]);
+            }
+
+            if (player.Hand.cards.size() == 1)
+            {
+                // Add a dummy card that isn't shown.
+                AddDealerCard(Card(CardSuit::CS_Club, CardValue::CV_Ace, false));
+                if (player.Hand.cards[0].value == CardValue::CV_Ace && !m_alreadyInsured) allow_insurance = true;
+            }
+            continue;
+        }
+
+        if (uri::decode(player.Name) == m_name)
+        {
+            allow_double = (player.Hand.cards.size() == 2);
+        }
+
+        _playerSpaces[i - 1]->Update(player);
+
+        for (size_t j = _playerSpaces[i - 1]->CardsHeld(); j < player.Hand.cards.size(); j++)
+        {
+            _playerSpaces[i - 1]->AddCard(player.Hand.cards[j]);
+        }
+
+        if (player.Hand.result != BJHandResult::HR_None)
+        {
+            _playerSpaces[i - 1]->ShowResult(player.Hand.result);
+        }
+    }
+
+    switch (answer.Status)
+    {
+        case ST_PlaceBet:
+            this->resultLabel->Text = L"Place your bet!";
+            EnableBetting();
+            DisableHit();
+            DisableInsurance();
+            EnableExit();
+            EnableDisconnecting();
+            break;
+        case ST_YourTurn:
+            this->resultLabel->Text = L"Your turn!";
+            DisableExit();
+            DisableDisconnecting();
+            DisableBetting();
+            EnableHit();
+            doubleButton->Visibility =
+                allow_double ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed;
+            if (allow_insurance)
+                EnableInsurance();
+            else
+                DisableInsurance();
+            break;
+        case ST_Refresh:
+            this->resultLabel->Text = L"Waiting for other players";
+            DisableExit();
+            DisableDisconnecting();
+            DisableHit();
+            DisableBetting();
+            Refresh();
+            break;
+    }
+}
+
+void BlackjackClient::PlayingTable::BetButton_Click(Platform::Object ^ sender,
+                                                    Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e)
+{
+    if (_tableId.empty() || m_name.empty()) return;
+
+    ClearPlayerCards();
+    ClearDealerCards();
+
+    DisableExit();
+    DisableDisconnecting();
+    DisableBetting();
+
+    std::wstring betText(betBox->Text->Data());
+
+    this->resultLabel->Text = L"Waiting for a response";
+
+    std::wostringstream buf;
+    buf << _tableId << L"?request=bet&amount=" << betText
+        << "&name=" << uri::encode_uri(m_name, uri::components::query);
+
+    auto request = m_client.request(methods::PUT, buf.str());
+
+    auto ctx = pplx::task_continuation_context::use_current();
+
+    request.then(
+        [this](pplx::task<http_response> tsk) {
+            this->resultLabel->Text.clear();
+
+            try
+            {
+                auto response = tsk.get();
+                InterpretResponse(response);
+            }
+            catch (const http_exception& exc)
+            {
+                InterpretError(exc.error_code().value());
+                EnableExit();
+                EnableDisconnecting();
+                EnableBetting();
+            }
+        },
+        ctx);
+}
+
+void BlackjackClient::PlayingTable::InsuranceButton_Click(Platform::Object ^ sender,
+                                                          Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e)
+{
+    if (_tableId.empty() || m_name.empty()) return;
+
+    ClearTable();
+    DisableHit();
+    DisableInsurance();
+    m_alreadyInsured = true;
+
+    std::wstring betText(buyInsuranceTextBox->Text->Data());
+
+    this->resultLabel->Text = L"Waiting for a response";
+
+    std::wostringstream buf;
+    buf << _tableId << L"?request=insure&amount=" << betText
+        << "&name=" << uri::encode_uri(m_name, uri::components::query);
+
+    auto request = m_client.request(methods::PUT, buf.str());
+
+    auto ctx = pplx::task_continuation_context::use_current();
+
+    request.then(
+        [this](pplx::task<http_response> tsk) {
+            this->resultLabel->Text.clear();
+
+            try
+            {
+                auto response = tsk.get();
+                InterpretResponse(response);
+            }
+            catch (const http_exception& exc)
+            {
+                InterpretError(exc.error_code().value());
+                EnableHit();
+                EnableInsurance();
+            }
+        },
+        ctx);
+}
+
+void BlackjackClient::PlayingTable::DoubleButton_Click(Platform::Object ^ sender,
+                                                       Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e)
+{
+    if (_tableId.empty() || m_name.empty()) return;
+
+    DisableHit();
+    DisableInsurance();
+
+    this->resultLabel->Text = L"Waiting for a response";
+
+    std::wostringstream buf;
+    buf << _tableId << L"?request=double&name=" << uri::encode_uri(m_name, uri::components::query);
+
+    auto request = m_client.request(methods::PUT, buf.str());
+
+    auto ctx = pplx::task_continuation_context::use_current();
+
+    request.then(
+        [this](pplx::task<http_response> tsk) {
+            this->resultLabel->Text.clear();
+
+            try
+            {
+                auto response = tsk.get();
+                InterpretResponse(response);
+            }
+            catch (const http_exception& exc)
+            {
+                InterpretError(exc.error_code().value());
+                EnableHit();
+            }
+        },
+        ctx);
+}
+
+void BlackjackClient::PlayingTable::StayButton_Click(Platform::Object ^ sender,
+                                                     Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e)
+{
+    if (_tableId.empty() || m_name.empty()) return;
+
+    DisableHit();
+    DisableInsurance();
+
+    this->resultLabel->Text = L"Waiting for a response";
+
+    std::wostringstream buf;
+    buf << _tableId << L"?request=stay&name=" << uri::encode_uri(m_name, uri::components::query);
+
+    auto request = m_client.request(methods::PUT, buf.str());
+
+    auto ctx = pplx::task_continuation_context::use_current();
+
+    request.then(
+        [this](pplx::task<http_response> tsk) {
+            this->resultLabel->Text.clear();
+
+            try
+            {
+                auto response = tsk.get();
+                InterpretResponse(response);
+            }
+            catch (const http_exception& exc)
+            {
+                InterpretError(exc.error_code().value());
+                EnableHit();
+            }
+        },
+        ctx);
+}
+
+void BlackjackClient::PlayingTable::HitButton_Click(Platform::Object ^ sender,
+                                                    Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e)
+{
+    if (_tableId.empty() || m_name.empty()) return;
+
+    DisableInsurance();
+
+    this->resultLabel->Text = L"Waiting for a response";
+
+    std::wostringstream buf;
+    buf << _tableId << L"?request=hit&name=" << uri::encode_uri(m_name, uri::components::query);
+
+    auto request = m_client.request(methods::PUT, buf.str());
+
+    auto ctx = pplx::task_continuation_context::use_current();
+
+    request.then(
+        [this](pplx::task<http_response> tsk) {
+            this->resultLabel->Text.clear();
+
+            try
+            {
+                auto response = tsk.get();
+                InterpretResponse(response);
+            }
+            catch (const http_exception& exc)
+            {
+                InterpretError(exc.error_code().value());
+            }
+        },
+        ctx);
+}
+
+void BlackjackClient::PlayingTable::ExitButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
+{
+    if (_tableId.empty() || m_name.empty())
+    {
+        Windows::ApplicationModel::Core::CoreApplication::Exit();
+    }
+    else
+    {
+        DisableExit();
+        DisableConnecting();
+
+        auto ctx = pplx::task_continuation_context::use_current();
+
+        std::wostringstream buf;
+        buf << _tableId << L"?name=" << uri::encode_uri(m_name, uri::components::query);
+
+        auto request = m_client.request(methods::DEL, buf.str());
+
+        request.then(
+            [this](pplx::task<http_response> tsk) {
+                EnableExit();
+
+                this->resultLabel->Text.clear();
+
+                try
+                {
+                    auto response = tsk.get();
+                }
+                catch (const http_exception& exc)
+                {
+                    InterpretError(exc.error_code().value());
+                    EnableExit();
+                }
+
+                Windows::ApplicationModel::Core::CoreApplication::Exit();
+            },
+            ctx);
+    }
+}
+
+void BlackjackClient::PlayingTable::JoinButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
+{
+    m_name = this->playerName->Text->Data();
+
+    if (m_name.size() == 0)
+    {
+        this->resultLabel->Text = L"Please state your name!";
+        return;
+    }
+
+    _tableId = L"1";
+
+    DisableExit();
+    DisableConnecting();
+
+    this->resultLabel->Text = L"Joining table";
+
+    uri_builder bldr(this->serverAddress->Text->Data());
+    bldr.append_path(m_dealerResource);
+    m_client = http_client(bldr.to_string());
+
+    std::wostringstream buf;
+    buf << _tableId << L"?name=" << uri::encode_uri(m_name, uri::components::query);
+
+    auto request = m_client.request(methods::POST, buf.str());
+
+    auto ctx = pplx::task_continuation_context::use_current();
+
+    request.then(
+        [this](pplx::task<http_response> tsk) {
+            EnableExit();
+
+            this->resultLabel->Text.clear();
+
+            try
+            {
+                auto response = tsk.get();
+
+                this->resultLabel->Text = L"Place your bet!";
+
+                EnableDisconnecting();
+                EnableBetting();
+
+                InterpretResponse(response);
+            }
+            catch (const http_exception& exc)
+            {
+                InterpretError(exc.error_code().value());
+                EnableConnecting();
+                _tableId.clear();
+            }
+        },
+        ctx);
+}
+
+void BlackjackClient::PlayingTable::LeaveButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
+{
+    this->resultLabel->Text = L"Leaving table";
+
+    DisableExit();
+    DisableBetting();
+    DisableDisconnecting();
+
+    auto ctx = pplx::task_continuation_context::use_current();
+
+    std::wostringstream buf;
+    buf << _tableId << L"?name=" << uri::encode_uri(m_name, uri::components::query);
+
+    auto request = m_client.request(methods::DEL, buf.str());
+
+    request.then(
+        [this](pplx::task<http_response> tsk) {
+            EnableExit();
+
+            try
+            {
+                auto response = tsk.get();
+
+                EnableConnecting();
+                ClearDealerCards();
+                ClearTable();
+
+                this->resultLabel->Text = L"Thanks for playing!";
+
+                _tableId.clear();
+            }
+            catch (const http_exception& exc)
+            {
+                InterpretError(exc.error_code().value());
+                EnableBetting();
+                EnableDisconnecting();
+            }
+        },
+        ctx);
+}
+
+void PlayingTable::OnNavigatedTo(NavigationEventArgs ^ e) {}
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.h b/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.h
new file mode 100644 (file)
index 0000000..9f2fbe6
--- /dev/null
@@ -0,0 +1,159 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Table.xaml.h: Declaration of the Table.xaml class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "pch.h"
+
+#include "Player.xaml.h"
+#include "PlayingTable.g.h"
+#include "cpprest/http_client.h"
+
+using namespace Platform;
+using namespace Windows::UI::Xaml::Controls;
+using namespace web;
+using namespace utility;
+using namespace http;
+using namespace http::client;
+
+namespace BlackjackClient
+{
+public
+ref class PlayingTable sealed
+{
+public:
+    PlayingTable();
+    virtual ~PlayingTable();
+
+protected:
+    virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e) override;
+
+private:
+    void JoinButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
+    void LeaveButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
+
+    void InsuranceButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e);
+    void DoubleButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e);
+    void StayButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e);
+    void HitButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e);
+    void BetButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e);
+    void ExitButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
+
+    void Refresh();
+
+    http_client m_client;
+
+    std::wstring _tableId;
+
+    bool m_alreadyInsured;
+
+    std::wstring m_dealerResource;
+
+    std::wstring m_name;
+
+    pplx::cancellation_token_source _cancellationTokenSource;
+
+    std::vector<Card> _dealerCards;
+
+    std::vector<PlayerSpace ^> _playerSpaces;
+
+    void AddPlayerCard(int idx, Card card);
+    void AddDealerCard(Card card);
+
+    void ShowResult(int playerIdx, Player player);
+
+    bool InterpretError(HRESULT hr);
+
+    void InterpretResponse(http_response& response);
+    void InterpretResponse(json::value response);
+
+    void ClearPlayerCards()
+    {
+        for (size_t i = 0; i < _playerSpaces.size(); i++)
+        {
+            _playerSpaces[i]->Clear();
+        }
+    }
+
+    void ClearDealerCards()
+    {
+        _dealerCards.clear();
+        dealerGrid->Children->Clear();
+    }
+
+    void ClearTable()
+    {
+        ClearDealerCards();
+        ClearPlayerCards();
+        resultLabel->Text.clear();
+    }
+
+    void EnableBetting()
+    {
+        button1->Visibility = Windows::UI::Xaml::Visibility::Visible;
+        betBox->Visibility = Windows::UI::Xaml::Visibility::Visible;
+        label1->Visibility = Windows::UI::Xaml::Visibility::Visible;
+        betBox->IsEnabled = true;
+    }
+    void DisableBetting()
+    {
+        button1->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+        betBox->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+        label1->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+    }
+
+    void EnableInsurance()
+    {
+        buyInsuranceLabel->Visibility = Windows::UI::Xaml::Visibility::Visible;
+        buyInsuranceTextBox->Visibility = Windows::UI::Xaml::Visibility::Visible;
+        buyInsuranceButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
+        m_alreadyInsured = true;
+    }
+    void DisableInsurance()
+    {
+        buyInsuranceLabel->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+        buyInsuranceTextBox->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+        buyInsuranceButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+    }
+
+    void EnableHit()
+    {
+        hitButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
+        stayButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
+    }
+    void DisableHit()
+    {
+        hitButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+        stayButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+        doubleButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+    }
+
+    void EnableConnecting()
+    {
+        joinButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
+        TopBar->IsEnabled = true;
+    }
+
+    void DisableConnecting()
+    {
+        joinButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+        TopBar->IsEnabled = false;
+    }
+
+    void EnableDisconnecting() { leaveButton->Visibility = Windows::UI::Xaml::Visibility::Visible; }
+
+    void DisableDisconnecting() { leaveButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed; }
+
+    void EnableExit() { exitButton->Visibility = Windows::UI::Xaml::Visibility::Visible; }
+
+    void DisableExit() { exitButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed; }
+};
+} // namespace BlackjackClient
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/messagetypes.h b/Release/samples/BlackJack/BlackJack_UIClient/messagetypes.h
new file mode 100644 (file)
index 0000000..d7eff9e
--- /dev/null
@@ -0,0 +1,275 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * messagetypes.h
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+#include "cpprest/json.h"
+
+enum BJHandResult
+{
+    HR_None,
+    HR_PlayerBlackJack,
+    HR_PlayerWin,
+    HR_ComputerWin,
+    HR_Push
+};
+
+enum BJHandState
+{
+    HR_Empty,
+    HR_BlackJack,
+    HR_Active,
+    HR_Held,
+    HR_Busted
+};
+
+enum BJPossibleMoves
+{
+    PM_None = 0x0,
+    PM_Hit = 0x1,
+    PM_DoubleDown = 0x2,
+    PM_Split = 0x4,
+    PM_All = 0x7
+};
+
+enum CardSuit
+{
+    CS_Heart,
+    CS_Diamond,
+    CS_Club,
+    CS_Spade
+};
+
+enum CardValue
+{
+    CV_None,
+    CV_Ace,
+    CV_Two,
+    CV_Three,
+    CV_Four,
+    CV_Five,
+    CV_Six,
+    CV_Seven,
+    CV_Eight,
+    CV_Nine,
+    CV_Ten,
+    CV_Jack,
+    CV_Queen,
+    CV_King
+};
+
+enum BJStatus
+{
+    ST_PlaceBet,
+    ST_Refresh,
+    ST_YourTurn,
+    ST_None
+};
+
+#define STATE L"state"
+#define BET L"bet"
+#define DOUBLE L"double"
+#define INSURE L"insure"
+#define HIT L"hit"
+#define STAY L"stay"
+#define REFRESH L"refresh"
+#define INSURANCE L"insurance"
+#define RESULT L"result"
+#define NAME L"Name"
+#define BALANCE L"Balance"
+#define HAND L"Hand"
+#define SUIT L"suit"
+#define VALUE L"value"
+#define CARDS L"cards"
+#define CAPACITY L"Capacity"
+#define ID L"Id"
+#define PLAYERS L"Players"
+#define DEALER L"DEALER"
+#define DATA L"Data"
+#define STATUS L"Status"
+#define REQUEST L"request"
+#define AMOUNT L"amount"
+#define QUERY_NAME L"name"
+
+struct Card
+{
+    CardSuit suit;
+    CardValue value;
+    bool visible;
+
+    Card(CardSuit suit, CardValue value, bool show = true) : suit(suit), value(value), visible(show) {}
+    Card(const Card& other) : suit(other.suit), value(other.value), visible(other.visible) {}
+
+    Card(web::json::value object) : visible(true)
+    {
+        suit = (CardSuit)(int)object[SUIT].as_double();
+        value = (CardValue)(int)object[VALUE].as_double();
+    }
+};
+
+struct NumericHandValues
+{
+    int low;
+    int high;
+
+    int Best() { return (high < 22) ? high : low; }
+};
+
+struct BJHand
+{
+    bool revealBoth;
+
+    std::vector<Card> cards;
+    double bet;
+    double insurance;
+    BJHandState state;
+    BJHandResult result;
+
+    BJHand() : state(HR_Empty), result(HR_None), bet(0.0), insurance(0), revealBoth(true) {}
+
+    void Clear()
+    {
+        cards.clear();
+        state = HR_Empty;
+        result = HR_None;
+        insurance = 0.0;
+    }
+
+    void AddCard(Card card)
+    {
+        cards.push_back(card);
+        NumericHandValues value = GetNumericValues();
+
+        if (cards.size() == 2 && value.high == 21)
+        {
+            state = HR_BlackJack;
+        }
+        else if (value.low > 21)
+        {
+            state = HR_Busted;
+        }
+        else
+        {
+            state = HR_Active;
+        }
+    }
+
+    NumericHandValues GetNumericValues()
+    {
+        NumericHandValues r;
+        r.low = 0;
+        r.low = 0;
+
+        bool hasAces = false;
+
+        for (auto iter = cards.begin(); iter != cards.end(); ++iter)
+        {
+            if (iter->value == CV_Ace) hasAces = true;
+
+            r.low += (iter->value < 10) ? iter->value : 10;
+        }
+        r.high = hasAces ? r.low + 10 : r.low;
+        return r;
+    }
+
+    BJHand(web::json::value object)
+    {
+        web::json::value l_cards = object[CARDS];
+
+        for (auto iter = l_cards.as_array().begin(); iter != l_cards.as_array().end(); ++iter)
+        {
+            if (!iter->is_null())
+            {
+                Card card(*iter);
+                this->cards.push_back(card);
+            }
+        }
+
+        state = (BJHandState)(int)object[STATE].as_double();
+        bet = object[BET].as_double();
+        insurance = object[INSURANCE].as_double();
+        result = (BJHandResult)(int)object[RESULT].as_double();
+    }
+};
+
+struct Player
+{
+    std::wstring Name;
+    BJHand Hand;
+    double Balance;
+
+    Player() {}
+    Player(const std::wstring& name) : Name(name), Balance(1000.0) {}
+
+    static Player FromJSON(web::json::value object)
+    {
+        Player result;
+
+        const web::json::value& name = object[NAME];
+        const web::json::value& balance = object[BALANCE];
+        const web::json::value& hand = object[HAND];
+
+        result.Name = name.as_string();
+        result.Balance = balance.as_double();
+        result.Hand = BJHand(hand);
+
+        return result;
+    }
+};
+
+struct BJTable
+{
+    int Id;
+    size_t Capacity;
+    std::vector<Player> Players;
+
+    BJTable() : Capacity(0) {}
+    BJTable(int id, size_t capacity) : Id(id), Capacity(capacity)
+    {
+        Player tmp(DEALER);
+        Players.push_back(tmp);
+    }
+
+    BJTable(web::json::value object)
+    {
+        Id = (int)object[ID].as_double();
+        Capacity = (size_t)object[CAPACITY].as_double();
+
+        web::json::value players = object[PLAYERS];
+
+        for (auto iter = players.as_object().begin(); iter != players.as_object().end(); ++iter)
+        {
+            Players.push_back(Player::FromJSON(iter->second));
+        }
+    }
+};
+
+struct BJPutResponse
+{
+    BJStatus Status;
+    web::json::value Data;
+
+    BJPutResponse() {}
+    BJPutResponse(BJStatus status, web::json::value data) : Status(status), Data(data) {}
+
+    static BJPutResponse FromJSON(web::json::value object)
+    {
+        BJPutResponse result((BJStatus)(int)object[STATUS].as_double(), object[DATA]);
+        return result;
+    }
+
+    web::json::value AsJSON() const
+    {
+        web::json::value result = web::json::value::object();
+        result[STATUS] = web::json::value::number((double)Status);
+        result[DATA] = Data;
+        return result;
+    }
+};
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/pch.cpp b/Release/samples/BlackJack/BlackJack_UIClient/pch.cpp
new file mode 100644 (file)
index 0000000..74bfcf9
--- /dev/null
@@ -0,0 +1,12 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * pch.cpp: Include the standard header and generate the precompiled header.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "pch.h"
diff --git a/Release/samples/BlackJack/BlackJack_UIClient/pch.h b/Release/samples/BlackJack/BlackJack_UIClient/pch.h
new file mode 100644 (file)
index 0000000..427ba87
--- /dev/null
@@ -0,0 +1,16 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * pch.h: Header for standard system include files.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "App.xaml.h"
+#include "messagetypes.h"
+#include <collection.h>
diff --git a/Release/samples/BlackJack/CMakeLists.txt b/Release/samples/BlackJack/CMakeLists.txt
new file mode 100644 (file)
index 0000000..422ec4c
--- /dev/null
@@ -0,0 +1,6 @@
+if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE)
+  add_subdirectory(BlackJack_Server)
+  add_subdirectory(BlackJack_Client)
+else()
+  # TODO: add BlackJack_UIClient
+endif()
diff --git a/Release/samples/CMakeLists.txt b/Release/samples/CMakeLists.txt
new file mode 100644 (file)
index 0000000..df1fcb3
--- /dev/null
@@ -0,0 +1,9 @@
+add_subdirectory(SearchFile)
+add_subdirectory(BingRequest)
+add_subdirectory(BlackJack)
+add_subdirectory(Oauth1Client)
+add_subdirectory(Oauth2Client)
+
+add_custom_target(samples
+  DEPENDS SearchFile BingRequest blackjackclient blackjackserver oauth1client oauth2client
+  )
\ No newline at end of file
diff --git a/Release/samples/CasaLens/AppCode.html b/Release/samples/CasaLens/AppCode.html
new file mode 100644 (file)
index 0000000..60ab734
--- /dev/null
@@ -0,0 +1,157 @@
+ļ»æ<!DOCTYPE html>
+<html>
+<head>
+    <title>Casa Lens | Casablana Test App</title>
+    <meta charset="utf-8" />
+    <link href="css/default.css" rel="stylesheet" type="text/css">
+    <script type="text/javascript" src="/js/default.js"></script>
+</head>
+<body style="width: 60%; margin: 0 auto;">
+    <table>
+        <tr>
+            <td>
+                <div>
+                    <img class="Logo" src="image/logo.png">
+                </div>
+            </td>
+            <td id="searchBar" style="width: 90%; vertical-align: middle; text-align: right; padding-right: 40px">
+                <input style="width: 80%; min-height: 30px; color: #444444;" id="location_ip" type="text"
+                    class="textclass" name="q" size="21" maxlength="120" value="Search Zip or City" onclick="ClearText()" />
+                <button style="width: 120px; min-height: 30px;" type="button" onclick="FetchData()" class="tftextbtn" name="q" size="21" maxlength="120">Search</button>
+            </td>
+        </tr>
+    </table>
+    <div id="defaultData">
+        <img style="height: 100%; width: 100%" src="/image/wall.jpg">
+    </div>
+    <div id="errorData" style="display: none;">
+        <p style="color: #950C0C; font-size: large;">Failed to fetch the data! Please try again later.</p>
+        <img style="height: 100%; width: 100%" src="/image/wall.jpg">
+    </div>
+    <div id="cityData" style="display: none;">
+        <table style="width: 100%;">
+            <tr>
+                <td style="width: 70%;">
+                    <h4 style="background-color: #006092; color: #fff;">Attractions</h4>
+                    <table>
+                        <tr>
+                            <td style="width: 80%">
+                                <center style="width: 100%; height: 100%;">
+                                                           <img class="mainPic" id="mainPic1" style="width: 95%;" 
+                                                   src="/image/wall.jpg">
+                                                   </center>
+                            </td>
+                            <td style="width: 20%">
+                                <img class="thumbnailPic" id="thumbnailPic1" src="/image/wall.jpg" style="width: 100%;">
+                                <img class="thumbnailPic" id="thumbnailPic2" src="/image/wall.jpg" style="width: 100%;">
+                                <img class="thumbnailPic" id="thumbnailPic3" src="/image/wall.jpg" style="width: 100%;">
+                            </td>
+                        </tr>
+                    </table>
+                    <div>
+                        <p>
+                            <a href="http://www.bing.com/">Image </a>results by
+                            <img src="image/bing-logo.jpg">
+                        </p>
+                    </div>
+
+                </td>
+                <td style="width: 45%; height: 100%;" id="weatherData">
+                    <h4 style="background-color: #006092; color: #fff;">Current Weather</h4>
+                    <table id="weatherTemplate" class="WeatherData" style="width: 100%; height: 100%; display: none">
+                        <tr style="height: 100%; width: 100%;">
+                            <td>
+                                <div class="CurrentTemperature">%wt%Ā°F</div>
+                                <img id="weatherImg" src="%wimg%" style="float: left;">
+                                <p>%wdesc%</p>
+                                <hr>
+                            </td>
+                        </tr>
+                        <tr style="height: 100%; width: 100%;">
+                            <td style="vertical-align: middle; width: 100%">
+                                <table style="width: 100%">
+                                    <tr>
+                                        <td>Min. Temperature</td>
+                                        <td style="text-align: right">%wtmin%Ā°F</td>
+                                    </tr>
+                                    <tr>
+                                        <td>Max. Temperature</td>
+                                        <td style="text-align: right">%wtmax%Ā°F</td>
+                                    </tr>
+                                    <tr>
+                                        <td>Pressure</td>
+                                        <td style="text-align: right">%wp%mb</td>
+                                    </tr>
+                                </table>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <div>
+                                    <p><a href="http://openweathermap.org/">Weather </a>by OpenWeatherMap</p>
+                                    <p><a href="https://developers.google.com/maps/">Postal code to location conversion </a>by Google Maps API</p>
+                                </div>
+                            </td>
+                        </tr>
+                    </table>
+                </td>
+            </tr>
+            <tr>
+                <td style="width: 60%; padding-right: 20px; padding-left: 20px;">
+                    <div id="eventsData">
+                        <h4 style="background-color: #006092; color: #fff;">Events around you</h4>
+                        <table id="eventsTable" style="width: 100%">
+                            <tr id="eventTemplate" style="display: none">
+                                <td style="margin: 10px; padding-bottom: 20px">
+                                    <div class="EventTitle wrapWords"><a href="%eventlink1%">%eventtitle%</a></div>
+                                    <div class="EventDescription wrapWords" style="max-height: 200px">
+                                        %eventdescription%
+                                        <a class="read-more" href="%eventlink2%" data-omni-sm="gbl_river_readmore">...</a>
+                                    </div>
+                                </td>
+                                <td style="min-width: 100px">%eventvenue%</td>
+                                <td style="min-width: 100px">%eventstarttime% </td>
+                            </tr>
+                        </table>
+                        <div class="eventful-badge eventful-medium">
+                            <img src="http://api.eventful.com/images/powered/eventful_88x31.gif"
+                                alt="Local Events, Concerts, Tickets">
+                            <p><a href="http://eventful.com/">Events</a> by Eventful</p>
+                        </div>
+                    </div>
+                </td>
+                <td style="width: 40%; padding-left: 10px;">
+                    <div id="moviesData">
+                        <h4 style="background-color: #006092; color: #fff;">Now playing in Theaters</h4>
+                        <div id="movieTemplateToHide">
+                            <div id="movieTemplate" style="display: none">
+                                <table>
+                                    <tr>
+                                        <td>
+                                            <h2 class="EventTitle">%mtitle%</h2>
+                                            <p>%mtheater%</p>
+                                        </td>
+                                        <td rowspan="2">
+                                            <img class="thumbnailPic" id="Img1" src="%mposter%" style="width: 100%;">
+                                        </td>
+                                    </tr>
+                                </table>
+                                <hr>
+                            </div>
+                        </div>
+                        <div>
+                            <p><a href="http://developer.tmsapi.com/">Movie data</a> by OnConnect TMS API services</p>
+                            <p>
+                                <a href="http://www.bing.com/">Movie poster </a>results by
+                                        <img src="/image/bing-logo.jpg" alt="Movie poster results by Bing">
+                            </p>
+                        </div>
+                    </div>
+                </td>
+            </tr>
+
+        </table>
+
+    </div>
+</body>
+</html>
diff --git a/Release/samples/CasaLens/CasaLens141/AppCode.html b/Release/samples/CasaLens/CasaLens141/AppCode.html
new file mode 100644 (file)
index 0000000..60ab734
--- /dev/null
@@ -0,0 +1,157 @@
+ļ»æ<!DOCTYPE html>
+<html>
+<head>
+    <title>Casa Lens | Casablana Test App</title>
+    <meta charset="utf-8" />
+    <link href="css/default.css" rel="stylesheet" type="text/css">
+    <script type="text/javascript" src="/js/default.js"></script>
+</head>
+<body style="width: 60%; margin: 0 auto;">
+    <table>
+        <tr>
+            <td>
+                <div>
+                    <img class="Logo" src="image/logo.png">
+                </div>
+            </td>
+            <td id="searchBar" style="width: 90%; vertical-align: middle; text-align: right; padding-right: 40px">
+                <input style="width: 80%; min-height: 30px; color: #444444;" id="location_ip" type="text"
+                    class="textclass" name="q" size="21" maxlength="120" value="Search Zip or City" onclick="ClearText()" />
+                <button style="width: 120px; min-height: 30px;" type="button" onclick="FetchData()" class="tftextbtn" name="q" size="21" maxlength="120">Search</button>
+            </td>
+        </tr>
+    </table>
+    <div id="defaultData">
+        <img style="height: 100%; width: 100%" src="/image/wall.jpg">
+    </div>
+    <div id="errorData" style="display: none;">
+        <p style="color: #950C0C; font-size: large;">Failed to fetch the data! Please try again later.</p>
+        <img style="height: 100%; width: 100%" src="/image/wall.jpg">
+    </div>
+    <div id="cityData" style="display: none;">
+        <table style="width: 100%;">
+            <tr>
+                <td style="width: 70%;">
+                    <h4 style="background-color: #006092; color: #fff;">Attractions</h4>
+                    <table>
+                        <tr>
+                            <td style="width: 80%">
+                                <center style="width: 100%; height: 100%;">
+                                                           <img class="mainPic" id="mainPic1" style="width: 95%;" 
+                                                   src="/image/wall.jpg">
+                                                   </center>
+                            </td>
+                            <td style="width: 20%">
+                                <img class="thumbnailPic" id="thumbnailPic1" src="/image/wall.jpg" style="width: 100%;">
+                                <img class="thumbnailPic" id="thumbnailPic2" src="/image/wall.jpg" style="width: 100%;">
+                                <img class="thumbnailPic" id="thumbnailPic3" src="/image/wall.jpg" style="width: 100%;">
+                            </td>
+                        </tr>
+                    </table>
+                    <div>
+                        <p>
+                            <a href="http://www.bing.com/">Image </a>results by
+                            <img src="image/bing-logo.jpg">
+                        </p>
+                    </div>
+
+                </td>
+                <td style="width: 45%; height: 100%;" id="weatherData">
+                    <h4 style="background-color: #006092; color: #fff;">Current Weather</h4>
+                    <table id="weatherTemplate" class="WeatherData" style="width: 100%; height: 100%; display: none">
+                        <tr style="height: 100%; width: 100%;">
+                            <td>
+                                <div class="CurrentTemperature">%wt%Ā°F</div>
+                                <img id="weatherImg" src="%wimg%" style="float: left;">
+                                <p>%wdesc%</p>
+                                <hr>
+                            </td>
+                        </tr>
+                        <tr style="height: 100%; width: 100%;">
+                            <td style="vertical-align: middle; width: 100%">
+                                <table style="width: 100%">
+                                    <tr>
+                                        <td>Min. Temperature</td>
+                                        <td style="text-align: right">%wtmin%Ā°F</td>
+                                    </tr>
+                                    <tr>
+                                        <td>Max. Temperature</td>
+                                        <td style="text-align: right">%wtmax%Ā°F</td>
+                                    </tr>
+                                    <tr>
+                                        <td>Pressure</td>
+                                        <td style="text-align: right">%wp%mb</td>
+                                    </tr>
+                                </table>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <div>
+                                    <p><a href="http://openweathermap.org/">Weather </a>by OpenWeatherMap</p>
+                                    <p><a href="https://developers.google.com/maps/">Postal code to location conversion </a>by Google Maps API</p>
+                                </div>
+                            </td>
+                        </tr>
+                    </table>
+                </td>
+            </tr>
+            <tr>
+                <td style="width: 60%; padding-right: 20px; padding-left: 20px;">
+                    <div id="eventsData">
+                        <h4 style="background-color: #006092; color: #fff;">Events around you</h4>
+                        <table id="eventsTable" style="width: 100%">
+                            <tr id="eventTemplate" style="display: none">
+                                <td style="margin: 10px; padding-bottom: 20px">
+                                    <div class="EventTitle wrapWords"><a href="%eventlink1%">%eventtitle%</a></div>
+                                    <div class="EventDescription wrapWords" style="max-height: 200px">
+                                        %eventdescription%
+                                        <a class="read-more" href="%eventlink2%" data-omni-sm="gbl_river_readmore">...</a>
+                                    </div>
+                                </td>
+                                <td style="min-width: 100px">%eventvenue%</td>
+                                <td style="min-width: 100px">%eventstarttime% </td>
+                            </tr>
+                        </table>
+                        <div class="eventful-badge eventful-medium">
+                            <img src="http://api.eventful.com/images/powered/eventful_88x31.gif"
+                                alt="Local Events, Concerts, Tickets">
+                            <p><a href="http://eventful.com/">Events</a> by Eventful</p>
+                        </div>
+                    </div>
+                </td>
+                <td style="width: 40%; padding-left: 10px;">
+                    <div id="moviesData">
+                        <h4 style="background-color: #006092; color: #fff;">Now playing in Theaters</h4>
+                        <div id="movieTemplateToHide">
+                            <div id="movieTemplate" style="display: none">
+                                <table>
+                                    <tr>
+                                        <td>
+                                            <h2 class="EventTitle">%mtitle%</h2>
+                                            <p>%mtheater%</p>
+                                        </td>
+                                        <td rowspan="2">
+                                            <img class="thumbnailPic" id="Img1" src="%mposter%" style="width: 100%;">
+                                        </td>
+                                    </tr>
+                                </table>
+                                <hr>
+                            </div>
+                        </div>
+                        <div>
+                            <p><a href="http://developer.tmsapi.com/">Movie data</a> by OnConnect TMS API services</p>
+                            <p>
+                                <a href="http://www.bing.com/">Movie poster </a>results by
+                                        <img src="/image/bing-logo.jpg" alt="Movie poster results by Bing">
+                            </p>
+                        </div>
+                    </div>
+                </td>
+            </tr>
+
+        </table>
+
+    </div>
+</body>
+</html>
diff --git a/Release/samples/CasaLens/CasaLens141/css/default.css b/Release/samples/CasaLens/CasaLens141/css/default.css
new file mode 100644 (file)
index 0000000..65809e8
--- /dev/null
@@ -0,0 +1,97 @@
+ļ»æbody
+{
+       width: 100%;
+       height: 100%;
+       font-family: Calibri;
+       background-image: linear-gradient(20deg, white, #DDE6DF 62.6%, #24507C);
+       background-repeat: no-repeat;
+       background-size: 100% 100%; 
+}
+
+h4 {
+    height:25px;
+    text-align: center;
+}
+
+.Logo {
+       width: 105.59px;
+       height: 103.64px;
+}
+
+.searchBox {
+}
+
+.textclass
+{
+       width: 46%;
+       font-family: "segoe ui";
+}
+       
+.tftextbtn
+{
+       color: #fff;
+       display: inline-block;
+       background-color: rgb(230, 100, 0);
+    font-family: "segoe ui";
+}
+
+.PictureGallery {
+}
+
+.mainPic {
+}
+
+.thumbnailPic {
+}
+
+.CurrentTemperature {
+       vertical-align:middle;
+       text-align: center;
+       font-size: 75px;
+}
+
+.WeatherData {
+}
+
+.EventTitle {
+    font-size: large;
+}
+
+.EventDescription {
+    font-family:Helvetica;
+   word-wrap:break-word;
+   font-size:small;
+}
+
+a:link {text-decoration:none;}
+td 
+{
+       vertical-align:top;
+       valign:top;
+}
+
+.verticalLine {
+    border-left: thick solid #ff0000;
+}
+
+.wrap-words {
+    word-wrap:break-word;
+}
+
+.eventful-badge,
+.eventful-badge * {
+  margin: 0             !important;
+  padding: 0            !important;
+  border: 0             !important;
+  text-align: center    !important;
+  color: #CCC           !important;
+  font-family: Arial    !important;
+  text-decoration: none !important;
+}
+
+.eventful-medium {
+  position: relative    !important;
+  width: 100px          !important;
+  font-size: 12px       !important;
+  line-height: 13px     !important;
+}  
\ No newline at end of file
diff --git a/Release/samples/CasaLens/CasaLens141/image/bing-logo.jpg b/Release/samples/CasaLens/CasaLens141/image/bing-logo.jpg
new file mode 100644 (file)
index 0000000..22a2dbe
Binary files /dev/null and b/Release/samples/CasaLens/CasaLens141/image/bing-logo.jpg differ
diff --git a/Release/samples/CasaLens/CasaLens141/image/logo.png b/Release/samples/CasaLens/CasaLens141/image/logo.png
new file mode 100644 (file)
index 0000000..1ba6835
Binary files /dev/null and b/Release/samples/CasaLens/CasaLens141/image/logo.png differ
diff --git a/Release/samples/CasaLens/CasaLens141/image/wall.jpg b/Release/samples/CasaLens/CasaLens141/image/wall.jpg
new file mode 100644 (file)
index 0000000..ccd15ff
Binary files /dev/null and b/Release/samples/CasaLens/CasaLens141/image/wall.jpg differ
diff --git a/Release/samples/CasaLens/CasaLens141/js/default.js b/Release/samples/CasaLens/CasaLens141/js/default.js
new file mode 100644 (file)
index 0000000..b7e1ea2
--- /dev/null
@@ -0,0 +1,173 @@
+ļ»æfunction formatAMPM(date) {
+    var hours = date.getHours();
+    var minutes = date.getMinutes();
+    var ampm = hours >= 12 ? 'pm' : 'am';
+    hours = hours % 12;
+    hours = hours ? hours : 12; // the hour '0' should be '12'
+    minutes = minutes < 10 ? '0' + minutes : minutes;
+    var strTime = hours + ':' + minutes + ' ' + ampm;
+    return strTime;
+}
+
+function formatEventTime(dateTimeStr) {
+    dateTimeStr = dateTimeStr.substr(0, dateTimeStr.lastIndexOf(":")).split(" ").join("T")
+    var dateObj = new Date(dateTimeStr);
+    if (!dateObj) {
+        return "";
+    }
+    var dateStr = dateObj.toDateString();
+    dateStr = dateStr.substring(0, dateStr.length - 5);
+    return dateStr + "<br/>" + formatAMPM(dateObj);
+}
+
+function ClearText() {
+    location_ip.value = "";
+}
+
+function TrimText(text, maxLength) {
+    if (text.length < maxLength) {
+        return text;
+    } else {
+        var i = maxLength;
+        while (i < text.length) {
+
+            if (text[i] == " " || text[i] == "," || text[i] == "." || text[i] == "\r" || text[i] == "\n") {
+                break;
+            }
+            i++;
+        }
+        return text.substr(0, i);
+    }
+}
+
+function populateEvents(events) {
+
+    var tempDiv = document.createElement("div");
+
+    var currentChildNodes = eventsTable.childNodes;
+    var defaultChildrenCount = 2;
+    for (var i = defaultChildrenCount; i < currentChildNodes.length; i++) {
+        currentChildNodes[i].parentNode.removeChild(currentChildNodes[i]);
+    }
+    for (var i = 0; i < events.length; i++) {
+        var dataObj = events[i];
+
+        tempDiv.innerHTML = dataObj.description;
+        var description = TrimText(tempDiv.innerText, 150);
+        var innerHtml = eventTemplate.innerHTML;
+        innerHtml = innerHtml.replace("%eventtitle%", TrimText(dataObj.title, 30));
+        innerHtml = innerHtml.replace("%eventstarttime%", formatEventTime(dataObj.starttime));
+        innerHtml = innerHtml.replace("%eventdescription%", description);
+        innerHtml = innerHtml.replace("%eventlink1%", dataObj.url);
+        innerHtml = innerHtml.replace("%eventlink2%", dataObj.url);
+        innerHtml = innerHtml.replace("%eventvenue%", dataObj.venue_address);
+        var obj = document.createElement("tr");
+        obj.innerHTML = window.toStaticHTML(innerHtml);
+
+        eventsTable.appendChild(obj);
+    }
+}
+
+function populateWeather(weather) {
+
+    var currentChildNodes = weatherData.childNodes;
+    var defaultChildrenCount = 5;
+    for (var i = defaultChildrenCount; i < currentChildNodes.length; i++) {
+        currentChildNodes[i].parentNode.removeChild(currentChildNodes[i]);
+    }
+
+    var weatherElement = weatherTemplate.cloneNode(true);
+    weatherElement.style.display = "inherit";
+    var innerHtml = weatherElement.innerHTML;
+    innerHtml = innerHtml.replace("%wt%", weather.temperature);
+    innerHtml = innerHtml.replace("%wp%", weather.pressure);
+    innerHtml = innerHtml.replace("%wtmin%", weather.temp_min);
+    innerHtml = innerHtml.replace("%wtmax%", weather.temp_max);
+    innerHtml = innerHtml.replace("%wdesc%", weather.description);
+    innerHtml = innerHtml.replace("%wimg%", weather.image);
+    weatherElement.innerHTML = window.toStaticHTML(innerHtml);
+    weatherData.appendChild(weatherElement);
+}
+
+function populateMovies(movies) {
+
+    var currentChildNodes = movieTemplateToHide.childNodes;
+    var defaultChildrenCount = 3;
+    for (var i = defaultChildrenCount; i < currentChildNodes.length; i++) {
+        currentChildNodes[i].parentNode.removeChild(currentChildNodes[i]);
+    }
+
+    var showtimes = "";
+    for (var i = 0; i < movies.length; i++) {
+        var innerHtml = movieTemplate.innerHTML;
+        var dataObj = movies[i];
+
+        var theatrename;
+        for (var j = 0; j < dataObj.theatre.length; j++) {
+
+            showtimes += dataObj.theatre[j].name;
+            showtimes += "<br/>";
+            var showtimelist = dataObj.theatre[j].datetime;
+            for (var k = 0; k < showtimelist.length; k++) {
+                var dateObj = new Date(showtimelist[k]);
+                if (dateObj) {
+                    showtimes += formatAMPM(dateObj) + " | ";
+                }
+            }
+            showtimes = showtimes.substr(0, showtimes.length - 2);
+            showtimes += "<br/><br/>";
+        }
+        innerHtml = innerHtml.replace("%mtitle%", dataObj.title);
+        innerHtml = innerHtml.replace("%mtheater%", showtimes);
+        innerHtml = innerHtml.replace("%mposter%", dataObj.poster);
+
+        var obj = document.createElement("div");
+        obj.innerHTML = window.toStaticHTML(innerHtml);
+        movieTemplateToHide.appendChild(obj);
+    }
+}
+
+function populateImages(images) {
+    if (images.length == 4) {
+        var mainimg = document.getElementById('mainPic1');
+        mainimg.src = images[0];
+
+        var img1 = document.getElementById('thumbnailPic1');
+        img1.src = images[1];
+
+        var img2 = document.getElementById('thumbnailPic2');
+        img2.src = images[2];
+
+        var img3 = document.getElementById('thumbnailPic3');
+        img3.src = images[3];
+    }
+}
+
+function FetchData() {
+    var xhr = new XMLHttpRequest();
+    xhr.onreadystatechange = function (e) {
+        // The request finished and response is ready
+        if (xhr.readyState == 4) {
+            var searchbar = document.getElementById('searchBar');
+            searchbar.style.display = 'none';
+
+            if (xhr.status == 200) {
+                var replydata = JSON.parse(xhr.responseText);
+                populateImages(replydata.images);
+                populateEvents(replydata.events);
+                populateMovies(replydata.movies);
+                populateWeather(replydata.weather);
+
+                defaultData.style.display = "none";
+                cityData.style.display = 'inherit';
+            }
+            else {
+                defaultData.style.display = "none";
+                errorData.style.display = 'inherit';
+            }
+        }
+    }
+    xhr.open("POST", document.URL, true);
+    xhr.setRequestHeader("Content-type", 'text/html');
+    xhr.send(location_ip.value);
+}
diff --git a/Release/samples/CasaLens/ReadMe.txt b/Release/samples/CasaLens/ReadMe.txt
new file mode 100644 (file)
index 0000000..84098c8
--- /dev/null
@@ -0,0 +1,49 @@
+========================================================================
+    CasaLens Project Overview
+========================================================================
+
+CasaLens is a data mash-up sample i.e use the Casablanca features to build a service that can collate data from different services and provide them to the user. 
+Given the postal code/city name, the service overlays events, movies, weather, pictures around the place.
+
+To run the sample, obtain the api keys from the different services (listed below) and update them in casalens.cpp (see Services section below for more details).
+The sample takes one parameter: the port to listen for requests.
+
+Services:
+Here are the details about the services the sample is interacting with, how to obtain the api keys for using these services and how to pass it to the sample. 
+1. Bing image search: http://datamarket.azure.com/dataset/bing/search
+   Sign UP at the above link to obtain the api key.
+   In casalens.cpp, set the value of bmaps_key variable to this key.
+   Query this service for pictures of a place and return it to the client.
+   This service is also used to collect movie posters.
+
+2. OpenWeatherMap:http://openweathermap.org/ 
+   Fetch weather data: current temperature, pressure at the location.
+
+3. Eventful: http://http://api.eventful.com/
+   Follow the "Get Started" steps -> "Register a new account" at the eventful website to obtain the application key.
+   In casalens.cpp, set the value of events_key variable to this key.
+   Fetch different events happening at the specified location.
+
+4. OnConnect tmsapi: http://developer.tmsapi.com/
+   Register at the above website and obtain a new key for the TMS API package.
+   In casalens.cpp, set the value of movies_key variable to this key.
+   Get currently playing movies in local theaters along with the show times.
+   
+5. Google maps: https://developers.google.com/maps
+   This does not require a key.
+   Given a postal code, use google maps API to get the location (city name) corresponding to that code.
+
+6. Bing maps: http://dev.virtualearth.net/
+   The key populated in step 1 should work for both bing maps and search.
+   Given a location (city name), use bing maps API to get the postal code.
+
+Files:
+1. casalens.cpp: 
+   Main file that contains code to initialize and start the http_listener.
+   We add two GET and POST handlers handle_get and handle_post and open the listener to listen for requests.
+
+2. datafetcher.cpp:
+   This file contains logic to collect data from different services, create JSON objects with the data and return it to the client.
+
+This sample is merely a demonstration of how one can use Casablanca to author data mash-ups. 
+If you plan to use/deploy the sample, do not forget to read and follow the "Terms and Conditions" for each service and ensure that you are adhering to all the requirements.
diff --git a/Release/samples/CasaLens/casalens.cpp b/Release/samples/CasaLens/casalens.cpp
new file mode 100644 (file)
index 0000000..5990ef2
--- /dev/null
@@ -0,0 +1,160 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * casalens.cpp: Listener code: Given a location/postal code, the listener queries different services
+ * for weather, things to do: events, movie and pictures and returns it to the client.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "casalens.h"
+
+using namespace web;
+using namespace http;
+using namespace utility;
+using namespace http::experimental::listener;
+
+const utility::string_t casalens_creds::events_url = U("http://api.eventful.com/json/events/search?...&location=");
+const utility::string_t casalens_creds::movies_url = U("http://data.tmsapi.com/v1/movies/showings?");
+const utility::string_t casalens_creds::images_url =
+    U("https://api.datamarket.azure.com/Bing/Search/Image?$format=json");
+const utility::string_t casalens_creds::bmaps_url = U("http://dev.virtualearth.net/REST/v1/Locations");
+const utility::string_t casalens_creds::gmaps_url = U("http://maps.googleapis.com/maps/api/geocode/json");
+const utility::string_t casalens_creds::weather_url = U("http://api.openweathermap.org/data/2.1/find/name?q=");
+
+// 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;
+const utility::string_t casalens_creds::movies_keyname = U("api_key");
+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;
+const utility::string_t casalens_creds::bmaps_keyname = U("key");
+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");
+const utility::string_t CasaLens::weather_json_key = U("weather");
+const utility::string_t CasaLens::images_json_key = U("images");
+const utility::string_t CasaLens::error_json_key = U("error");
+
+CasaLens::CasaLens(utility::string_t url) : m_listener(url)
+{
+    m_listener.support(methods::GET, std::bind(&CasaLens::handle_get, this, std::placeholders::_1));
+    m_listener.support(methods::POST, std::bind(&CasaLens::handle_post, this, std::placeholders::_1));
+
+    m_htmlcontentmap[U("/")] = std::make_tuple(U("AppCode.html"), U("text/html"));
+    m_htmlcontentmap[U("/js/default.js")] = std::make_tuple(U("js/default.js"), U("application/javascript"));
+    m_htmlcontentmap[U("/css/default.css")] = std::make_tuple(U("css/default.css"), U("text/css"));
+    m_htmlcontentmap[U("/image/logo.png")] = std::make_tuple(U("image/logo.png"), U("application/octet-stream"));
+    m_htmlcontentmap[U("/image/bing-logo.jpg")] =
+        std::make_tuple(U("image/bing-logo.jpg"), U("application/octet-stream"));
+    m_htmlcontentmap[U("/image/wall.jpg")] = std::make_tuple(U("image/wall.jpg"), U("application/octet-stream"));
+}
+
+void CasaLens::handle_error(pplx::task<void>& t)
+{
+    try
+    {
+        t.get();
+    }
+    catch (...)
+    {
+        // Ignore the error, Log it if a logger is available
+    }
+}
+
+pplx::task<void> CasaLens::open()
+{
+    return m_listener.open().then([](pplx::task<void> t) { handle_error(t); });
+}
+
+pplx::task<void> CasaLens::close()
+{
+    return m_listener.close().then([](pplx::task<void> t) { handle_error(t); });
+}
+
+// Handler to process HTTP::GET requests.
+// Replies to the request with data.
+void CasaLens::handle_get(http_request message)
+{
+    auto path = message.relative_uri().path();
+    auto content_data = m_htmlcontentmap.find(path);
+    if (content_data == m_htmlcontentmap.end())
+    {
+        message.reply(status_codes::NotFound, U("Path not found")).then([](pplx::task<void> t) { handle_error(t); });
+        return;
+    }
+
+    auto file_name = std::get<0>(content_data->second);
+    auto content_type = std::get<1>(content_data->second);
+    concurrency::streams::fstream::open_istream(file_name, std::ios::in)
+        .then([=](concurrency::streams::istream is) {
+            message.reply(status_codes::OK, is, content_type).then([](pplx::task<void> t) { handle_error(t); });
+        })
+        .then([=](pplx::task<void> t) {
+            try
+            {
+                t.get();
+            }
+            catch (...)
+            {
+                // opening the file (open_istream) failed.
+                // Reply with an error.
+                message.reply(status_codes::InternalError).then([](pplx::task<void> t) { handle_error(t); });
+            }
+        });
+}
+
+// Respond to HTTP::POST messages
+// Post data will contain the postal code or location string.
+// Aggregate location data from different services and reply to the POST request.
+void CasaLens::handle_post(http_request message)
+{
+    auto path = message.relative_uri().path();
+    if (0 == path.compare(U("/")))
+    {
+        message.extract_string()
+            .then([=](const utility::string_t& location) { get_data(message, location); })
+            .then([](pplx::task<void> t) { handle_error(t); });
+    }
+    else
+    {
+        message.reply(status_codes::NotFound, U("Path not found")).then([](pplx::task<void> t) { handle_error(t); });
+    }
+}
+
+#ifdef _WIN32
+int wmain(int argc, wchar_t* args[])
+#else
+int main(int argc, char* args[])
+#endif
+{
+    if (argc != 2)
+    {
+        wprintf(U("Usage: casalens.exe port\n"));
+        return -1;
+    }
+
+    std::wstring address = U("http://localhost:");
+    address.append(args[1]);
+
+    CasaLens listener(address);
+    listener.open().wait();
+
+    std::wcout << utility::string_t(U("Listening for requests at: ")) << address << std::endl;
+
+    std::string line;
+    std::wcout << U("Hit Enter to close the listener.");
+    std::getline(std::cin, line);
+
+    listener.close().wait();
+
+    return 0;
+}
diff --git a/Release/samples/CasaLens/casalens.h b/Release/samples/CasaLens/casalens.h
new file mode 100644 (file)
index 0000000..655a9d1
--- /dev/null
@@ -0,0 +1,116 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * casalens.h: Listener code: Given a location/postal code, the listener queries different services
+ * for weather, things to do: events, movie and pictures and returns it to the client.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "stdafx.h"
+
+#include "cpprest/containerstream.h"
+#include "cpprest/filestream.h"
+#include "cpprest/http_client.h"
+#include "cpprest/http_listener.h"
+#include "cpprest/json.h"
+#include "cpprest/producerconsumerstream.h"
+#include "cpprest\http_listener.h"
+#pragma warning(push)
+#pragma warning(disable : 4457)
+#include <agents.h>
+#pragma warning(pop)
+#include <ctime>
+#include <locale>
+
+//
+// Credentials class to manage the service URLs, key name and api-keys.
+//
+class casalens_creds
+{
+public:
+    static const utility::string_t events_url;
+    static const utility::string_t movies_url;
+    static const utility::string_t images_url;
+    static const utility::string_t weather_url;
+    static const utility::string_t bmaps_url;
+    static const utility::string_t gmaps_url;
+
+    static const utility::string_t events_keyname;
+    static const utility::string_t events_key;
+    static const utility::string_t movies_keyname;
+    static const utility::string_t movies_key;
+    static const utility::string_t images_keyname;
+    static const utility::string_t images_key;
+    static const utility::string_t bmaps_keyname;
+    static const utility::string_t bmaps_key;
+};
+
+//
+// This class implements the CasaLens server functionality.
+// It maintains a http_listener, registers to listen for requests.
+// Also implements the handlers to respond to requests and methods to fetch
+// and aggregate data from different services.
+//
+class CasaLens
+{
+public:
+    CasaLens() {}
+    CasaLens(utility::string_t url);
+
+    pplx::task<void> open();
+    pplx::task<void> close();
+
+    void handle_get(web::http::http_request message);
+    void handle_post(web::http::http_request message);
+
+private:
+    // Error handlers
+    static void handle_error(pplx::task<void>& t);
+    pplx::task<web::json::value> handle_exception(pplx::task<web::json::value>& t, const utility::string_t& field_name);
+
+    // methods to fetch data from the services
+    pplx::task<web::json::value> get_events(const utility::string_t& post_code);
+    pplx::task<web::json::value> get_weather(const utility::string_t& post_code);
+    pplx::task<web::json::value> get_pictures(const utility::string_t& location, const utility::string_t& count);
+    pplx::task<web::json::value> get_movies(const utility::string_t& post_code);
+
+    // Utility functions
+    bool is_number(const std::string& s);
+    std::wstring get_date();
+
+    void fetch_data(web::http::http_request message, const std::wstring& postal_code, const std::wstring& location);
+    void get_data(web::http::http_request message, const std::wstring& location);
+
+    static const int num_events = 5;
+    static const int num_images = 5;
+    static const int num_movies = 5;
+
+    static const utility::string_t events_json_key;
+    static const utility::string_t movies_json_key;
+    static const utility::string_t weather_json_key;
+    static const utility::string_t images_json_key;
+    static const utility::string_t error_json_key;
+
+    // Lock to the in memory cache (m_data)
+    concurrency::reader_writer_lock m_rwlock;
+
+    // key: City name or postal code
+    // Value: JSON response data to be sent to clients
+    // We are caching the data for multiple requests.
+    std::map<utility::string_t, web::json::value> m_data;
+
+    // m_htmlcontentmap contains data about the html contents of the website, their mime types
+    // key: relative URI path of the HTML content being requested
+    // value: Tuple where:
+    // Element1: relative path on the disk of the file being requested
+    // Element2: Mime type/content type of the file
+    std::map<utility::string_t, std::tuple<utility::string_t, utility::string_t>> m_htmlcontentmap;
+
+    // HTTP listener
+    web::http::experimental::listener::http_listener m_listener;
+};
diff --git a/Release/samples/CasaLens/css/default.css b/Release/samples/CasaLens/css/default.css
new file mode 100644 (file)
index 0000000..65809e8
--- /dev/null
@@ -0,0 +1,97 @@
+ļ»æbody
+{
+       width: 100%;
+       height: 100%;
+       font-family: Calibri;
+       background-image: linear-gradient(20deg, white, #DDE6DF 62.6%, #24507C);
+       background-repeat: no-repeat;
+       background-size: 100% 100%; 
+}
+
+h4 {
+    height:25px;
+    text-align: center;
+}
+
+.Logo {
+       width: 105.59px;
+       height: 103.64px;
+}
+
+.searchBox {
+}
+
+.textclass
+{
+       width: 46%;
+       font-family: "segoe ui";
+}
+       
+.tftextbtn
+{
+       color: #fff;
+       display: inline-block;
+       background-color: rgb(230, 100, 0);
+    font-family: "segoe ui";
+}
+
+.PictureGallery {
+}
+
+.mainPic {
+}
+
+.thumbnailPic {
+}
+
+.CurrentTemperature {
+       vertical-align:middle;
+       text-align: center;
+       font-size: 75px;
+}
+
+.WeatherData {
+}
+
+.EventTitle {
+    font-size: large;
+}
+
+.EventDescription {
+    font-family:Helvetica;
+   word-wrap:break-word;
+   font-size:small;
+}
+
+a:link {text-decoration:none;}
+td 
+{
+       vertical-align:top;
+       valign:top;
+}
+
+.verticalLine {
+    border-left: thick solid #ff0000;
+}
+
+.wrap-words {
+    word-wrap:break-word;
+}
+
+.eventful-badge,
+.eventful-badge * {
+  margin: 0             !important;
+  padding: 0            !important;
+  border: 0             !important;
+  text-align: center    !important;
+  color: #CCC           !important;
+  font-family: Arial    !important;
+  text-decoration: none !important;
+}
+
+.eventful-medium {
+  position: relative    !important;
+  width: 100px          !important;
+  font-size: 12px       !important;
+  line-height: 13px     !important;
+}  
\ No newline at end of file
diff --git a/Release/samples/CasaLens/datafetcher.cpp b/Release/samples/CasaLens/datafetcher.cpp
new file mode 100644 (file)
index 0000000..604e5b3
--- /dev/null
@@ -0,0 +1,454 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * datafetcher.cpp: Listener code: Given a location/postal code, the listener queries different services
+ * for weather, things to do: events, movie and pictures and returns it to the client.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "casalens.h"
+
+using namespace utility;
+using namespace concurrency;
+using namespace concurrency::streams;
+
+using namespace web;
+using namespace web::http;
+using namespace web::http::client;
+using namespace web::http::experimental;
+using namespace web::http::experimental::listener;
+
+// In case of any failure to fetch data from a service,
+// create a json object with "error" key and value containing the exception details.
+pplx::task<json::value> CasaLens::handle_exception(pplx::task<json::value>& t, const utility::string_t& field_name)
+{
+    try
+    {
+        t.get();
+    }
+    catch (const std::exception& ex)
+    {
+        json::value error_json = json::value::object();
+        error_json[field_name] = json::value::object();
+        error_json[field_name][error_json_key] = json::value::string(utility::conversions::to_string_t(ex.what()));
+        return pplx::task_from_result<json::value>(error_json);
+    }
+
+    return t;
+}
+
+// Given postal code, query eventful service and obtain num_events upcoming events at that location.
+// Returns a task of json::value of event data
+// JSON result Format:
+// {"events":["title":"event1","url":"http://event1","starttime":"ddmmyy",..]}
+pplx::task<json::value> CasaLens::get_events(const utility::string_t& postal_code)
+{
+    uri_builder ub(casalens_creds::events_url + postal_code);
+
+    ub.append_query(casalens_creds::events_keyname, casalens_creds::events_key);
+    auto event_url = ub.to_string();
+
+    http_client ev_client(event_url);
+    return ev_client.request(methods::GET, event_url)
+        .then([](http_response resp) { return resp.extract_json(); })
+        .then([](json::value event_json) {
+            json::value event_result_node = json::value::object();
+
+            if (!event_json[U("events")][U("event")].is_null())
+            {
+                event_result_node[U("events")] = json::value::array();
+
+                int i = 0;
+                for (auto& iter : event_json[U("events")][U("event")].as_array())
+                {
+                    const auto& event = iter;
+                    auto iTitle = event.as_object().find(U("title"));
+                    if (iTitle == event.as_object().end())
+                    {
+                        throw web::json::json_exception(U("title key not found"));
+                    }
+                    event_result_node[events_json_key][i][U("title")] = iTitle->second;
+                    auto iUrl = event.as_object().find(U("url"));
+                    if (iUrl == event.as_object().end())
+                    {
+                        throw web::json::json_exception(U("url key not found"));
+                    }
+                    event_result_node[events_json_key][i][U("url")] = iUrl->second;
+                    auto iStartTime = event.as_object().find(U("start_time"));
+                    if (iStartTime == event.as_object().end())
+                    {
+                        throw web::json::json_exception(U("start_time key not found"));
+                    }
+                    event_result_node[events_json_key][i][U("starttime")] = iStartTime->second;
+                    auto iDescription = event.as_object().find(U("description"));
+                    if (iDescription == event.as_object().end())
+                    {
+                        throw web::json::json_exception(U("descriotion key not found"));
+                    }
+                    event_result_node[events_json_key][i][U("description")] = iDescription->second;
+                    auto iVenueAddress = event.as_object().find(U("venue_address"));
+                    if (iVenueAddress == event.as_object().end())
+                    {
+                        throw web::json::json_exception(U("venue_address key not found"));
+                    }
+                    auto iCityName = event.as_object().find(U("city_name"));
+                    if (iCityName == event.as_object().end())
+                    {
+                        throw web::json::json_exception(U("city_name key not found"));
+                    }
+                    event_result_node[events_json_key][i][U("venue_address")] =
+                        json::value::string(iVenueAddress->second.as_string() + U(" ") + iCityName->second.as_string());
+
+                    if (i++ > num_events) break;
+                }
+            }
+            else
+            {
+                // Event data is null, we hit an error.
+                event_result_node[events_json_key] = json::value::object();
+                event_result_node[events_json_key][error_json_key] = event_json[U("events")][U("description")];
+            }
+
+            return event_result_node;
+        })
+        .then([=](pplx::task<json::value> t) { return handle_exception(t, events_json_key); });
+}
+
+// Query openweathermap API and obtain weather information at the given location.
+// Returns a task of json::value containing the weather data.
+// Format:
+// {"weather":{"temperature":"49","pressure":"xxx"..}}
+pplx::task<json::value> CasaLens::get_weather(const utility::string_t& postal_code)
+{
+    utility::string_t weather_url(casalens_creds::weather_url);
+
+    uri_builder ub_weather(weather_url.append(postal_code));
+    ub_weather.append_query(U("units"), U("imperial"));
+
+    http_client weather_client(ub_weather.to_string());
+    return weather_client.request(methods::GET)
+        .then([](http_response resp) { return resp.extract_string(); })
+        .then([](utility::string_t weather_str) {
+            json::value weather_json = json::value::parse(weather_str);
+
+            auto& j = weather_json[U("list")][0][U("main")];
+
+            json::value weather_result_node = json::value::object();
+            weather_result_node[weather_json_key] = json::value::object();
+            auto& w = weather_result_node[U("weather")];
+            w[U("temperature")] = j[U("temp")];
+            w[U("pressure")] = j[U("pressure")];
+            w[U("temp_min")] = j[U("temp_min")];
+            w[U("temp_max")] = j[U("temp_max")];
+            w[U("image")] =
+                json::value::string(U("http://openweathermap.org/img/w/") +
+                                    weather_json[U("list")][0][U("weather")][0][U("icon")].as_string() + U(".png"));
+            w[U("description")] = weather_json[U("list")][0][U("weather")][0][U("description")];
+            return weather_result_node;
+        })
+        .then([=](pplx::task<json::value> t) { return handle_exception(t, weather_json_key); });
+}
+
+// Query bing images to fetch some image URLs of the given location
+// Returns a task of json::Value
+// JSON result format:
+// {"images":["url1", "url2"]}
+pplx::task<json::value> CasaLens::get_pictures(const utility::string_t& location, const utility::string_t& count)
+{
+    http_client_config config;
+    credentials cred(casalens_creds::images_keyname, casalens_creds::images_key);
+    config.set_credentials(cred);
+    utility::string_t bing_url(casalens_creds::images_url);
+    uri_builder ub_bing(bing_url);
+    ub_bing.append_query(U("Query"), U("'") + location + U("'"));
+    ub_bing.append_query(U("$top"), count);
+    ub_bing.append_query(U("ImageFilters"), U("'Size:Medium'"));
+
+    http_client bing_client(ub_bing.to_string(), config);
+    return bing_client.request(methods::GET)
+        .then([](http_response resp) { return resp.extract_json(); })
+        .then([](json::value image_json) {
+            json::value image_result_node = json::value::object();
+            image_result_node[images_json_key] = json::value::array();
+
+            int i = 0;
+            for (auto& val : image_json[U("d")][U("results")].as_object())
+            {
+                const json::object& image = val.second.as_object();
+                auto iMediaUrl = image.find(U("MediaUrl"));
+                if (iMediaUrl == image.end())
+                {
+                    throw web::json::json_exception(U("MediaUrl key not found"));
+                }
+                image_result_node[images_json_key][i] = iMediaUrl->second;
+                if (i++ > num_images) break;
+            }
+            return image_result_node;
+        })
+        .then([=](pplx::task<json::value> t) { return handle_exception(t, images_json_key); });
+}
+
+// Get the current date
+std::wstring CasaLens::get_date()
+{
+    time_t t = time(0);
+    struct tm now;
+    std::wostringstream date;
+    if (0 == localtime_s(&now, &t))
+    {
+        date << (now.tm_year + 1900) << '-' << (now.tm_mon + 1) << '-' << now.tm_mday;
+    }
+    return date.str();
+}
+
+// Query tmsapi and fetch current movie showtimes at local theaters, for the given postal code
+// Also quert bing images for movie posters
+// Returns a task of JSON value
+// JSON result format:
+// "movies":[{"title":"abc","theatre":[{"name":"theater1","datetime":["dd-mm-yyThh:mm"]},{"name":"theater2","datetime":["ddmmyy"]}],"poster":"img-url"}}]}..
+pplx::task<json::value> CasaLens::get_movies(const utility::string_t& postal_code)
+{
+    uri_builder ub_movie(casalens_creds::movies_url);
+
+    ub_movie.append_query(U("startDate"), get_date());
+    ub_movie.append_query(U("zip"), postal_code);
+    ub_movie.append_query(casalens_creds::movies_keyname, casalens_creds::movies_key);
+    ub_movie.append_query(U("imageSize"), U("Sm"));
+
+    http_client tms_client(ub_movie.to_string());
+    return tms_client.request(methods::GET)
+        .then([](http_response resp) { return resp.extract_json(); })
+        .then([](json::value movie_json) {
+            // From the data obtained from tmsapi, construct movie JSON value to be sent to the client
+            // We will collect data for 5 movies, and 5 showtime info per movie.
+            json::value movie_result_node = json::value::object();
+
+            if (movie_json.size())
+            {
+                auto temp = json::value::array();
+                int i = 0;
+                for (auto iter = movie_json.as_array().cbegin(); iter != movie_json.as_array().cend() && i < num_movies;
+                     iter++, i++)
+                {
+                    auto iShowTimes = iter->as_object().find(U("showtimes"));
+                    if (iShowTimes == iter->as_object().end())
+                    {
+                        throw web::json::json_exception(U("showtimes key not found"));
+                    }
+                    auto& showtimes = iShowTimes->second;
+
+                    int j = 0;
+                    int showtime_index = 0;
+                    int theater_index = -1;
+                    utility::string_t current_theater;
+                    auto iTitle = iter->as_object().find(U("title"));
+                    if (iTitle == iter->as_object().end())
+                    {
+                        throw web::json::json_exception(U("title key not found"));
+                    }
+                    temp[i][U("title")] = iTitle->second;
+                    for (auto iter2 = showtimes.as_array().cbegin();
+                         iter2 != showtimes.as_array().cend() && j < num_movies;
+                         iter2++, j++)
+                    {
+                        auto iTheatre = iter2->as_object().find(U("theatre"));
+                        if (iTheatre == iter2->as_object().end())
+                        {
+                            throw web::json::json_exception(U("theatre key not found"));
+                        }
+                        auto iName = iTheatre->second.as_object().find(U("name"));
+                        if (iName == iTheatre->second.as_object().end())
+                        {
+                            throw web::json::json_exception(U("name key not found"));
+                        }
+                        auto theater = iName->second.as_string();
+                        if (0 != theater.compare(current_theater)) // new theater
+                        {
+                            temp[i][U("theatre")][++theater_index][U("name")] =
+                                json::value::string(theater); // Add theater entry
+                            showtime_index = 0;
+                            current_theater = theater;
+                        }
+                        auto iDateTime = iter2->as_object().find(U("dateTime"));
+                        if (iDateTime == iter2->as_object().end())
+                        {
+                            throw web::json::json_exception(U("dateTime key not found"));
+                        }
+                        temp[i][U("theatre")][theater_index][U("datetime")][showtime_index++] =
+                            iDateTime->second; // Update the showtime for same theater
+                    }
+                }
+                movie_result_node[movies_json_key] = std::move(temp);
+            }
+            else
+            {
+                movie_result_node[movies_json_key] = json::value::object();
+                movie_result_node[movies_json_key][error_json_key] =
+                    json::value::string(U("Failed to fetch movie data"));
+            }
+
+            return pplx::task_from_result(movie_result_node);
+        })
+        .then([=](json::value movie_result) {
+            try
+            {
+                // For every movie, obtain movie poster URL from bing
+                std::vector<utility::string_t> movie_list;
+                std::vector<pplx::task<json::value>> poster_tasks;
+                auto date = get_date();
+                std::wstring year = date.substr(0, date.find(U("-")));
+
+                for (auto& iter : movie_result[movies_json_key].as_object())
+                {
+                    auto title = iter.second[U("title")].as_string();
+                    auto searchStr = title + U(" ") + year + U(" new movie poster");
+                    poster_tasks.push_back(get_pictures(searchStr, U("1")));
+                    movie_list.push_back(title);
+                }
+
+                pplx::when_all(poster_tasks.begin(), poster_tasks.end()).wait();
+                json::value resp_data = json::value::object();
+
+                for (unsigned int i = 0; i < poster_tasks.size(); i++)
+                {
+                    auto jval = poster_tasks[i].get();
+                    auto poster_url = jval.as_array().begin()[0];
+                    movie_result[movies_json_key][i][U("poster")] = poster_url;
+                }
+            }
+            catch (...)
+            {
+                // Ignore any failure in fetching movie posters. Just return
+                // the movie data to the client.
+            }
+            return pplx::task_from_result(movie_result);
+        })
+        .then([=](pplx::task<json::value> t) { return handle_exception(t, movies_json_key); });
+}
+
+bool CasaLens::is_number(const std::string& s)
+{
+    return !s.empty() &&
+           std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c, std::locale()); }) == s.end();
+}
+
+void CasaLens::fetch_data(http_request message, const std::wstring& postal_code, const std::wstring& location)
+{
+    json::value resp_data;
+
+    try
+    {
+        m_rwlock.lock_read();
+        resp_data = m_data[postal_code];
+        m_rwlock.unlock();
+
+        if (resp_data.is_null())
+        {
+            std::vector<pplx::task<json::value>> tasks;
+
+            tasks.push_back(get_events(postal_code));
+            tasks.push_back(get_weather(postal_code));
+            tasks.push_back(get_pictures(location, U("4")));
+            tasks.push_back(get_movies(postal_code));
+
+            pplx::when_all(tasks.begin(), tasks.end()).wait();
+            resp_data = json::value::object();
+
+            for (auto& iter : tasks)
+            {
+                auto jval = iter.get();
+                auto key = jval.as_object().begin()->first;
+                resp_data[key] = jval.as_object().begin()->second;
+            }
+
+            m_rwlock.lock();
+            m_data[postal_code] = resp_data;
+            m_rwlock.unlock();
+        }
+
+        // Reply with the aggregated JSON data
+        message.reply(status_codes::OK, resp_data).then([](pplx::task<void> t) { handle_error(t); });
+    }
+    catch (...)
+    {
+        message.reply(status_codes::InternalError).then([](pplx::task<void> t) { handle_error(t); });
+    }
+}
+
+// Check if the input text is a number or string.
+// If string => city name, use bing maps API to obtain the postal code for that city
+// number => postal code, use google maps API to obtain city name (location data) for that postal code.
+// then call fetch_data to query different services, aggregate movie, images, events, weather etc for that city and
+// respond to the request.
+void CasaLens::get_data(http_request message, const std::wstring& input_text)
+{
+    if (!is_number(utility::conversions::to_utf8string(input_text)))
+    {
+        std::wstring bing_maps_url(casalens_creds::bmaps_url);
+        uri_builder maps_builder;
+        maps_builder.append_query(U("locality"), input_text);
+        maps_builder.append_query(casalens_creds::bmaps_keyname, casalens_creds::bmaps_key);
+        auto s = maps_builder.to_string();
+        http_client bing_client(bing_maps_url);
+        bing_client.request(methods::GET, s)
+            .then([=](http_response resp) { return resp.extract_json(); })
+            .then([=](json::value maps_result) mutable {
+                auto coordinates = maps_result[U("resourceSets")][0][U("resources")][0][U("point")];
+                auto lattitude = coordinates[U("coordinates")][0].serialize();
+                auto longitude = coordinates[U("coordinates")][1].serialize();
+                uri_builder ub;
+                ub.append_path(lattitude + U(",") + longitude)
+                    .append_query(casalens_creds::bmaps_keyname, casalens_creds::bmaps_key);
+                auto s2 = ub.to_string();
+                return bing_client.request(methods::GET, s2);
+            })
+            .then([](http_response resp) { return resp.extract_json(); })
+            .then([=](json::value maps_result) {
+                auto postal_code =
+                    maps_result[U("resourceSets")][0][U("resources")][0][U("address")][U("postalCode")].as_string();
+                fetch_data(message, postal_code, input_text);
+            })
+            .then([=](pplx::task<void> t) {
+                try
+                {
+                    t.get();
+                }
+                catch (...)
+                {
+                    message.reply(status_codes::InternalError, U("Failed to fetch the postal code"));
+                }
+            });
+    }
+    else // get location from postal code
+    {
+        http_client client(casalens_creds::gmaps_url);
+
+        uri_builder ub;
+        ub.append_query(U("address"), input_text);
+        ub.append_query(U("sensor"), U("false"));
+
+        client.request(methods::GET, ub.to_string())
+            .then([](http_response resp) { return resp.extract_json(); })
+            .then([=](json::value jval) {
+                auto locationstr = jval[U("results")][0][U("address_components")][1][U("long_name")].as_string();
+                fetch_data(message, input_text, locationstr);
+            })
+            .then([=](pplx::task<void> t) {
+                try
+                {
+                    t.get();
+                }
+                catch (...)
+                {
+                    message.reply(status_codes::InternalError, U("Failed to fetch the location from postal code"));
+                }
+            });
+    }
+    return;
+}
diff --git a/Release/samples/CasaLens/image/bing-logo.jpg b/Release/samples/CasaLens/image/bing-logo.jpg
new file mode 100644 (file)
index 0000000..22a2dbe
Binary files /dev/null and b/Release/samples/CasaLens/image/bing-logo.jpg differ
diff --git a/Release/samples/CasaLens/image/logo.png b/Release/samples/CasaLens/image/logo.png
new file mode 100644 (file)
index 0000000..1ba6835
Binary files /dev/null and b/Release/samples/CasaLens/image/logo.png differ
diff --git a/Release/samples/CasaLens/image/wall.jpg b/Release/samples/CasaLens/image/wall.jpg
new file mode 100644 (file)
index 0000000..ccd15ff
Binary files /dev/null and b/Release/samples/CasaLens/image/wall.jpg differ
diff --git a/Release/samples/CasaLens/js/default.js b/Release/samples/CasaLens/js/default.js
new file mode 100644 (file)
index 0000000..b7e1ea2
--- /dev/null
@@ -0,0 +1,173 @@
+ļ»æfunction formatAMPM(date) {
+    var hours = date.getHours();
+    var minutes = date.getMinutes();
+    var ampm = hours >= 12 ? 'pm' : 'am';
+    hours = hours % 12;
+    hours = hours ? hours : 12; // the hour '0' should be '12'
+    minutes = minutes < 10 ? '0' + minutes : minutes;
+    var strTime = hours + ':' + minutes + ' ' + ampm;
+    return strTime;
+}
+
+function formatEventTime(dateTimeStr) {
+    dateTimeStr = dateTimeStr.substr(0, dateTimeStr.lastIndexOf(":")).split(" ").join("T")
+    var dateObj = new Date(dateTimeStr);
+    if (!dateObj) {
+        return "";
+    }
+    var dateStr = dateObj.toDateString();
+    dateStr = dateStr.substring(0, dateStr.length - 5);
+    return dateStr + "<br/>" + formatAMPM(dateObj);
+}
+
+function ClearText() {
+    location_ip.value = "";
+}
+
+function TrimText(text, maxLength) {
+    if (text.length < maxLength) {
+        return text;
+    } else {
+        var i = maxLength;
+        while (i < text.length) {
+
+            if (text[i] == " " || text[i] == "," || text[i] == "." || text[i] == "\r" || text[i] == "\n") {
+                break;
+            }
+            i++;
+        }
+        return text.substr(0, i);
+    }
+}
+
+function populateEvents(events) {
+
+    var tempDiv = document.createElement("div");
+
+    var currentChildNodes = eventsTable.childNodes;
+    var defaultChildrenCount = 2;
+    for (var i = defaultChildrenCount; i < currentChildNodes.length; i++) {
+        currentChildNodes[i].parentNode.removeChild(currentChildNodes[i]);
+    }
+    for (var i = 0; i < events.length; i++) {
+        var dataObj = events[i];
+
+        tempDiv.innerHTML = dataObj.description;
+        var description = TrimText(tempDiv.innerText, 150);
+        var innerHtml = eventTemplate.innerHTML;
+        innerHtml = innerHtml.replace("%eventtitle%", TrimText(dataObj.title, 30));
+        innerHtml = innerHtml.replace("%eventstarttime%", formatEventTime(dataObj.starttime));
+        innerHtml = innerHtml.replace("%eventdescription%", description);
+        innerHtml = innerHtml.replace("%eventlink1%", dataObj.url);
+        innerHtml = innerHtml.replace("%eventlink2%", dataObj.url);
+        innerHtml = innerHtml.replace("%eventvenue%", dataObj.venue_address);
+        var obj = document.createElement("tr");
+        obj.innerHTML = window.toStaticHTML(innerHtml);
+
+        eventsTable.appendChild(obj);
+    }
+}
+
+function populateWeather(weather) {
+
+    var currentChildNodes = weatherData.childNodes;
+    var defaultChildrenCount = 5;
+    for (var i = defaultChildrenCount; i < currentChildNodes.length; i++) {
+        currentChildNodes[i].parentNode.removeChild(currentChildNodes[i]);
+    }
+
+    var weatherElement = weatherTemplate.cloneNode(true);
+    weatherElement.style.display = "inherit";
+    var innerHtml = weatherElement.innerHTML;
+    innerHtml = innerHtml.replace("%wt%", weather.temperature);
+    innerHtml = innerHtml.replace("%wp%", weather.pressure);
+    innerHtml = innerHtml.replace("%wtmin%", weather.temp_min);
+    innerHtml = innerHtml.replace("%wtmax%", weather.temp_max);
+    innerHtml = innerHtml.replace("%wdesc%", weather.description);
+    innerHtml = innerHtml.replace("%wimg%", weather.image);
+    weatherElement.innerHTML = window.toStaticHTML(innerHtml);
+    weatherData.appendChild(weatherElement);
+}
+
+function populateMovies(movies) {
+
+    var currentChildNodes = movieTemplateToHide.childNodes;
+    var defaultChildrenCount = 3;
+    for (var i = defaultChildrenCount; i < currentChildNodes.length; i++) {
+        currentChildNodes[i].parentNode.removeChild(currentChildNodes[i]);
+    }
+
+    var showtimes = "";
+    for (var i = 0; i < movies.length; i++) {
+        var innerHtml = movieTemplate.innerHTML;
+        var dataObj = movies[i];
+
+        var theatrename;
+        for (var j = 0; j < dataObj.theatre.length; j++) {
+
+            showtimes += dataObj.theatre[j].name;
+            showtimes += "<br/>";
+            var showtimelist = dataObj.theatre[j].datetime;
+            for (var k = 0; k < showtimelist.length; k++) {
+                var dateObj = new Date(showtimelist[k]);
+                if (dateObj) {
+                    showtimes += formatAMPM(dateObj) + " | ";
+                }
+            }
+            showtimes = showtimes.substr(0, showtimes.length - 2);
+            showtimes += "<br/><br/>";
+        }
+        innerHtml = innerHtml.replace("%mtitle%", dataObj.title);
+        innerHtml = innerHtml.replace("%mtheater%", showtimes);
+        innerHtml = innerHtml.replace("%mposter%", dataObj.poster);
+
+        var obj = document.createElement("div");
+        obj.innerHTML = window.toStaticHTML(innerHtml);
+        movieTemplateToHide.appendChild(obj);
+    }
+}
+
+function populateImages(images) {
+    if (images.length == 4) {
+        var mainimg = document.getElementById('mainPic1');
+        mainimg.src = images[0];
+
+        var img1 = document.getElementById('thumbnailPic1');
+        img1.src = images[1];
+
+        var img2 = document.getElementById('thumbnailPic2');
+        img2.src = images[2];
+
+        var img3 = document.getElementById('thumbnailPic3');
+        img3.src = images[3];
+    }
+}
+
+function FetchData() {
+    var xhr = new XMLHttpRequest();
+    xhr.onreadystatechange = function (e) {
+        // The request finished and response is ready
+        if (xhr.readyState == 4) {
+            var searchbar = document.getElementById('searchBar');
+            searchbar.style.display = 'none';
+
+            if (xhr.status == 200) {
+                var replydata = JSON.parse(xhr.responseText);
+                populateImages(replydata.images);
+                populateEvents(replydata.events);
+                populateMovies(replydata.movies);
+                populateWeather(replydata.weather);
+
+                defaultData.style.display = "none";
+                cityData.style.display = 'inherit';
+            }
+            else {
+                defaultData.style.display = "none";
+                errorData.style.display = 'inherit';
+            }
+        }
+    }
+    xhr.open("POST", document.URL, true);
+    xhr.setRequestHeader("Content-type", 'text/html');
+    xhr.send(location_ip.value);
+}
diff --git a/Release/samples/CasaLens/stdafx.cpp b/Release/samples/CasaLens/stdafx.cpp
new file mode 100644 (file)
index 0000000..abaab74
--- /dev/null
@@ -0,0 +1,12 @@
+/***
+ * 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 "stdafx.h"
diff --git a/Release/samples/CasaLens/stdafx.h b/Release/samples/CasaLens/stdafx.h
new file mode 100644 (file)
index 0000000..99ebd10
--- /dev/null
@@ -0,0 +1,22 @@
+/***
+ * 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 : include file for standard system include files,
+ * or project specific include files that are used frequently, but
+ * are changed infrequently
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/http_client.h"
+#include <exception>
+#include <map>
+#include <stdio.h>
+#include <string>
+#include <tchar.h>
+#include <vector>
diff --git a/Release/samples/FacebookDemo/App.xaml b/Release/samples/FacebookDemo/App.xaml
new file mode 100644 (file)
index 0000000..d68af66
--- /dev/null
@@ -0,0 +1,29 @@
+<!--
+ Copyright (C) Microsoft. All rights reserved.
+ Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+
+ =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ App.xaml - Default application xaml file from blank project.
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+-->
+
+<Application
+    x:Class="FacebookDemo.App"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:FacebookDemo">
+
+    <Application.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+
+                <!--
+                    Styles that define common aspects of the platform look and feel
+                    Required by Visual Studio project and item templates
+                 -->
+                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
+            </ResourceDictionary.MergedDictionaries>
+
+        </ResourceDictionary>
+    </Application.Resources>
+</Application>
diff --git a/Release/samples/FacebookDemo/App.xaml.cpp b/Release/samples/FacebookDemo/App.xaml.cpp
new file mode 100644 (file)
index 0000000..c458f90
--- /dev/null
@@ -0,0 +1,112 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * App.xaml.cpp - Implementation of the App class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "pch.h"
+
+#include "MainPage.xaml.h"
+
+using namespace FacebookDemo;
+
+using namespace Platform;
+using namespace Windows::ApplicationModel;
+using namespace Windows::ApplicationModel::Activation;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Interop;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227
+
+/// <summary>
+/// Initializes the singleton application object.  This is the first line of authored code
+/// executed, and as such is the logical equivalent of main() or WinMain().
+/// </summary>
+App::App()
+{
+    InitializeComponent();
+    Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);
+}
+
+/// <summary>
+/// Invoked when the application is launched normally by the end user.  Other entry points
+/// will be used when the application is launched to open a specific file, to display
+/// search results, and so forth.
+/// </summary>
+/// <param name="args">Details about the launch request and process.</param>
+void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^ args)
+{
+    auto rootFrame = dynamic_cast<Frame ^>(Window::Current->Content);
+
+    // Do not repeat app initialization when the Window already has content,
+    // just ensure that the window is active
+    if (rootFrame == nullptr)
+    {
+        // Create a Frame to act as the navigation context and associate it with
+        // a SuspensionManager key
+        rootFrame = ref new Frame();
+
+        if (args->PreviousExecutionState == ApplicationExecutionState::Terminated)
+        {
+            // TODO: Restore the saved session state only when appropriate, scheduling the
+            // final launch steps after the restore is complete
+        }
+
+        if (rootFrame->Content == nullptr)
+        {
+            // When the navigation stack isn't restored navigate to the first page,
+            // configuring the new page by passing required information as a navigation
+            // parameter
+            if (!rootFrame->Navigate(TypeName(MainPage::typeid), args->Arguments))
+            {
+                throw ref new FailureException("Failed to create initial page");
+            }
+        }
+        // Place the frame in the current Window
+        Window::Current->Content = rootFrame;
+        // Ensure the current window is active
+        Window::Current->Activate();
+    }
+    else
+    {
+        if (rootFrame->Content == nullptr)
+        {
+            // When the navigation stack isn't restored navigate to the first page,
+            // configuring the new page by passing required information as a navigation
+            // parameter
+            if (!rootFrame->Navigate(TypeName(MainPage::typeid), args->Arguments))
+            {
+                throw ref new FailureException("Failed to create initial page");
+            }
+        }
+        // Ensure the current window is active
+        Window::Current->Activate();
+    }
+}
+
+/// <summary>
+/// Invoked when application execution is being suspended.  Application state is saved
+/// without knowing whether the application will be terminated or resumed with the contents
+/// of memory still intact.
+/// </summary>
+/// <param name="sender">The source of the suspend request.</param>
+/// <param name="e">Details about the suspend request.</param>
+void App::OnSuspending(Object ^ sender, SuspendingEventArgs ^ e)
+{
+    (void)sender; // Unused parameter
+    (void)e;      // Unused parameter
+
+    // TODO: Save application state and stop any background activity
+}
diff --git a/Release/samples/FacebookDemo/App.xaml.h b/Release/samples/FacebookDemo/App.xaml.h
new file mode 100644 (file)
index 0000000..45afa68
--- /dev/null
@@ -0,0 +1,29 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * App.xaml.h - Declaration of the App class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "App.g.h"
+
+namespace FacebookDemo
+{
+/// <summary>
+/// Provides application-specific behavior to supplement the default Application class.
+/// </summary>
+ref class App sealed
+{
+public:
+    App();
+    virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^ args) override;
+
+private:
+    void OnSuspending(Platform::Object ^ sender, Windows::ApplicationModel::SuspendingEventArgs ^ e);
+};
+} // namespace FacebookDemo
diff --git a/Release/samples/FacebookDemo/Assets/Logo.png b/Release/samples/FacebookDemo/Assets/Logo.png
new file mode 100644 (file)
index 0000000..e26771c
Binary files /dev/null and b/Release/samples/FacebookDemo/Assets/Logo.png differ
diff --git a/Release/samples/FacebookDemo/Assets/SmallLogo.png b/Release/samples/FacebookDemo/Assets/SmallLogo.png
new file mode 100644 (file)
index 0000000..1eb0d9d
Binary files /dev/null and b/Release/samples/FacebookDemo/Assets/SmallLogo.png differ
diff --git a/Release/samples/FacebookDemo/Assets/SplashScreen.png b/Release/samples/FacebookDemo/Assets/SplashScreen.png
new file mode 100644 (file)
index 0000000..c951e03
Binary files /dev/null and b/Release/samples/FacebookDemo/Assets/SplashScreen.png differ
diff --git a/Release/samples/FacebookDemo/Assets/StoreLogo.png b/Release/samples/FacebookDemo/Assets/StoreLogo.png
new file mode 100644 (file)
index 0000000..dcb6727
Binary files /dev/null and b/Release/samples/FacebookDemo/Assets/StoreLogo.png differ
diff --git a/Release/samples/FacebookDemo/Common/StandardStyles.xaml b/Release/samples/FacebookDemo/Common/StandardStyles.xaml
new file mode 100644 (file)
index 0000000..2bde471
--- /dev/null
@@ -0,0 +1,1836 @@
+<!--
+ Copyright (C) Microsoft. All rights reserved.
+ Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+
+ =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ StandardStyles.xaml
+
+    This file contains XAML styles that simplify application development.
+
+    These are not merely convenient, but are required by most Visual Studio project and item templates.
+    Removing, renaming, or otherwise modifying the content of these files may result in a project that
+    does not build, or that will not build once additional pages are added.  If variations on these
+    styles are desired it is recommended that you copy the content under a new name and modify your
+    private copy.
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+-->
+
+<ResourceDictionary
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+
+    <!-- Non-brush values that vary across themes -->
+
+    <ResourceDictionary.ThemeDictionaries>
+        <ResourceDictionary x:Key="Default">
+            <x:String x:Key="BackButtonGlyph">&#xE071;</x:String>
+            <x:String x:Key="BackButtonSnappedGlyph">&#xE0BA;</x:String>
+        </ResourceDictionary>
+
+        <ResourceDictionary x:Key="HighContrast">
+            <x:String x:Key="BackButtonGlyph">&#xE071;</x:String>
+            <x:String x:Key="BackButtonSnappedGlyph">&#xE0C4;</x:String>
+        </ResourceDictionary>
+    </ResourceDictionary.ThemeDictionaries>
+
+    <x:String x:Key="ChevronGlyph">&#xE26B;</x:String>
+
+    <!-- RichTextBlock styles -->
+
+    <Style x:Key="BasicRichTextStyle" TargetType="RichTextBlock">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationForegroundThemeBrush}"/>
+        <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>
+        <Setter Property="TextWrapping" Value="Wrap"/>
+        <Setter Property="Typography.StylisticSet20" Value="True"/>
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>
+    </Style>
+
+    <Style x:Key="BaselineRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BasicRichTextStyle}">
+        <Setter Property="LineHeight" Value="20"/>
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
+        <!-- Properly align text along its baseline -->
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="4"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="ItemRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaselineRichTextStyle}"/>
+
+    <Style x:Key="BodyRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaselineRichTextStyle}">
+        <Setter Property="FontWeight" Value="SemiLight"/>
+    </Style>
+
+    <!-- TextBlock styles -->
+
+    <Style x:Key="BasicTextStyle" TargetType="TextBlock">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationForegroundThemeBrush}"/>
+        <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>
+        <Setter Property="TextWrapping" Value="Wrap"/>
+        <Setter Property="Typography.StylisticSet20" Value="True"/>
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>
+    </Style>
+
+    <Style x:Key="BaselineTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BasicTextStyle}">
+        <Setter Property="LineHeight" Value="20"/>
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
+        <!-- Properly align text along its baseline -->
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="4"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="HeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontSize" Value="56"/>
+        <Setter Property="FontWeight" Value="Light"/>
+        <Setter Property="LineHeight" Value="40"/>
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-2" Y="8"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="SubheaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontSize" Value="26.667"/>
+        <Setter Property="FontWeight" Value="Light"/>
+        <Setter Property="LineHeight" Value="30"/>
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="6"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="TitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontWeight" Value="SemiBold"/>
+    </Style>
+
+    <Style x:Key="SubtitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontWeight" Value="Normal"/>
+    </Style>
+
+    <Style x:Key="ItemTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}"/>
+
+    <Style x:Key="BodyTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontWeight" Value="SemiLight"/>
+    </Style>
+
+    <Style x:Key="CaptionTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontSize" Value="12"/>
+        <Setter Property="Foreground" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>
+    </Style>
+
+    <Style x:Key="GroupHeaderTextStyle" TargetType="TextBlock">
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>
+        <Setter Property="TextWrapping" Value="NoWrap"/>
+        <Setter Property="Typography.StylisticSet20" Value="True"/>
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>
+        <Setter Property="FontSize" Value="26.667"/>
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
+        <Setter Property="FontWeight" Value="Light"/>
+        <Setter Property="LineHeight" Value="30"/>
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="6"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!-- Button styles -->
+
+    <!--
+        TextButtonStyle is used to style a Button using subheader-styled text with no other adornment.  There
+        are two styles that are based on TextButtonStyle (TextPrimaryButtonStyle and TextSecondaryButtonStyle)
+        which are used in the GroupedItemsPage as a group header and in the FileOpenPickerPage for triggering
+        commands.
+    -->
+    <Style x:Key="TextButtonStyle" TargetType="ButtonBase">
+        <Setter Property="MinWidth" Value="0"/>
+        <Setter Property="MinHeight" Value="0"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="ButtonBase">
+                    <Grid Background="Transparent">
+                        <ContentPresenter x:Name="Text" Content="{TemplateBinding Content}" />
+                        <Rectangle
+                            x:Name="FocusVisualWhite"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="1.5"/>
+                        <Rectangle
+                            x:Name="FocusVisualBlack"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="0.5"/>
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal"/>
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetName="FocusVisualWhite" Storyboard.TargetProperty="Opacity"/>
+                                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetName="FocusVisualBlack" Storyboard.TargetProperty="Opacity"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused"/>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="CheckStates">
+                                <VisualState x:Name="Checked"/>
+                                <VisualState x:Name="Unchecked">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Indeterminate"/>
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="TextPrimaryButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource TextButtonStyle}">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationHeaderForegroundThemeBrush}"/>
+    </Style>
+
+    <Style x:Key="TextSecondaryButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource TextButtonStyle}">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>
+    </Style>
+
+    <!--
+        TextRadioButtonStyle is used to style a RadioButton using subheader-styled text with no other adornment.
+        This style is used in the SearchResultsPage to allow selection among filters.
+    -->
+    <Style x:Key="TextRadioButtonStyle" TargetType="RadioButton" BasedOn="{StaticResource TextButtonStyle}">
+        <Setter Property="Margin" Value="0,0,30,0"/>
+    </Style>
+
+    <!--
+        AppBarButtonStyle is used to style a Button (or ToggleButton) for use in an App Bar.  Content will be centered
+        and should fit within the 40 pixel radius glyph provided.  16-point Segoe UI Symbol is used for content text
+        to simplify the use of glyphs from that font.  AutomationProperties.Name is used for the text below the glyph.
+    -->
+    <Style x:Key="AppBarButtonStyle" TargetType="ButtonBase">
+        <Setter Property="Foreground" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+        <Setter Property="VerticalAlignment" Value="Stretch"/>
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
+        <Setter Property="FontWeight" Value="Normal"/>
+        <Setter Property="FontSize" Value="20"/>
+        <Setter Property="AutomationProperties.ItemType" Value="App Bar Button"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="ButtonBase">
+                    <Grid x:Name="RootGrid" Width="100" Background="Transparent">
+                        <StackPanel VerticalAlignment="Top" Margin="0,12,0,11">
+                            <Grid Width="40" Height="40" Margin="0,0,0,5" HorizontalAlignment="Center">
+                                <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0" Foreground="{StaticResource AppBarItemBackgroundThemeBrush}"/>
+                                <TextBlock x:Name="OutlineGlyph" Text="&#xE0A7;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0"/>
+                                <ContentPresenter x:Name="Content" HorizontalAlignment="Center" Margin="-1,-1,0,0" VerticalAlignment="Center"/>
+                            </Grid>
+                            <TextBlock
+                                x:Name="TextLabel"
+                                Text="{TemplateBinding AutomationProperties.Name}"
+                                Foreground="{StaticResource AppBarItemForegroundThemeBrush}"
+                                Margin="0,0,2,0"
+                                FontSize="12"
+                                TextAlignment="Center"
+                                Width="88"
+                                MaxHeight="32"
+                                TextTrimming="WordEllipsis"
+                                Style="{StaticResource BasicTextStyle}"/>
+                        </StackPanel>
+                        <Rectangle
+                                x:Name="FocusVisualWhite"
+                                IsHitTestVisible="False"
+                                Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                                StrokeEndLineCap="Square"
+                                StrokeDashArray="1,1"
+                                Opacity="0"
+                                StrokeDashOffset="1.5"/>
+                        <Rectangle
+                                x:Name="FocusVisualBlack"
+                                IsHitTestVisible="False"
+                                Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                                StrokeEndLineCap="Square"
+                                StrokeDashArray="1,1"
+                                Opacity="0"
+                                StrokeDashOffset="0.5"/>
+
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="ApplicationViewStates">
+                                <VisualState x:Name="FullScreenLandscape"/>
+                                <VisualState x:Name="Filled"/>
+                                <VisualState x:Name="FullScreenPortrait">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Width">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="60"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Snapped">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Width">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="60"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal"/>
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPointerOverBackgroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation
+                                                Storyboard.TargetName="FocusVisualWhite"
+                                                Storyboard.TargetProperty="Opacity"
+                                                To="1"
+                                                Duration="0"/>
+                                        <DoubleAnimation
+                                                Storyboard.TargetName="FocusVisualBlack"
+                                                Storyboard.TargetProperty="Opacity"
+                                                To="1"
+                                                Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused" />
+                                <VisualState x:Name="PointerFocused" />
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="CheckStates">
+                                <VisualState x:Name="Checked">
+                                    <Storyboard>
+                                        <DoubleAnimation Duration="0" To="0" Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Opacity"/>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundCheckedGlyph" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unchecked"/>
+                                <VisualState x:Name="Indeterminate"/>
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!--
+        Standard AppBarButton Styles for use with Button and ToggleButton
+
+        An AppBarButton Style is provided for each of the glyphs in the Segoe UI Symbol font.
+        Uncomment any style you reference (as not all may be required).
+    -->
+
+    <!--
+
+    <Style x:Key="SkipBackAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SkipBackAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Skip Back"/>
+        <Setter Property="Content" Value="&#xE100;"/>
+    </Style>
+    <Style x:Key="SkipAheadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SkipAheadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Skip Ahead"/>
+        <Setter Property="Content" Value="&#xE101;"/>
+    </Style>
+    <Style x:Key="PlayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PlayAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Play"/>
+        <Setter Property="Content" Value="&#xE102;"/>
+    </Style>
+    <Style x:Key="PauseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PauseAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Pause"/>
+        <Setter Property="Content" Value="&#xE103;"/>
+    </Style>
+    <Style x:Key="EditAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="EditAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Edit"/>
+        <Setter Property="Content" Value="&#xE104;"/>
+    </Style>
+    <Style x:Key="SaveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SaveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Save"/>
+        <Setter Property="Content" Value="&#xE105;"/>
+    </Style>
+    <Style x:Key="DeleteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DeleteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Delete"/>
+        <Setter Property="Content" Value="&#xE106;"/>
+    </Style>
+    <Style x:Key="DiscardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DiscardAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Discard"/>
+        <Setter Property="Content" Value="&#xE107;"/>
+    </Style>
+    <Style x:Key="RemoveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RemoveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Remove"/>
+        <Setter Property="Content" Value="&#xE108;"/>
+    </Style>
+    <Style x:Key="AddAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AddAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Add"/>
+        <Setter Property="Content" Value="&#xE109;"/>
+    </Style>
+    <Style x:Key="NoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="No"/>
+        <Setter Property="Content" Value="&#xE10A;"/>
+    </Style>
+    <Style x:Key="YesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="YesAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Yes"/>
+        <Setter Property="Content" Value="&#xE10B;"/>
+    </Style>
+    <Style x:Key="MoreAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MoreAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="More"/>
+        <Setter Property="Content" Value="&#xE10C;"/>
+    </Style>
+    <Style x:Key="RedoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RedoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Redo"/>
+        <Setter Property="Content" Value="&#xE10D;"/>
+    </Style>
+    <Style x:Key="UndoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UndoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Undo"/>
+        <Setter Property="Content" Value="&#xE10E;"/>
+    </Style>
+    <Style x:Key="HomeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HomeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Home"/>
+        <Setter Property="Content" Value="&#xE10F;"/>
+    </Style>
+    <Style x:Key="OutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OutAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Out"/>
+        <Setter Property="Content" Value="&#xE110;"/>
+    </Style>
+    <Style x:Key="NextAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NextAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Next"/>
+        <Setter Property="Content" Value="&#xE111;"/>
+    </Style>
+    <Style x:Key="PreviousAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PreviousAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Previous"/>
+        <Setter Property="Content" Value="&#xE112;"/>
+    </Style>
+    <Style x:Key="FavoriteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FavoriteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Favorite"/>
+        <Setter Property="Content" Value="&#xE113;"/>
+    </Style>
+    <Style x:Key="PhotoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PhotoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Photo"/>
+        <Setter Property="Content" Value="&#xE114;"/>
+    </Style>
+    <Style x:Key="SettingsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SettingsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Settings"/>
+        <Setter Property="Content" Value="&#xE115;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="VideoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="VideoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Video"/>
+        <Setter Property="Content" Value="&#xE116;"/>
+    </Style>
+    <Style x:Key="RefreshAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RefreshAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Refresh"/>
+        <Setter Property="Content" Value="&#xE117;"/>
+    </Style>
+    <Style x:Key="DownloadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DownloadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Download"/>
+        <Setter Property="Content" Value="&#xE118;"/>
+    </Style>
+    <Style x:Key="MailAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Mail"/>
+        <Setter Property="Content" Value="&#xE119;"/>
+    </Style>
+    <Style x:Key="SearchAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SearchAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Search"/>
+        <Setter Property="Content" Value="&#xE11A;"/>
+    </Style>
+    <Style x:Key="HelpAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HelpAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Help"/>
+        <Setter Property="Content" Value="&#xE11B;"/>
+    </Style>
+    <Style x:Key="UploadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UploadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Upload"/>
+        <Setter Property="Content" Value="&#xE11C;"/>
+    </Style>
+    <Style x:Key="EmojiAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="EmojiAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Emoji"/>
+        <Setter Property="Content" Value="&#xE11D;"/>
+    </Style>
+    <Style x:Key="TwoPageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TwoPageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Two Page"/>
+        <Setter Property="Content" Value="&#xE11E;"/>
+    </Style>
+    <Style x:Key="LeaveChatAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LeaveChatAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Upload"/>
+        <Setter Property="Content" Value="&#xE11F;"/>
+    </Style>
+    <Style x:Key="MailForwardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailForwardAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Forward Mail"/>
+        <Setter Property="Content" Value="&#xE120;"/>
+    </Style>
+    <Style x:Key="ClockAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ClockAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Clock"/>
+        <Setter Property="Content" Value="&#xE121;"/>
+    </Style>
+    <Style x:Key="SendAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SendAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Send"/>
+        <Setter Property="Content" Value="&#xE122;"/>
+    </Style>
+    <Style x:Key="CropAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CropAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Crop"/>
+        <Setter Property="Content" Value="&#xE123;"/>
+    </Style>
+    <Style x:Key="RotateCameraAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RotateCameraAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Rotate Camera"/>
+        <Setter Property="Content" Value="&#xE124;"/>
+    </Style>
+    <Style x:Key="PeopleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PeopleAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="People"/>
+        <Setter Property="Content" Value="&#xE125;"/>
+    </Style>
+    <Style x:Key="ClosePaneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ClosePaneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Close Pane"/>
+        <Setter Property="Content" Value="&#xE126;"/>
+    </Style>
+    <Style x:Key="OpenPaneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenPaneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open Pane"/>
+        <Setter Property="Content" Value="&#xE127;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="WorldAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="WorldAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="World"/>
+        <Setter Property="Content" Value="&#xE128;"/>
+    </Style>
+    <Style x:Key="FlagAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FlagAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Flag"/>
+        <Setter Property="Content" Value="&#xE129;"/>
+    </Style>
+    <Style x:Key="PreviewLinkAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PreviewLinkAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Preview Link"/>
+        <Setter Property="Content" Value="&#xE12A;"/>
+    </Style>
+    <Style x:Key="GlobeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GlobeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Globe"/>
+        <Setter Property="Content" Value="&#xE12B;"/>
+    </Style>
+    <Style x:Key="TrimAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TrimAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Trim"/>
+        <Setter Property="Content" Value="&#xE12C;"/>
+    </Style>
+    <Style x:Key="AttachCameraAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AttachCameraAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Attach Camera"/>
+        <Setter Property="Content" Value="&#xE12D;"/>
+    </Style>
+    <Style x:Key="ZoomInAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomInAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zoom In"/>
+        <Setter Property="Content" Value="&#xE12E;"/>
+    </Style>
+    <Style x:Key="BookmarksAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BookmarksAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Bookmarks"/>
+        <Setter Property="Content" Value="&#xE12F;"/>
+    </Style>
+    <Style x:Key="DocumentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DocumentAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Document"/>
+        <Setter Property="Content" Value="&#xE130;"/>
+    </Style>
+    <Style x:Key="ProtectedDocumentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ProtectedDocumentAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Protected Document"/>
+        <Setter Property="Content" Value="&#xE131;"/>
+    </Style>
+    <Style x:Key="PageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Page"/>
+        <Setter Property="Content" Value="&#xE132;"/>
+    </Style>
+    <Style x:Key="BulletsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BulletsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Bullets"/>
+        <Setter Property="Content" Value="&#xE133;"/>
+    </Style>
+    <Style x:Key="CommentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CommentAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Comment"/>
+        <Setter Property="Content" Value="&#xE134;"/>
+    </Style>
+    <Style x:Key="Mail2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Mail2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Mail2"/>
+        <Setter Property="Content" Value="&#xE135;"/>
+    </Style>
+    <Style x:Key="ContactInfoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactInfoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Contact Info"/>
+        <Setter Property="Content" Value="&#xE136;"/>
+    </Style>
+    <Style x:Key="HangUpAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HangUpAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Hang Up"/>
+        <Setter Property="Content" Value="&#xE137;"/>
+    </Style>
+    <Style x:Key="ViewAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ViewAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="View All"/>
+        <Setter Property="Content" Value="&#xE138;"/>
+    </Style>
+    <Style x:Key="MapPinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MapPinAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Map Pin"/>
+        <Setter Property="Content" Value="&#xE139;"/>
+    </Style>
+    <Style x:Key="PhoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PhoneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Phone"/>
+        <Setter Property="Content" Value="&#xE13A;"/>
+    </Style>
+    <Style x:Key="VideoChatAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="VideoChatAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Video Chat"/>
+        <Setter Property="Content" Value="&#xE13B;"/>
+    </Style>
+    <Style x:Key="SwitchAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SwitchAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Switch"/>
+        <Setter Property="Content" Value="&#xE13C;"/>
+    </Style>
+    <Style x:Key="ContactAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Contact"/>
+        <Setter Property="Content" Value="&#xE13D;"/>
+    </Style>
+
+    -->
+
+    <!--
+
+    <Style x:Key="RenameAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RenameAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Rename"/>
+        <Setter Property="Content" Value="&#xE13E;"/>
+    </Style>
+    <Style x:Key="PinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PinAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Pin"/>
+        <Setter Property="Content" Value="&#xE141;"/>
+    </Style>
+    <Style x:Key="MusicInfoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MusicInfoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Music Info"/>
+        <Setter Property="Content" Value="&#xE142;"/>
+    </Style>
+    <Style x:Key="GoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Go"/>
+        <Setter Property="Content" Value="&#xE143;"/>
+    </Style>
+    <Style x:Key="KeyboardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="KeyboardAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Keyboard"/>
+        <Setter Property="Content" Value="&#xE144;"/>
+    </Style>
+    <Style x:Key="DockLeftAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DockLeftAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dock Left"/>
+        <Setter Property="Content" Value="&#xE145;"/>
+    </Style>
+    <Style x:Key="DockRightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DockRightAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dock Right"/>
+        <Setter Property="Content" Value="&#xE146;"/>
+    </Style>
+    <Style x:Key="DockBottomAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DockBottomAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dock Bottom"/>
+        <Setter Property="Content" Value="&#xE147;"/>
+    </Style>
+    <Style x:Key="RemoteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RemoteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Remote"/>
+        <Setter Property="Content" Value="&#xE148;"/>
+    </Style>
+    <Style x:Key="SyncAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SyncAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Sync"/>
+        <Setter Property="Content" Value="&#xE149;"/>
+    </Style>
+    <Style x:Key="RotateAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RotateAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Rotate"/>
+        <Setter Property="Content" Value="&#xE14A;"/>
+    </Style>
+    <Style x:Key="ShuffleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShuffleAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Shuffle"/>
+        <Setter Property="Content" Value="&#xE14B;"/>
+    </Style>
+    <Style x:Key="ListAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ListAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="List"/>
+        <Setter Property="Content" Value="&#xE14C;"/>
+    </Style>
+    <Style x:Key="ShopAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShopAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Shop"/>
+        <Setter Property="Content" Value="&#xE14D;"/>
+    </Style>
+    <Style x:Key="SelectAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SelectAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Select All"/>
+        <Setter Property="Content" Value="&#xE14E;"/>
+    </Style>
+    <Style x:Key="OrientationAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OrientationAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Orientation"/>
+        <Setter Property="Content" Value="&#xE14F;"/>
+    </Style>
+    <Style x:Key="ImportAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Import"/>
+        <Setter Property="Content" Value="&#xE150;"/>
+    </Style>
+    <Style x:Key="ImportAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Import All"/>
+        <Setter Property="Content" Value="&#xE151;"/>
+    </Style>
+    <Style x:Key="BrowsePhotosAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BrowsePhotosAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Browse Photos"/>
+        <Setter Property="Content" Value="&#xE155;"/>
+    </Style>
+    <Style x:Key="WebcamAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="WebcamAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Webcam"/>
+        <Setter Property="Content" Value="&#xE156;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="PicturesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PicturesAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Pictures"/>
+        <Setter Property="Content" Value="&#xE158;"/>
+    </Style>
+    <Style x:Key="SaveLocalAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SaveLocalAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Save Local"/>
+        <Setter Property="Content" Value="&#xE159;"/>
+    </Style>
+    <Style x:Key="CaptionAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CaptionAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Caption"/>
+        <Setter Property="Content" Value="&#xE15A;"/>
+    </Style>
+    <Style x:Key="StopAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="StopAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Stop"/>
+        <Setter Property="Content" Value="&#xE15B;"/>
+    </Style>
+    <Style x:Key="ShowResultsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShowResultsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Show Results"/>
+        <Setter Property="Content" Value="&#xE15C;"/>
+    </Style>
+    <Style x:Key="VolumeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="VolumeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Volume"/>
+        <Setter Property="Content" Value="&#xE15D;"/>
+    </Style>
+    <Style x:Key="RepairAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RepairAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Repair"/>
+        <Setter Property="Content" Value="&#xE15E;"/>
+    </Style>
+    <Style x:Key="MessageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MessageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Message"/>
+        <Setter Property="Content" Value="&#xE15F;"/>
+    </Style>
+    <Style x:Key="Page2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Page2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Page2"/>
+        <Setter Property="Content" Value="&#xE160;"/>
+    </Style>
+    <Style x:Key="CalendarDayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarDayAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Day"/>
+        <Setter Property="Content" Value="&#xE161;"/>
+    </Style>
+    <Style x:Key="CalendarWeekAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarWeekAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Week"/>
+        <Setter Property="Content" Value="&#xE162;"/>
+    </Style>
+    <Style x:Key="CalendarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Calendar"/>
+        <Setter Property="Content" Value="&#xE163;"/>
+    </Style>
+    <Style x:Key="CharactersAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CharactersAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Characters"/>
+        <Setter Property="Content" Value="&#xE164;"/>
+    </Style>
+    <Style x:Key="MailReplyAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailReplyAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Reply All"/>
+        <Setter Property="Content" Value="&#xE165;"/>
+    </Style>
+    <Style x:Key="ReadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ReadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Read"/>
+        <Setter Property="Content" Value="&#xE166;"/>
+    </Style>
+    <Style x:Key="LinkAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LinkAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Link"/>
+        <Setter Property="Content" Value="&#xE167;"/>
+    </Style>
+    <Style x:Key="AccountsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AccountsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Accounts"/>
+        <Setter Property="Content" Value="&#xE168;"/>
+    </Style>
+    <Style x:Key="ShowBccAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShowBccAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Show Bcc"/>
+        <Setter Property="Content" Value="&#xE169;"/>
+    </Style>
+    <Style x:Key="HideBccAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HideBccAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Hide Bcc"/>
+        <Setter Property="Content" Value="&#xE16A;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="CutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CutAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Cut"/>
+        <Setter Property="Content" Value="&#xE16B;"/>
+    </Style>
+    <Style x:Key="AttachAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AttachAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Attach"/>
+        <Setter Property="Content" Value="&#xE16C;"/>
+    </Style>
+    <Style x:Key="PasteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PasteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Paste"/>
+        <Setter Property="Content" Value="&#xE16D;"/>
+    </Style>
+    <Style x:Key="FilterAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FilterAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Filter"/>
+        <Setter Property="Content" Value="&#xE16E;"/>
+    </Style>
+    <Style x:Key="CopyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CopyAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Copy"/>
+        <Setter Property="Content" Value="&#xE16F;"/>
+    </Style>
+    <Style x:Key="Emoji2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Emoji2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Emoji2"/>
+        <Setter Property="Content" Value="&#xE170;"/>
+    </Style>
+    <Style x:Key="ImportantAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportantAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Important"/>
+        <Setter Property="Content" Value="&#xE171;"/>
+    </Style>
+    <Style x:Key="MailReplyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailReplyAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Reply"/>
+        <Setter Property="Content" Value="&#xE172;"/>
+    </Style>
+    <Style x:Key="SlideShowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SlideShowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Slideshow"/>
+        <Setter Property="Content" Value="&#xE173;"/>
+    </Style>
+    <Style x:Key="SortAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SortAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Sort"/>
+        <Setter Property="Content" Value="&#xE174;"/>
+    </Style>
+    <Style x:Key="ManageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ManageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Manage"/>
+        <Setter Property="Content" Value="&#xE178;"/>
+    </Style>
+    <Style x:Key="AllAppsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AllAppsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="All Apps"/>
+        <Setter Property="Content" Value="&#xE179;"/>
+    </Style>
+    <Style x:Key="DisconnectDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DisconnectDriveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Disconnect Drive"/>
+        <Setter Property="Content" Value="&#xE17A;"/>
+    </Style>
+    <Style x:Key="MapDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MapDriveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Map Drive"/>
+        <Setter Property="Content" Value="&#xE17B;"/>
+    </Style>
+    <Style x:Key="NewWindowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NewWindowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="New Window"/>
+        <Setter Property="Content" Value="&#xE17C;"/>
+    </Style>
+    <Style x:Key="OpenWithAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenWithAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open With"/>
+        <Setter Property="Content" Value="&#xE17D;"/>
+    </Style>
+    <Style x:Key="ContactPresenceAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactPresenceAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Presence"/>
+        <Setter Property="Content" Value="&#xE181;"/>
+    </Style>
+    <Style x:Key="PriorityAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PriorityAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Priority"/>
+        <Setter Property="Content" Value="&#xE182;"/>
+    </Style>
+    <Style x:Key="UploadSkyDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UploadSkyDriveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Skydrive"/>
+        <Setter Property="Content" Value="&#xE183;"/>
+    </Style>
+    <Style x:Key="GoToTodayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GoToTodayAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Today"/>
+        <Setter Property="Content" Value="&#xE184;"/>
+    </Style>
+    <Style x:Key="FontAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Font"/>
+        <Setter Property="Content" Value="&#xE185;"/>
+    </Style>
+
+    -->
+
+    <!--
+
+    <Style x:Key="FontColorAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontColorAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Font Color"/>
+        <Setter Property="Content" Value="&#xE186;"/>
+    </Style>
+    <Style x:Key="Contact2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Contact2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Contact"/>
+        <Setter Property="Content" Value="&#xE187;"/>
+    </Style>
+    <Style x:Key="FolderppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Folder"/>
+        <Setter Property="Content" Value="&#xE188;"/>
+    </Style>
+    <Style x:Key="AudioAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AudioAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Audio"/>
+        <Setter Property="Content" Value="&#xE189;"/>
+    </Style>
+    <Style x:Key="PlaceholderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PlaceholderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Placeholder"/>
+        <Setter Property="Content" Value="&#xE18A;"/>
+    </Style>
+    <Style x:Key="ViewAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ViewAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="View"/>
+        <Setter Property="Content" Value="&#xE18B;"/>
+    </Style>
+    <Style x:Key="SetLockScreenAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SetLockscreenAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Set Lockscreen"/>
+        <Setter Property="Content" Value="&#xE18C;"/>
+    </Style>
+    <Style x:Key="SetTitleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SetTitleAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Set Title"/>
+        <Setter Property="Content" Value="&#xE18D;"/>
+    </Style>
+    <Style x:Key="CcAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CcAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Cc"/>
+        <Setter Property="Content" Value="&#xE190;"/>
+    </Style>
+    <Style x:Key="StopSlideShowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="StopSlideshowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Stop Slideshow"/>
+        <Setter Property="Content" Value="&#xE191;"/>
+    </Style>
+    <Style x:Key="PermissionsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PermissionsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Permisions"/>
+        <Setter Property="Content" Value="&#xE192;"/>
+    </Style>
+    <Style x:Key="HighlightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HighlightAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Highlight"/>
+        <Setter Property="Content" Value="&#xE193;"/>
+    </Style>
+    <Style x:Key="DisableUpdatesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DisableUpdatesAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Disable Updates"/>
+        <Setter Property="Content" Value="&#xE194;"/>
+    </Style>
+    <Style x:Key="UnfavoriteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnfavoriteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Unfavorite"/>
+        <Setter Property="Content" Value="&#xE195;"/>
+    </Style>
+    <Style x:Key="UnPinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnPinAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Unpin"/>
+        <Setter Property="Content" Value="&#xE196;"/>
+    </Style>
+    <Style x:Key="OpenLocalAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenLocalAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open Loal"/>
+        <Setter Property="Content" Value="&#xE197;"/>
+    </Style>
+    <Style x:Key="MuteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MuteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Mute"/>
+        <Setter Property="Content" Value="&#xE198;"/>
+    </Style>
+    <Style x:Key="ItalicAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ItalicAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Italic"/>
+        <Setter Property="Content" Value="&#xE199;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="UnderlineAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnderlineAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Underline"/>
+        <Setter Property="Content" Value="&#xE19A;"/>
+    </Style>
+    <Style x:Key="BoldAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BoldAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Bold"/>
+        <Setter Property="Content" Value="&#xE19B;"/>
+    </Style>
+    <Style x:Key="MoveToFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MoveToFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Move to Folder"/>
+        <Setter Property="Content" Value="&#xE19C;"/>
+    </Style>
+    <Style x:Key="LikeDislikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LikeDislikeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Like/Dislike"/>
+        <Setter Property="Content" Value="&#xE19D;"/>
+    </Style>
+    <Style x:Key="DislikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DislikeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dislike"/>
+        <Setter Property="Content" Value="&#xE19E;"/>
+    </Style>
+    <Style x:Key="LikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LikeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Like"/>
+        <Setter Property="Content" Value="&#xE19F;"/>
+    </Style>
+    <Style x:Key="AlignRightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignRightAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Align Right"/>
+        <Setter Property="Content" Value="&#xE1A0;"/>
+    </Style>
+    <Style x:Key="AlignCenterAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignCenterAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Align Center"/>
+        <Setter Property="Content" Value="&#xE1A1;"/>
+    </Style>
+    <Style x:Key="AlignLeftAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignLeftAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Align Left"/>
+        <Setter Property="Content" Value="&#xE1A2;"/>
+    </Style>
+    <Style x:Key="ZoomAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zoom"/>
+        <Setter Property="Content" Value="&#xE1A3;"/>
+    </Style>
+    <Style x:Key="ZoomOutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomOutAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zoom Out"/>
+        <Setter Property="Content" Value="&#xE1A4;"/>
+    </Style>
+    <Style x:Key="OpenFileAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenFileAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open File"/>
+        <Setter Property="Content" Value="&#xE1A5;"/>
+    </Style>
+    <Style x:Key="OtherUserAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OtherUserAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Other User"/>
+        <Setter Property="Content" Value="&#xE1A6;"/>
+    </Style>
+    <Style x:Key="AdminAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AdminAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Admin"/>
+        <Setter Property="Content" Value="&#xE1A7;"/>
+    </Style>
+    <Style x:Key="StreetAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="StreetAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Street"/>
+        <Setter Property="Content" Value="&#xE1C3;"/>
+    </Style>
+    <Style x:Key="MapAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MapAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Map"/>
+        <Setter Property="Content" Value="&#xE1C4;"/>
+    </Style>
+    <Style x:Key="ClearSelectionAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ClearSelectionAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Clear Selection"/>
+        <Setter Property="Content" Value="&#xE1C5;"/>
+    </Style>
+    <Style x:Key="FontDecreaseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontDecreaseAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Decrease Font"/>
+        <Setter Property="Content" Value="&#xE1C6;"/>
+    </Style>
+    <Style x:Key="FontIncreaseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontIncreaseAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Increase Font"/>
+        <Setter Property="Content" Value="&#xE1C7;"/>
+    </Style>
+    <Style x:Key="FontSizeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontSizeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Font Size"/>
+        <Setter Property="Content" Value="&#xE1C8;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="CellphoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CellphoneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Cellphone"/>
+        <Setter Property="Content" Value="&#xE1C9;"/>
+    </Style>
+    <Style x:Key="ReshareAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ReshareAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Reshare"/>
+        <Setter Property="Content" Value="&#xE1CA;"/>
+    </Style>
+    <Style x:Key="TagAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TagAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Tag"/>
+        <Setter Property="Content" Value="&#xE1CB;"/>
+    </Style>
+    <Style x:Key="RepeatOneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RepeatOneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Repeat Once"/>
+        <Setter Property="Content" Value="&#xE1CC;"/>
+    </Style>
+    <Style x:Key="RepeatAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RepeatAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Repeat All"/>
+        <Setter Property="Content" Value="&#xE1CD;"/>
+    </Style>
+    <Style x:Key="OutlineStarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OutlineStarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Outline Star"/>
+        <Setter Property="Content" Value="&#xE1CE;"/>
+    </Style>
+    <Style x:Key="SolidStarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SolidStarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Solid Star"/>
+        <Setter Property="Content" Value="&#xE1CF;"/>
+    </Style>
+    <Style x:Key="CalculatorAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalculatorAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Calculator"/>
+        <Setter Property="Content" Value="&#xE1D0;"/>
+    </Style>
+    <Style x:Key="DirectionsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DirectionsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Directions"/>
+        <Setter Property="Content" Value="&#xE1D1;"/>
+    </Style>
+    <Style x:Key="TargetAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TargetAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Target"/>
+        <Setter Property="Content" Value="&#xE1D2;"/>
+    </Style>
+    <Style x:Key="LibraryAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LibraryAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Library"/>
+        <Setter Property="Content" Value="&#xE1D3;"/>
+    </Style>
+    <Style x:Key="PhonebookAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PhonebookAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Phonebook"/>
+        <Setter Property="Content" Value="&#xE1D4;"/>
+    </Style>
+    <Style x:Key="MemoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MemoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Memo"/>
+        <Setter Property="Content" Value="&#xE1D5;"/>
+    </Style>
+    <Style x:Key="MicrophoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MicrophoneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Microphone"/>
+        <Setter Property="Content" Value="&#xE1D6;"/>
+    </Style>
+    <Style x:Key="PostUpdateAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PostUpdateAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Post Update"/>
+        <Setter Property="Content" Value="&#xE1D7;"/>
+    </Style>
+    <Style x:Key="BackToWindowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BackToWindowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Back to Window"/>
+        <Setter Property="Content" Value="&#xE1D8;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="FullScreenAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FullScreenAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Full Screen"/>
+        <Setter Property="Content" Value="&#xE1D9;"/>
+    </Style>
+    <Style x:Key="NewFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NewFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="New Folder"/>
+        <Setter Property="Content" Value="&#xE1DA;"/>
+    </Style>
+    <Style x:Key="CalendarReplyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarReplyAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Calendar Reply"/>
+        <Setter Property="Content" Value="&#xE1DB;"/>
+    </Style>
+    <Style x:Key="UnsyncFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnsyncFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Unsync Folder"/>
+        <Setter Property="Content" Value="&#xE1DD;"/>
+    </Style>
+    <Style x:Key="ReportHackedAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ReportHackedAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Report Hacked"/>
+        <Setter Property="Content" Value="&#xE1DE;"/>
+    </Style>
+    <Style x:Key="SyncFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SyncFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Sync Folder"/>
+        <Setter Property="Content" Value="&#xE1DF;"/>
+    </Style>
+    <Style x:Key="BlockContactAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Block ContactAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="BlockContact"/>
+        <Setter Property="Content" Value="&#xE1E0;"/>
+    </Style>
+    <Style x:Key="SwitchAppsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SwitchAppsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Switch Apps"/>
+        <Setter Property="Content" Value="&#xE1E1;"/>
+    </Style>
+    <Style x:Key="AddFriendAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AddFriendAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Add Friend"/>
+        <Setter Property="Content" Value="&#xE1E2;"/>
+    </Style>
+    <Style x:Key="TouchPointerAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TouchPointerAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Touch Pointer"/>
+        <Setter Property="Content" Value="&#xE1E3;"/>
+    </Style>
+    <Style x:Key="GoToStartAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GoToStartAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Go to Start"/>
+        <Setter Property="Content" Value="&#xE1E4;"/>
+    </Style>
+    <Style x:Key="ZeroBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZeroBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zero Bars"/>
+        <Setter Property="Content" Value="&#xE1E5;"/>
+    </Style>
+    <Style x:Key="OneBarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OneBarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="One Bar"/>
+        <Setter Property="Content" Value="&#xE1E6;"/>
+    </Style>
+    <Style x:Key="TwoBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TwoBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Two Bars"/>
+        <Setter Property="Content" Value="&#xE1E7;"/>
+    </Style>
+    <Style x:Key="ThreeBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ThreeBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Three Bars"/>
+        <Setter Property="Content" Value="&#xE1E8;"/>
+    </Style>
+    <Style x:Key="FourBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FourBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Four Bars"/>
+        <Setter Property="Content" Value="&#xE1E9;"/>
+    </Style>
+
+    -->
+
+    <!-- Title area styles -->
+
+    <Style x:Key="PageHeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource HeaderTextStyle}">
+        <Setter Property="TextWrapping" Value="NoWrap"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="Margin" Value="0,0,30,40"/>
+    </Style>
+
+    <Style x:Key="PageSubheaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource SubheaderTextStyle}">
+        <Setter Property="TextWrapping" Value="NoWrap"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="Margin" Value="0,0,0,40"/>
+    </Style>
+
+    <Style x:Key="SnappedPageHeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource PageSubheaderTextStyle}">
+        <Setter Property="Margin" Value="0,0,18,40"/>
+    </Style>
+
+    <!--
+        BackButtonStyle is used to style a Button for use in the title area of a page.  Margins appropriate for
+        the conventional page layout are included as part of the style.
+    -->
+    <Style x:Key="BackButtonStyle" TargetType="Button">
+        <Setter Property="MinWidth" Value="0"/>
+        <Setter Property="Width" Value="48"/>
+        <Setter Property="Height" Value="48"/>
+        <Setter Property="Margin" Value="36,0,36,36"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
+        <Setter Property="FontWeight" Value="Normal"/>
+        <Setter Property="FontSize" Value="56"/>
+        <Setter Property="AutomationProperties.AutomationId" Value="BackButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Back"/>
+        <Setter Property="AutomationProperties.ItemType" Value="Navigation Button"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="Button">
+                    <Grid x:Name="RootGrid">
+                        <Grid Margin="-1,-16,0,0">
+                            <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" Foreground="{StaticResource BackButtonBackgroundThemeBrush}"/>
+                            <TextBlock x:Name="NormalGlyph" Text="{StaticResource BackButtonGlyph}" Foreground="{StaticResource BackButtonForegroundThemeBrush}"/>
+                            <TextBlock x:Name="ArrowGlyph" Text="&#xE0A6;" Foreground="{StaticResource BackButtonPressedForegroundThemeBrush}" Opacity="0"/>
+                        </Grid>
+                        <Rectangle
+                            x:Name="FocusVisualWhite"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="1.5"/>
+                        <Rectangle
+                            x:Name="FocusVisualBlack"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="0.5"/>
+
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal" />
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverBackgroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="ArrowGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="NormalGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="0"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualWhite"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualBlack"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused" />
+                                <VisualState x:Name="PointerFocused" />
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!--
+        PortraitBackButtonStyle is used to style a Button for use in the title area of a portrait page.  Margins appropriate
+        for the conventional page layout are included as part of the style.
+    -->
+    <Style x:Key="PortraitBackButtonStyle" TargetType="Button" BasedOn="{StaticResource BackButtonStyle}">
+        <Setter Property="Margin" Value="26,0,26,36"/>
+    </Style>
+
+    <!--
+        SnappedBackButtonStyle is used to style a Button for use in the title area of a snapped page.  Margins appropriate
+        for the conventional page layout are included as part of the style.
+
+        The obvious duplication here is necessary as the glyphs used in snapped are not merely smaller versions of the same
+        glyph but are actually distinct.
+    -->
+    <Style x:Key="SnappedBackButtonStyle" TargetType="Button">
+        <Setter Property="MinWidth" Value="0"/>
+        <Setter Property="Margin" Value="20,0,0,0"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
+        <Setter Property="FontWeight" Value="Normal"/>
+        <Setter Property="FontSize" Value="26.66667"/>
+        <Setter Property="AutomationProperties.AutomationId" Value="BackButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Back"/>
+        <Setter Property="AutomationProperties.ItemType" Value="Navigation Button"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="Button">
+                    <Grid x:Name="RootGrid" Width="36" Height="36" Margin="-3,0,7,33">
+                        <Grid Margin="-1,-1,0,0">
+                            <TextBlock x:Name="BackgroundGlyph" Text="&#xE0D4;" Foreground="{StaticResource BackButtonBackgroundThemeBrush}"/>
+                            <TextBlock x:Name="NormalGlyph" Text="{StaticResource BackButtonSnappedGlyph}" Foreground="{StaticResource BackButtonForegroundThemeBrush}"/>
+                            <TextBlock x:Name="ArrowGlyph" Text="&#xE0C4;" Foreground="{StaticResource BackButtonPressedForegroundThemeBrush}" Opacity="0"/>
+                        </Grid>
+                        <Rectangle
+                            x:Name="FocusVisualWhite"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="1.5"/>
+                        <Rectangle
+                            x:Name="FocusVisualBlack"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="0.5"/>
+
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal" />
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverBackgroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="ArrowGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="NormalGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="0"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualWhite"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualBlack"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused" />
+                                <VisualState x:Name="PointerFocused" />
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!-- Item templates -->
+
+    <!-- Grid-appropriate 250 pixel square item template as seen in the GroupedItemsPage and ItemsPage -->
+    <DataTemplate x:Key="Standard250x250ItemTemplate">
+        <Grid HorizontalAlignment="Left" Width="250" Height="250">
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
+            </Border>
+            <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
+                <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
+                <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- Grid-appropriate 500 by 130 pixel item template as seen in the GroupDetailPage -->
+    <DataTemplate x:Key="Standard500x130ItemTemplate">
+        <Grid Height="110" Width="480" Margin="10">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
+            </Border>
+            <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- List-appropriate 130 pixel high item template as seen in the SplitPage -->
+    <DataTemplate x:Key="Standard130ItemTemplate">
+        <Grid Height="110" Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
+            </Border>
+            <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!--
+        List-appropriate 80 pixel high item template as seen in the SplitPage when Filled, and
+        the following pages when snapped: GroupedItemsPage, GroupDetailPage, and ItemsPage
+    -->
+    <DataTemplate x:Key="Standard80ItemTemplate">
+        <Grid Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="60" Height="60">
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>
+            </Border>
+            <StackPanel Grid.Column="1" Margin="10,0,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource ItemTextStyle}" MaxHeight="40"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- Grid-appropriate 300 by 70 pixel item template as seen in the SearchResultsPage -->
+    <DataTemplate x:Key="StandardSmallIcon300x70ItemTemplate">
+        <Grid Width="294" Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,0,0,10" Width="40" Height="40">
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>
+            </Border>
+            <StackPanel Grid.Column="1" Margin="10,-10,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource BodyTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- List-appropriate 70 pixel high item template as seen in the SearchResultsPage when Snapped -->
+    <DataTemplate x:Key="StandardSmallIcon70ItemTemplate">
+        <Grid Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,0,0,10" Width="40" Height="40">
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>
+            </Border>
+            <StackPanel Grid.Column="1" Margin="10,-10,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource BodyTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!--
+      190x130 pixel item template for displaying file previews as seen in the FileOpenPickerPage
+      Includes an elaborate tooltip to display title and description text
+  -->
+    <DataTemplate x:Key="StandardFileWithTooltip190x130ItemTemplate">
+        <Grid>
+            <Grid Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
+                <Image
+                    Source="{Binding Image}"
+                    Width="190"
+                    Height="130"
+                    HorizontalAlignment="Center"
+                    VerticalAlignment="Center"
+                    Stretch="Uniform"/>
+            </Grid>
+            <ToolTipService.Placement>Mouse</ToolTipService.Placement>
+            <ToolTipService.ToolTip>
+                <ToolTip>
+                    <ToolTip.Style>
+                        <Style TargetType="ToolTip">
+                            <Setter Property="BorderBrush" Value="{StaticResource ToolTipBackgroundThemeBrush}" />
+                            <Setter Property="Padding" Value="0" />
+                        </Style>
+                    </ToolTip.Style>
+
+                    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="Auto"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+
+                        <Grid Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="20">
+                            <Image
+                                Source="{Binding Image}"
+                                Width="160"
+                                Height="160"
+                                HorizontalAlignment="Center"
+                                VerticalAlignment="Center"
+                                Stretch="Uniform"/>
+                        </Grid>
+                        <StackPanel Width="200" Grid.Column="1" Margin="0,20,20,20">
+                            <TextBlock Text="{Binding Title}" TextWrapping="NoWrap" Style="{StaticResource BodyTextStyle}"/>
+                            <TextBlock Text="{Binding Description}" MaxHeight="140" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" Style="{StaticResource BodyTextStyle}"/>
+                        </StackPanel>
+                    </Grid>
+                </ToolTip>
+            </ToolTipService.ToolTip>
+        </Grid>
+    </DataTemplate>
+
+    <!-- ScrollViewer styles -->
+
+    <Style x:Key="HorizontalScrollViewerStyle" TargetType="ScrollViewer">
+        <Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
+        <Setter Property="VerticalScrollBarVisibility" Value="Disabled"/>
+        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Enabled" />
+        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" />
+        <Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
+    </Style>
+
+    <Style x:Key="VerticalScrollViewerStyle" TargetType="ScrollViewer">
+        <Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>
+        <Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
+        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
+        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Enabled" />
+        <Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
+    </Style>
+
+    <!-- Page layout roots typically use entrance animations and a theme-appropriate background color -->
+
+    <Style x:Key="LayoutRootStyle" TargetType="Panel">
+        <Setter Property="Background" Value="{StaticResource ApplicationPageBackgroundThemeBrush}"/>
+        <Setter Property="ChildrenTransitions">
+            <Setter.Value>
+                <TransitionCollection>
+                    <EntranceThemeTransition/>
+                </TransitionCollection>
+            </Setter.Value>
+        </Setter>
+    </Style>
+</ResourceDictionary>
diff --git a/Release/samples/FacebookDemo/Facebook.cpp b/Release/samples/FacebookDemo/Facebook.cpp
new file mode 100644 (file)
index 0000000..9789968
--- /dev/null
@@ -0,0 +1,138 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Facebook.cpp - Implementation file for simple facebook client.
+ * Note: this implementation will not work until you replace the placeholder
+ * strings below with tokens obtained from facebook.
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "pch.h"
+
+#include "facebook.h"
+
+using namespace pplx;
+using namespace web;
+using namespace Platform;
+using namespace Windows::Foundation;
+using namespace Windows::Security::Authentication::Web;
+using namespace Windows::Storage;
+
+// Placeholder strings for app tokens
+// see blog post at http://aka.ms/FacebookCppRest
+// for information on obtaining these values
+const std::wstring application_id(L"insert your application ID");
+const std::wstring application_token(L"insert your application token");
+
+facebook_client& facebook_client::instance()
+{
+    static facebook_client c;
+    return c;
+}
+
+pplx::task<void> facebook_client::login(std::wstring scopes)
+{
+    // Look in the Local Settings for previously-stored login credentials
+    auto ls = ApplicationData::Current->LocalSettings->CreateContainer("LoginDetailsCache",
+                                                                       ApplicationDataCreateDisposition::Always);
+
+    if (ls->Values->HasKey("facebookToken"))
+    {
+        token_ = dynamic_cast<String ^>(ls->Values->Lookup("facebookToken"))->Data();
+    }
+
+    if (!token_.empty())
+    {
+        // Check if the token is still valid
+        using namespace http;
+
+        uri_builder tokendbg_uri(L"/debug_token");
+        tokendbg_uri.append_query(L"input_token", token_);
+        tokendbg_uri.append_query(L"access_token", application_token);
+
+        http_request request(methods::GET);
+        request.set_request_uri(tokendbg_uri.to_string());
+        request.headers().add(header_names::accept, L"application/json");
+
+        return raw_client.request(request)
+            .then([](http_response response) { return response.extract_json(); })
+            .then(
+                [=](json::value v) -> task<void> {
+                    auto obj = v[L"data"];
+
+                    if (obj[L"is_valid"].as_bool())
+                    {
+                        // Currently cached access token is valid
+                        signed_in = true;
+                        return create_task([]() {}); // Return an empty task to match the function's return value
+                    }
+
+                    // If the token was invalid, go through full login
+                    return full_login(scopes);
+                },
+                task_continuation_context::use_current());
+    }
+
+    // If no token was found, go through full login
+    return full_login(scopes);
+}
+
+pplx::task<void> facebook_client::full_login(std::wstring scopes)
+{
+    // Create uri for OAuth login on Facebook
+    http::uri_builder login_uri(L"https://www.facebook.com/dialog/oauth");
+    login_uri.append_query(L"client_id", application_id); // App id
+    login_uri.append_query(L"redirect_uri", L"https://www.facebook.com/connect/login_success.html");
+    login_uri.append_query(L"scope", scopes);
+    login_uri.append_query(L"display", L"popup");
+    login_uri.append_query(L"response_type", L"token");
+
+    return create_task(WebAuthenticationBroker::AuthenticateAsync(
+                           WebAuthenticationOptions::None,
+                           ref new Uri(ref new String(login_uri.to_string().c_str())),
+                           ref new Uri("https://www.facebook.com/connect/login_success.html")))
+        .then([=](WebAuthenticationResult ^ result) {
+            if (result->ResponseStatus == WebAuthenticationStatus::Success)
+            {
+                signed_in = true;
+
+                std::wstring response(result->ResponseData->Data()); // Save authentication token
+                auto start = response.find(L"access_token=");
+                start += 13;
+                auto end = response.find('&');
+
+                token_ = response.substr(start, end - start);
+
+                // Add token to Local Settings for future login attempts
+                auto ls = ApplicationData::Current->LocalSettings->CreateContainer(
+                    "LoginDetailsCache", ApplicationDataCreateDisposition::Always);
+
+                ls->Values->Insert("facebookToken", ref new String(token_.c_str()));
+            }
+        });
+}
+
+task<json::value> facebook_client::get(std::wstring path)
+{
+    using namespace http;
+
+    http_request request(methods::GET);
+
+    request.set_request_uri(base_uri().append_path(path).to_uri());
+    request.headers().add(header_names::accept, L"application/json");
+
+    return raw_client.request(request).then([](http_response response) { return response.extract_json(); });
+}
+
+http::uri_builder facebook_client::base_uri(bool absolute)
+{
+    http::uri_builder ret;
+
+    if (absolute) ret.set_path(L"https://graph.facebook.com");
+
+    ret.append_query(L"access_token", token_);
+    return ret;
+}
diff --git a/Release/samples/FacebookDemo/Facebook.h b/Release/samples/FacebookDemo/Facebook.h
new file mode 100644 (file)
index 0000000..7da645e
--- /dev/null
@@ -0,0 +1,32 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Facebook.h - Simple client for connecting to facebook. See blog post
+ * at http://aka.ms/FacebookCppRest for a detailed walkthrough of this sample
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+#include <cpprest/http_client.h>
+#include <string>
+
+class facebook_client
+{
+public:
+    static facebook_client& instance(); // Singleton
+    pplx::task<void> login(std::wstring scopes);
+    pplx::task<web::json::value> get(std::wstring path);
+    web::http::uri_builder base_uri(bool absolute = false);
+
+private:
+    facebook_client() : raw_client(L"https://graph.facebook.com/"), signed_in(false) {}
+
+    pplx::task<void> full_login(std::wstring scopes);
+
+    std::wstring token_;
+    bool signed_in;
+    web::http::client::http_client raw_client;
+};
diff --git a/Release/samples/FacebookDemo/MainPage.xaml b/Release/samples/FacebookDemo/MainPage.xaml
new file mode 100644 (file)
index 0000000..ab036dc
--- /dev/null
@@ -0,0 +1,45 @@
+<!--
+ Copyright (C) Microsoft. All rights reserved.
+ Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+
+ =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ MainPage.xaml - Primary application page.  Contains two buttons for
+    logging into and fetching albums from Facebook, and a GridView
+    control for displaying the downloaded album data.
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+-->
+
+<Page
+    x:Class="FacebookDemo.MainPage"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:FacebookDemo"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d">
+
+    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="*" />
+            <RowDefinition Height="150" />
+        </Grid.RowDefinitions>
+
+        <GridView Grid.Row="0" x:Name="AlbumGrid">
+            <GridView.ItemTemplate>
+                <DataTemplate>
+                    <Grid HorizontalAlignment="Left" Width="400" Height="250"  Margin="5">
+                        <Image Source="{Binding Preview}" Stretch="UniformToFill" />
+                        <StackPanel VerticalAlignment="Bottom">
+                            <TextBlock Text="{Binding Title}" FontSize="30" HorizontalAlignment="Left" VerticalAlignment="Center"/>
+                            <TextBlock Text="{Binding Count}" FontSize="12" HorizontalAlignment="Right" VerticalAlignment="Top" />
+                        </StackPanel>
+                    </Grid>
+                </DataTemplate>
+            </GridView.ItemTemplate>
+        </GridView>
+        <StackPanel Orientation="Horizontal" Grid.Row="1">
+            <Button x:Name="LoginButton" Click="LoginButton_Click_1">Login</Button>
+            <Button x:Name="AlbumButton" IsEnabled="False" Click="AlbumButton_Click_1">Fetch Albums</Button>
+        </StackPanel>
+    </Grid>
+</Page>
diff --git a/Release/samples/FacebookDemo/MainPage.xaml.cpp b/Release/samples/FacebookDemo/MainPage.xaml.cpp
new file mode 100644 (file)
index 0000000..87e11ef
--- /dev/null
@@ -0,0 +1,99 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * MainPage.xaml.cpp - Implementation of the MainPage and
+ *  FacebookAlbum classes.
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "pch.h"
+
+#include "MainPage.xaml.h"
+
+#include "Facebook.h"
+#include <collection.h>
+
+using namespace FacebookDemo;
+
+using namespace Platform;
+using namespace Platform::Collections;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+MainPage::MainPage() { InitializeComponent(); }
+
+/// <summary>
+/// Invoked when this page is about to be displayed in a Frame.
+/// </summary>
+/// <param name="e">Event data that describes how this page was reached.  The Parameter
+/// property is typically used to configure the page.</param>
+void MainPage::OnNavigatedTo(NavigationEventArgs ^ e)
+{
+    (void)e; // Unused parameter
+}
+
+void MainPage::LoginButton_Click_1(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
+{
+    LoginButton->IsEnabled = false; // Disable button to prevent double-login
+
+    facebook_client::instance()
+        .login(L"user_photos")
+        .then([=]() { AlbumButton->IsEnabled = true; }, pplx::task_continuation_context::use_current());
+}
+
+void MainPage::AlbumButton_Click_1(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
+{
+    using namespace pplx;
+    AlbumButton->IsEnabled = false;
+
+    facebook_client::instance()
+        .get(L"/me/albums")
+        .then([](web::json::value v) {
+            web::json::object& obj = v.as_object();
+            std::vector<FacebookAlbum ^> albums;
+
+            for (auto& elem : obj[L"data"].as_array())
+            {
+                albums.push_back(ref new FacebookAlbum(elem[L"name"].as_string(),
+                                                       elem[L"count"].as_integer(),
+                                                       elem[L"id"].as_string(),
+                                                       elem[L"cover_photo"].as_string()));
+            }
+
+            return task_from_result(std::move(albums));
+        })
+        .then(
+            [=](std::vector<FacebookAlbum ^> albums) {
+                AlbumGrid->ItemsSource = ref new Vector<FacebookAlbum ^>(std::move(albums));
+            },
+            task_continuation_context::use_current());
+}
+
+String ^ FacebookAlbum::Title::get() { return ref new String(title_.c_str()); }
+
+int FacebookAlbum::Count::get() { return count_; }
+
+ImageSource ^ FacebookAlbum::Preview::get()
+{
+    if (preview_ == nullptr)
+    {
+        auto preview_uri = facebook_client::instance().base_uri(true);
+
+        preview_uri.append_path(photo_id_);
+        preview_uri.append_path(L"/picture");
+
+        preview_ = ref new Imaging::BitmapImage(ref new Uri(StringReference(preview_uri.to_string().c_str())));
+    }
+
+    return preview_;
+}
diff --git a/Release/samples/FacebookDemo/MainPage.xaml.h b/Release/samples/FacebookDemo/MainPage.xaml.h
new file mode 100644 (file)
index 0000000..aaaf2e9
--- /dev/null
@@ -0,0 +1,61 @@
+/***
+* Copyright (C) Microsoft. All rights reserved.
+* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+*
+* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+*
+* MainPage.xaml.h - Declaration of the MainPage class.  Also includes
+* the declaration for the FacebookAlbum class that the GridView databinds to.
+* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+****/
+#pragma once
+
+#include "MainPage.g.h"
+
+namespace FacebookDemo
+{
+       /// <summary>
+       /// An empty page that can be used on its own or navigated to within a Frame.
+       /// </summary>
+       public ref class MainPage sealed
+       {
+       public:
+               MainPage();
+
+       protected:
+               virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
+
+       private:
+               void LoginButton_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+               void AlbumButton_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+       };
+
+       [Windows::UI::Xaml::Data::Bindable]
+       [Windows::Foundation::Metadata::WebHostHidden]
+       public ref class FacebookAlbum sealed
+       {
+       internal:
+               FacebookAlbum(std::wstring title, int count, std::wstring id, std::wstring photo_id):
+                       title_(title), count_(count), id_(id), photo_id_(photo_id) {}
+
+       public:
+               property Platform::String^ Title {
+                       Platform::String^ get();
+               }
+
+               property int Count {
+                       int get();
+               }
+
+               property Windows::UI::Xaml::Media::ImageSource^ Preview {
+                       Windows::UI::Xaml::Media::ImageSource^ get();
+               }
+
+       private:
+               std::wstring id_;
+               std::wstring title_;
+               std::wstring photo_id_;
+               int count_;
+               Windows::UI::Xaml::Media::ImageSource^ preview_;
+       };
+}
diff --git a/Release/samples/FacebookDemo/Package.appxmanifest b/Release/samples/FacebookDemo/Package.appxmanifest
new file mode 100644 (file)
index 0000000..10e0fb8
--- /dev/null
@@ -0,0 +1,31 @@
+ļ»æ<?xml version="1.0" encoding="utf-8"?>
+<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
+  <Identity Name="2b173277-f2a9-4af1-905b-0da25a731def" Publisher="CN=casaserv" Version="1.0.0.0" />
+  <Properties>
+    <DisplayName>FacebookDemo</DisplayName>
+    <PublisherDisplayName>Andy</PublisherDisplayName>
+    <Logo>Assets\StoreLogo.png</Logo>
+  </Properties>
+  <Prerequisites>
+    <OSMinVersion>10.0</OSMinVersion>
+    <OSMaxVersionTested>10.0</OSMaxVersionTested>
+  </Prerequisites>
+  <Resources>
+    <Resource Language="x-generate" />
+  </Resources>
+  <Applications>
+    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="FacebookDemo.App">
+      <m2:VisualElements DisplayName="FacebookDemo" Description="FacebookDemo" BackgroundColor="#464646" ForegroundText="light" Square150x150Logo="Assets\Logo.png" Square30x30Logo="Assets\SmallLogo.png">
+        <m2:DefaultTile>
+          <m2:ShowNameOnTiles>
+            <m2:ShowOn Tile="square150x150Logo" />
+          </m2:ShowNameOnTiles>
+        </m2:DefaultTile>
+        <m2:SplashScreen Image="Assets\SplashScreen.png" />
+      </m2:VisualElements>
+    </Application>
+  </Applications>
+  <Capabilities>
+    <Capability Name="internetClient" />
+  </Capabilities>
+</Package>
\ No newline at end of file
diff --git a/Release/samples/FacebookDemo/Package.uwp.appxmanifest b/Release/samples/FacebookDemo/Package.uwp.appxmanifest
new file mode 100644 (file)
index 0000000..2c22a53
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Package\r
+  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"\r
+  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"\r
+  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"\r
+  IgnorableNamespaces="uap mp">\r
+  <Identity Name="2b173277-f2a9-4af1-905b-0da25a731def" Publisher="CN=casaserv" Version="1.0.0.0" />\r
+  <Properties>\r
+    <DisplayName>FacebookDemo</DisplayName>\r
+    <PublisherDisplayName>Andy</PublisherDisplayName>\r
+    <Logo>Assets\StoreLogo.png</Logo>\r
+  </Properties>\r
+\r
+  <mp:PhoneIdentity PhoneProductId="51cd24bd-4a31-4002-a587-b5ca8c380f24" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>\r
+\r
+  <Dependencies>\r
+    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />\r
+  </Dependencies>\r
+  \r
+  <Resources>\r
+    <Resource Language="x-generate" />\r
+  </Resources>\r
+  <Applications>\r
+    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="FacebookDemo.App">\r
+      <uap:VisualElements DisplayName="FacebookDemo" Description="FacebookDemo" BackgroundColor="#464646" Square150x150Logo="Assets\Logo.png" Square44x44Logo="Assets\SmallLogo.png">\r
+        <uap:SplashScreen Image="Assets\SplashScreen.png" />\r
+      </uap:VisualElements>\r
+    </Application>\r
+  </Applications>\r
+\r
+  <Capabilities>\r
+    <Capability Name="internetClient" />\r
+  </Capabilities>\r
+</Package>
\ No newline at end of file
diff --git a/Release/samples/FacebookDemo/pch.cpp b/Release/samples/FacebookDemo/pch.cpp
new file mode 100644 (file)
index 0000000..bfbd45c
--- /dev/null
@@ -0,0 +1,11 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * pch.cpp - Source file to generate pre-compiled header.
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "pch.h"
diff --git a/Release/samples/FacebookDemo/pch.h b/Release/samples/FacebookDemo/pch.h
new file mode 100644 (file)
index 0000000..b81db69
--- /dev/null
@@ -0,0 +1,14 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * pch.h - Pre-compiled header standard include file
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "App.xaml.h"
+#include <collection.h>
diff --git a/Release/samples/OAuth2Live/App.xaml b/Release/samples/OAuth2Live/App.xaml
new file mode 100644 (file)
index 0000000..d43efea
--- /dev/null
@@ -0,0 +1,20 @@
+ļ»æ<Application\r
+    x:Class="OAuth2Live.App"\r
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"\r
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"\r
+    xmlns:local="using:OAuth2Live">\r
+\r
+    <Application.Resources>\r
+        <ResourceDictionary>\r
+            <ResourceDictionary.MergedDictionaries>\r
+\r
+                <!-- \r
+                    Styles that define common aspects of the platform look and feel\r
+                    Required by Visual Studio project and item templates\r
+                 -->\r
+                <ResourceDictionary Source="Common/StandardStyles.xaml"/>\r
+            </ResourceDictionary.MergedDictionaries>\r
+\r
+        </ResourceDictionary>\r
+    </Application.Resources>\r
+</Application>\r
diff --git a/Release/samples/OAuth2Live/App.xaml.cpp b/Release/samples/OAuth2Live/App.xaml.cpp
new file mode 100644 (file)
index 0000000..853f6ee
--- /dev/null
@@ -0,0 +1,107 @@
+ļ»æ//\r
+// App.xaml.cpp\r
+// Implementation of the App class.\r
+//\r
+\r
+#include "pch.h"\r
+\r
+#include "MainPage.xaml.h"\r
+\r
+using namespace OAuth2Live;\r
+\r
+using namespace Platform;\r
+using namespace Windows::ApplicationModel;\r
+using namespace Windows::ApplicationModel::Activation;\r
+using namespace Windows::Foundation;\r
+using namespace Windows::Foundation::Collections;\r
+using namespace Windows::UI::Xaml;\r
+using namespace Windows::UI::Xaml::Controls;\r
+using namespace Windows::UI::Xaml::Controls::Primitives;\r
+using namespace Windows::UI::Xaml::Data;\r
+using namespace Windows::UI::Xaml::Input;\r
+using namespace Windows::UI::Xaml::Interop;\r
+using namespace Windows::UI::Xaml::Media;\r
+using namespace Windows::UI::Xaml::Navigation;\r
+\r
+// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227\r
+\r
+/// <summary>\r
+/// Initializes the singleton application object.  This is the first line of authored code\r
+/// executed, and as such is the logical equivalent of main() or WinMain().\r
+/// </summary>\r
+App::App()\r
+{\r
+    InitializeComponent();\r
+    Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);\r
+}\r
+\r
+/// <summary>\r
+/// Invoked when the application is launched normally by the end user.  Other entry points\r
+/// will be used when the application is launched to open a specific file, to display\r
+/// search results, and so forth.\r
+/// </summary>\r
+/// <param name="args">Details about the launch request and process.</param>\r
+void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^ args)\r
+{\r
+    auto rootFrame = dynamic_cast<Frame ^>(Window::Current->Content);\r
+\r
+    // Do not repeat app initialization when the Window already has content,\r
+    // just ensure that the window is active\r
+    if (rootFrame == nullptr)\r
+    {\r
+        // Create a Frame to act as the navigation context and associate it with\r
+        // a SuspensionManager key\r
+        rootFrame = ref new Frame();\r
+\r
+        if (args->PreviousExecutionState == ApplicationExecutionState::Terminated)\r
+        {\r
+            // TODO: Restore the saved session state only when appropriate, scheduling the\r
+            // final launch steps after the restore is complete\r
+        }\r
+\r
+        if (rootFrame->Content == nullptr)\r
+        {\r
+            // When the navigation stack isn't restored navigate to the first page,\r
+            // configuring the new page by passing required information as a navigation\r
+            // parameter\r
+            if (!rootFrame->Navigate(TypeName(MainPage::typeid), args->Arguments))\r
+            {\r
+                throw ref new FailureException("Failed to create initial page");\r
+            }\r
+        }\r
+        // Place the frame in the current Window\r
+        Window::Current->Content = rootFrame;\r
+        // Ensure the current window is active\r
+        Window::Current->Activate();\r
+    }\r
+    else\r
+    {\r
+        if (rootFrame->Content == nullptr)\r
+        {\r
+            // When the navigation stack isn't restored navigate to the first page,\r
+            // configuring the new page by passing required information as a navigation\r
+            // parameter\r
+            if (!rootFrame->Navigate(TypeName(MainPage::typeid), args->Arguments))\r
+            {\r
+                throw ref new FailureException("Failed to create initial page");\r
+            }\r
+        }\r
+        // Ensure the current window is active\r
+        Window::Current->Activate();\r
+    }\r
+}\r
+\r
+/// <summary>\r
+/// Invoked when application execution is being suspended.  Application state is saved\r
+/// without knowing whether the application will be terminated or resumed with the contents\r
+/// of memory still intact.\r
+/// </summary>\r
+/// <param name="sender">The source of the suspend request.</param>\r
+/// <param name="e">Details about the suspend request.</param>\r
+void App::OnSuspending(Object ^ sender, SuspendingEventArgs ^ e)\r
+{\r
+    (void)sender; // Unused parameter\r
+    (void)e;      // Unused parameter\r
+\r
+    // TODO: Save application state and stop any background activity\r
+}\r
diff --git a/Release/samples/OAuth2Live/App.xaml.h b/Release/samples/OAuth2Live/App.xaml.h
new file mode 100644 (file)
index 0000000..1875325
--- /dev/null
@@ -0,0 +1,24 @@
+ļ»æ//\r
+// App.xaml.h\r
+// Declaration of the App class.\r
+//\r
+\r
+#pragma once\r
+\r
+#include "App.g.h"\r
+\r
+namespace OAuth2Live\r
+{\r
+/// <summary>\r
+/// Provides application-specific behavior to supplement the default Application class.\r
+/// </summary>\r
+ref class App sealed\r
+{\r
+public:\r
+    App();\r
+    virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^ args) override;\r
+\r
+private:\r
+    void OnSuspending(Platform::Object ^ sender, Windows::ApplicationModel::SuspendingEventArgs ^ e);\r
+};\r
+} // namespace OAuth2Live\r
diff --git a/Release/samples/OAuth2Live/Assets/Logo.png b/Release/samples/OAuth2Live/Assets/Logo.png
new file mode 100644 (file)
index 0000000..e26771c
Binary files /dev/null and b/Release/samples/OAuth2Live/Assets/Logo.png differ
diff --git a/Release/samples/OAuth2Live/Assets/SmallLogo.png b/Release/samples/OAuth2Live/Assets/SmallLogo.png
new file mode 100644 (file)
index 0000000..1eb0d9d
Binary files /dev/null and b/Release/samples/OAuth2Live/Assets/SmallLogo.png differ
diff --git a/Release/samples/OAuth2Live/Assets/SplashScreen.png b/Release/samples/OAuth2Live/Assets/SplashScreen.png
new file mode 100644 (file)
index 0000000..c951e03
Binary files /dev/null and b/Release/samples/OAuth2Live/Assets/SplashScreen.png differ
diff --git a/Release/samples/OAuth2Live/Assets/StoreLogo.png b/Release/samples/OAuth2Live/Assets/StoreLogo.png
new file mode 100644 (file)
index 0000000..dcb6727
Binary files /dev/null and b/Release/samples/OAuth2Live/Assets/StoreLogo.png differ
diff --git a/Release/samples/OAuth2Live/Common/StandardStyles.xaml b/Release/samples/OAuth2Live/Common/StandardStyles.xaml
new file mode 100644 (file)
index 0000000..01fd46a
--- /dev/null
@@ -0,0 +1,1829 @@
+ļ»æ<!--\r
+    This file contains XAML styles that simplify application development.\r
+\r
+    These are not merely convenient, but are required by most Visual Studio project and item templates.\r
+    Removing, renaming, or otherwise modifying the content of these files may result in a project that\r
+    does not build, or that will not build once additional pages are added.  If variations on these\r
+    styles are desired it is recommended that you copy the content under a new name and modify your\r
+    private copy.\r
+-->\r
+\r
+<ResourceDictionary\r
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"\r
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">\r
+\r
+    <!-- Non-brush values that vary across themes -->\r
+\r
+    <ResourceDictionary.ThemeDictionaries>\r
+        <ResourceDictionary x:Key="Default">\r
+            <x:String x:Key="BackButtonGlyph">&#xE071;</x:String>\r
+            <x:String x:Key="BackButtonSnappedGlyph">&#xE0BA;</x:String>\r
+        </ResourceDictionary>\r
+\r
+        <ResourceDictionary x:Key="HighContrast">\r
+            <x:String x:Key="BackButtonGlyph">&#xE071;</x:String>\r
+            <x:String x:Key="BackButtonSnappedGlyph">&#xE0C4;</x:String>\r
+        </ResourceDictionary>\r
+    </ResourceDictionary.ThemeDictionaries>\r
+\r
+    <x:String x:Key="ChevronGlyph">&#xE26B;</x:String>\r
+\r
+    <!-- RichTextBlock styles -->\r
+\r
+    <Style x:Key="BasicRichTextStyle" TargetType="RichTextBlock">\r
+        <Setter Property="Foreground" Value="{StaticResource ApplicationForegroundThemeBrush}"/>\r
+        <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>\r
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>\r
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>\r
+        <Setter Property="TextWrapping" Value="Wrap"/>\r
+        <Setter Property="Typography.StylisticSet20" Value="True"/>\r
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>\r
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>\r
+    </Style>\r
+\r
+    <Style x:Key="BaselineRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BasicRichTextStyle}">\r
+        <Setter Property="LineHeight" Value="20"/>\r
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>\r
+        <!-- Properly align text along its baseline -->\r
+        <Setter Property="RenderTransform">\r
+            <Setter.Value>\r
+                <TranslateTransform X="-1" Y="4"/>\r
+            </Setter.Value>\r
+        </Setter>\r
+    </Style>\r
+\r
+    <Style x:Key="ItemRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaselineRichTextStyle}"/>\r
+\r
+    <Style x:Key="BodyRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaselineRichTextStyle}">\r
+        <Setter Property="FontWeight" Value="SemiLight"/>\r
+    </Style>\r
+\r
+    <!-- TextBlock styles -->\r
+\r
+    <Style x:Key="BasicTextStyle" TargetType="TextBlock">\r
+        <Setter Property="Foreground" Value="{StaticResource ApplicationForegroundThemeBrush}"/>\r
+        <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>\r
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>\r
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>\r
+        <Setter Property="TextWrapping" Value="Wrap"/>\r
+        <Setter Property="Typography.StylisticSet20" Value="True"/>\r
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>\r
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>\r
+    </Style>\r
+\r
+    <Style x:Key="BaselineTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BasicTextStyle}">\r
+        <Setter Property="LineHeight" Value="20"/>\r
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>\r
+        <!-- Properly align text along its baseline -->\r
+        <Setter Property="RenderTransform">\r
+            <Setter.Value>\r
+                <TranslateTransform X="-1" Y="4"/>\r
+            </Setter.Value>\r
+        </Setter>\r
+    </Style>\r
+\r
+    <Style x:Key="HeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">\r
+        <Setter Property="FontSize" Value="56"/>\r
+        <Setter Property="FontWeight" Value="Light"/>\r
+        <Setter Property="LineHeight" Value="40"/>\r
+        <Setter Property="RenderTransform">\r
+            <Setter.Value>\r
+                <TranslateTransform X="-2" Y="8"/>\r
+            </Setter.Value>\r
+        </Setter>\r
+    </Style>\r
+\r
+    <Style x:Key="SubheaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">\r
+        <Setter Property="FontSize" Value="26.667"/>\r
+        <Setter Property="FontWeight" Value="Light"/>\r
+        <Setter Property="LineHeight" Value="30"/>\r
+        <Setter Property="RenderTransform">\r
+            <Setter.Value>\r
+                <TranslateTransform X="-1" Y="6"/>\r
+            </Setter.Value>\r
+        </Setter>\r
+    </Style>\r
+\r
+    <Style x:Key="TitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">\r
+        <Setter Property="FontWeight" Value="SemiBold"/>\r
+    </Style>\r
+\r
+    <Style x:Key="SubtitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">\r
+        <Setter Property="FontWeight" Value="Normal"/>\r
+    </Style>\r
+\r
+    <Style x:Key="ItemTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}"/>\r
+\r
+    <Style x:Key="BodyTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">\r
+        <Setter Property="FontWeight" Value="SemiLight"/>\r
+    </Style>\r
+\r
+    <Style x:Key="CaptionTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">\r
+        <Setter Property="FontSize" Value="12"/>\r
+        <Setter Property="Foreground" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>\r
+    </Style>\r
+\r
+    <Style x:Key="GroupHeaderTextStyle" TargetType="TextBlock">\r
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>\r
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>\r
+        <Setter Property="TextWrapping" Value="NoWrap"/>\r
+        <Setter Property="Typography.StylisticSet20" Value="True"/>\r
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>\r
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>\r
+        <Setter Property="FontSize" Value="26.667"/>\r
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>\r
+        <Setter Property="FontWeight" Value="Light"/>\r
+        <Setter Property="LineHeight" Value="30"/>\r
+        <Setter Property="RenderTransform">\r
+            <Setter.Value>\r
+                <TranslateTransform X="-1" Y="6"/>\r
+            </Setter.Value>\r
+        </Setter>\r
+    </Style>\r
+\r
+    <!-- Button styles -->\r
+    \r
+    <!--\r
+        TextButtonStyle is used to style a Button using subheader-styled text with no other adornment.  There\r
+        are two styles that are based on TextButtonStyle (TextPrimaryButtonStyle and TextSecondaryButtonStyle)\r
+        which are used in the GroupedItemsPage as a group header and in the FileOpenPickerPage for triggering\r
+        commands.\r
+    -->\r
+    <Style x:Key="TextButtonStyle" TargetType="ButtonBase">\r
+        <Setter Property="MinWidth" Value="0"/>\r
+        <Setter Property="MinHeight" Value="0"/>\r
+        <Setter Property="Template">\r
+            <Setter.Value>\r
+                <ControlTemplate TargetType="ButtonBase">\r
+                    <Grid Background="Transparent">\r
+                        <ContentPresenter x:Name="Text" Content="{TemplateBinding Content}" />\r
+                        <Rectangle\r
+                            x:Name="FocusVisualWhite"\r
+                            IsHitTestVisible="False"\r
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"\r
+                            StrokeEndLineCap="Square"\r
+                            StrokeDashArray="1,1"\r
+                            Opacity="0"\r
+                            StrokeDashOffset="1.5"/>\r
+                        <Rectangle\r
+                            x:Name="FocusVisualBlack"\r
+                            IsHitTestVisible="False"\r
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"\r
+                            StrokeEndLineCap="Square"\r
+                            StrokeDashArray="1,1"\r
+                            Opacity="0"\r
+                            StrokeDashOffset="0.5"/>\r
+                        <VisualStateManager.VisualStateGroups>\r
+                            <VisualStateGroup x:Name="CommonStates">\r
+                                <VisualState x:Name="Normal"/>\r
+                                <VisualState x:Name="PointerOver">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPointerOverForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Pressed">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Disabled">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                            </VisualStateGroup>\r
+                            <VisualStateGroup x:Name="FocusStates">\r
+                                <VisualState x:Name="Focused">\r
+                                    <Storyboard>\r
+                                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetName="FocusVisualWhite" Storyboard.TargetProperty="Opacity"/>\r
+                                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetName="FocusVisualBlack" Storyboard.TargetProperty="Opacity"/>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Unfocused"/>\r
+                            </VisualStateGroup>\r
+                            <VisualStateGroup x:Name="CheckStates">\r
+                                <VisualState x:Name="Checked"/>\r
+                                <VisualState x:Name="Unchecked">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Indeterminate"/>\r
+                            </VisualStateGroup>\r
+                        </VisualStateManager.VisualStateGroups>\r
+                    </Grid>\r
+                </ControlTemplate>\r
+            </Setter.Value>\r
+        </Setter>\r
+    </Style>\r
+\r
+    <Style x:Key="TextPrimaryButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource TextButtonStyle}">\r
+        <Setter Property="Foreground" Value="{StaticResource ApplicationHeaderForegroundThemeBrush}"/>\r
+    </Style>\r
+\r
+    <Style x:Key="TextSecondaryButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource TextButtonStyle}">\r
+        <Setter Property="Foreground" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>\r
+    </Style>\r
+\r
+    <!--\r
+        TextRadioButtonStyle is used to style a RadioButton using subheader-styled text with no other adornment.\r
+        This style is used in the SearchResultsPage to allow selection among filters.\r
+    -->\r
+    <Style x:Key="TextRadioButtonStyle" TargetType="RadioButton" BasedOn="{StaticResource TextButtonStyle}">\r
+        <Setter Property="Margin" Value="0,0,30,0"/>\r
+    </Style>\r
+\r
+    <!--\r
+        AppBarButtonStyle is used to style a Button (or ToggleButton) for use in an App Bar.  Content will be centered \r
+        and should fit within the 40 pixel radius glyph provided.  16-point Segoe UI Symbol is used for content text \r
+        to simplify the use of glyphs from that font.  AutomationProperties.Name is used for the text below the glyph.\r
+    -->\r
+    <Style x:Key="AppBarButtonStyle" TargetType="ButtonBase">\r
+        <Setter Property="Foreground" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>\r
+        <Setter Property="VerticalAlignment" Value="Stretch"/>\r
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>\r
+        <Setter Property="FontWeight" Value="Normal"/>\r
+        <Setter Property="FontSize" Value="20"/>\r
+        <Setter Property="AutomationProperties.ItemType" Value="App Bar Button"/>\r
+        <Setter Property="Template">\r
+            <Setter.Value>\r
+                <ControlTemplate TargetType="ButtonBase">\r
+                    <Grid x:Name="RootGrid" Width="100" Background="Transparent">\r
+                        <StackPanel VerticalAlignment="Top" Margin="0,12,0,11">\r
+                            <Grid Width="40" Height="40" Margin="0,0,0,5" HorizontalAlignment="Center">\r
+                                <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0" Foreground="{StaticResource AppBarItemBackgroundThemeBrush}"/>\r
+                                <TextBlock x:Name="OutlineGlyph" Text="&#xE0A7;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0"/>\r
+                                <ContentPresenter x:Name="Content" HorizontalAlignment="Center" Margin="-1,-1,0,0" VerticalAlignment="Center"/>\r
+                            </Grid>\r
+                            <TextBlock\r
+                                x:Name="TextLabel"\r
+                                Text="{TemplateBinding AutomationProperties.Name}"\r
+                                Foreground="{StaticResource AppBarItemForegroundThemeBrush}"\r
+                                Margin="0,0,2,0"\r
+                                FontSize="12"\r
+                                TextAlignment="Center"\r
+                                Width="88"\r
+                                MaxHeight="32"\r
+                                TextTrimming="WordEllipsis"\r
+                                Style="{StaticResource BasicTextStyle}"/>\r
+                        </StackPanel>\r
+                        <Rectangle\r
+                                x:Name="FocusVisualWhite"\r
+                                IsHitTestVisible="False"\r
+                                Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"\r
+                                StrokeEndLineCap="Square"\r
+                                StrokeDashArray="1,1"\r
+                                Opacity="0"\r
+                                StrokeDashOffset="1.5"/>\r
+                        <Rectangle\r
+                                x:Name="FocusVisualBlack"\r
+                                IsHitTestVisible="False"\r
+                                Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"\r
+                                StrokeEndLineCap="Square"\r
+                                StrokeDashArray="1,1"\r
+                                Opacity="0"\r
+                                StrokeDashOffset="0.5"/>\r
+\r
+                        <VisualStateManager.VisualStateGroups>\r
+                            <VisualStateGroup x:Name="ApplicationViewStates">\r
+                                <VisualState x:Name="FullScreenLandscape"/>\r
+                                <VisualState x:Name="Filled"/>\r
+                                <VisualState x:Name="FullScreenPortrait">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Width">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="60"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Snapped">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Width">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="60"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                            </VisualStateGroup>\r
+                            <VisualStateGroup x:Name="CommonStates">\r
+                                <VisualState x:Name="Normal"/>\r
+                                <VisualState x:Name="PointerOver">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPointerOverBackgroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPointerOverForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Pressed">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Disabled">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                            </VisualStateGroup>\r
+                            <VisualStateGroup x:Name="FocusStates">\r
+                                <VisualState x:Name="Focused">\r
+                                    <Storyboard>\r
+                                        <DoubleAnimation\r
+                                                Storyboard.TargetName="FocusVisualWhite"\r
+                                                Storyboard.TargetProperty="Opacity"\r
+                                                To="1"\r
+                                                Duration="0"/>\r
+                                        <DoubleAnimation\r
+                                                Storyboard.TargetName="FocusVisualBlack"\r
+                                                Storyboard.TargetProperty="Opacity"\r
+                                                To="1"\r
+                                                Duration="0"/>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Unfocused" />\r
+                                <VisualState x:Name="PointerFocused" />\r
+                            </VisualStateGroup>\r
+                            <VisualStateGroup x:Name="CheckStates">\r
+                                <VisualState x:Name="Checked">\r
+                                    <Storyboard>\r
+                                        <DoubleAnimation Duration="0" To="0" Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Opacity"/>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundCheckedGlyph" Storyboard.TargetProperty="Visibility">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Unchecked"/>\r
+                                <VisualState x:Name="Indeterminate"/>\r
+                            </VisualStateGroup>\r
+                        </VisualStateManager.VisualStateGroups>\r
+                    </Grid>\r
+                </ControlTemplate>\r
+            </Setter.Value>\r
+        </Setter>\r
+    </Style>\r
+\r
+    <!-- \r
+        Standard AppBarButton Styles for use with Button and ToggleButton\r
+    \r
+        An AppBarButton Style is provided for each of the glyphs in the Segoe UI Symbol font.  \r
+        Uncomment any style you reference (as not all may be required).\r
+    -->\r
+\r
+    <!--\r
+    \r
+    <Style x:Key="SkipBackAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SkipBackAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Skip Back"/>\r
+        <Setter Property="Content" Value="&#xE100;"/>\r
+    </Style>\r
+    <Style x:Key="SkipAheadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SkipAheadAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Skip Ahead"/>\r
+        <Setter Property="Content" Value="&#xE101;"/>\r
+    </Style>\r
+    <Style x:Key="PlayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PlayAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Play"/>\r
+        <Setter Property="Content" Value="&#xE102;"/>\r
+    </Style>\r
+    <Style x:Key="PauseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PauseAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Pause"/>\r
+        <Setter Property="Content" Value="&#xE103;"/>\r
+    </Style>\r
+    <Style x:Key="EditAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="EditAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Edit"/>\r
+        <Setter Property="Content" Value="&#xE104;"/>\r
+    </Style>\r
+    <Style x:Key="SaveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SaveAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Save"/>\r
+        <Setter Property="Content" Value="&#xE105;"/>\r
+    </Style>\r
+    <Style x:Key="DeleteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DeleteAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Delete"/>\r
+        <Setter Property="Content" Value="&#xE106;"/>\r
+    </Style>\r
+    <Style x:Key="DiscardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DiscardAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Discard"/>\r
+        <Setter Property="Content" Value="&#xE107;"/>\r
+    </Style>\r
+    <Style x:Key="RemoveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="RemoveAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Remove"/>\r
+        <Setter Property="Content" Value="&#xE108;"/>\r
+    </Style>\r
+    <Style x:Key="AddAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AddAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Add"/>\r
+        <Setter Property="Content" Value="&#xE109;"/>\r
+    </Style>\r
+    <Style x:Key="NoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="NoAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="No"/>\r
+        <Setter Property="Content" Value="&#xE10A;"/>\r
+    </Style>\r
+    <Style x:Key="YesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="YesAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Yes"/>\r
+        <Setter Property="Content" Value="&#xE10B;"/>\r
+    </Style>\r
+    <Style x:Key="MoreAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MoreAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="More"/>\r
+        <Setter Property="Content" Value="&#xE10C;"/>\r
+    </Style>\r
+    <Style x:Key="RedoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="RedoAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Redo"/>\r
+        <Setter Property="Content" Value="&#xE10D;"/>\r
+    </Style>\r
+    <Style x:Key="UndoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="UndoAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Undo"/>\r
+        <Setter Property="Content" Value="&#xE10E;"/>\r
+    </Style>\r
+    <Style x:Key="HomeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="HomeAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Home"/>\r
+        <Setter Property="Content" Value="&#xE10F;"/>\r
+    </Style>\r
+    <Style x:Key="OutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="OutAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Out"/>\r
+        <Setter Property="Content" Value="&#xE110;"/>\r
+    </Style>\r
+    <Style x:Key="NextAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="NextAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Next"/>\r
+        <Setter Property="Content" Value="&#xE111;"/>\r
+    </Style>\r
+    <Style x:Key="PreviousAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PreviousAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Previous"/>\r
+        <Setter Property="Content" Value="&#xE112;"/>\r
+    </Style>\r
+    <Style x:Key="FavoriteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FavoriteAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Favorite"/>\r
+        <Setter Property="Content" Value="&#xE113;"/>\r
+    </Style>\r
+    <Style x:Key="PhotoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PhotoAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Photo"/>\r
+        <Setter Property="Content" Value="&#xE114;"/>\r
+    </Style>\r
+    <Style x:Key="SettingsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SettingsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Settings"/>\r
+        <Setter Property="Content" Value="&#xE115;"/>\r
+    </Style>\r
+    -->\r
+\r
+    <!--\r
+    <Style x:Key="VideoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="VideoAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Video"/>\r
+        <Setter Property="Content" Value="&#xE116;"/>\r
+    </Style>\r
+    <Style x:Key="RefreshAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="RefreshAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Refresh"/>\r
+        <Setter Property="Content" Value="&#xE117;"/>\r
+    </Style>\r
+    <Style x:Key="DownloadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DownloadAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Download"/>\r
+        <Setter Property="Content" Value="&#xE118;"/>\r
+    </Style>\r
+    <Style x:Key="MailAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MailAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Mail"/>\r
+        <Setter Property="Content" Value="&#xE119;"/>\r
+    </Style>\r
+    <Style x:Key="SearchAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SearchAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Search"/>\r
+        <Setter Property="Content" Value="&#xE11A;"/>\r
+    </Style>\r
+    <Style x:Key="HelpAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="HelpAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Help"/>\r
+        <Setter Property="Content" Value="&#xE11B;"/>\r
+    </Style>\r
+    <Style x:Key="UploadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="UploadAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Upload"/>\r
+        <Setter Property="Content" Value="&#xE11C;"/>\r
+    </Style>\r
+    <Style x:Key="EmojiAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="EmojiAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Emoji"/>\r
+        <Setter Property="Content" Value="&#xE11D;"/>\r
+    </Style>\r
+    <Style x:Key="TwoPageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="TwoPageAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Two Page"/>\r
+        <Setter Property="Content" Value="&#xE11E;"/>\r
+    </Style>\r
+    <Style x:Key="LeaveChatAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="LeaveChatAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Upload"/>\r
+        <Setter Property="Content" Value="&#xE11F;"/>\r
+    </Style>\r
+    <Style x:Key="MailForwardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MailForwardAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Forward Mail"/>\r
+        <Setter Property="Content" Value="&#xE120;"/>\r
+    </Style>\r
+    <Style x:Key="ClockAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ClockAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Clock"/>\r
+        <Setter Property="Content" Value="&#xE121;"/>\r
+    </Style>\r
+    <Style x:Key="SendAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SendAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Send"/>\r
+        <Setter Property="Content" Value="&#xE122;"/>\r
+    </Style>\r
+    <Style x:Key="CropAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CropAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Crop"/>\r
+        <Setter Property="Content" Value="&#xE123;"/>\r
+    </Style>\r
+    <Style x:Key="RotateCameraAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="RotateCameraAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Rotate Camera"/>\r
+        <Setter Property="Content" Value="&#xE124;"/>\r
+    </Style>\r
+    <Style x:Key="PeopleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PeopleAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="People"/>\r
+        <Setter Property="Content" Value="&#xE125;"/>\r
+    </Style>\r
+    <Style x:Key="ClosePaneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ClosePaneAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Close Pane"/>\r
+        <Setter Property="Content" Value="&#xE126;"/>\r
+    </Style>\r
+    <Style x:Key="OpenPaneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenPaneAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Open Pane"/>\r
+        <Setter Property="Content" Value="&#xE127;"/>\r
+    </Style>\r
+    -->\r
+\r
+    <!--\r
+    <Style x:Key="WorldAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="WorldAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="World"/>\r
+        <Setter Property="Content" Value="&#xE128;"/>\r
+    </Style>\r
+    <Style x:Key="FlagAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FlagAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Flag"/>\r
+        <Setter Property="Content" Value="&#xE129;"/>\r
+    </Style>\r
+    <Style x:Key="PreviewLinkAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PreviewLinkAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Preview Link"/>\r
+        <Setter Property="Content" Value="&#xE12A;"/>\r
+    </Style>\r
+    <Style x:Key="GlobeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="GlobeAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Globe"/>\r
+        <Setter Property="Content" Value="&#xE12B;"/>\r
+    </Style>\r
+    <Style x:Key="TrimAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="TrimAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Trim"/>\r
+        <Setter Property="Content" Value="&#xE12C;"/>\r
+    </Style>\r
+    <Style x:Key="AttachCameraAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AttachCameraAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Attach Camera"/>\r
+        <Setter Property="Content" Value="&#xE12D;"/>\r
+    </Style>\r
+    <Style x:Key="ZoomInAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomInAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Zoom In"/>\r
+        <Setter Property="Content" Value="&#xE12E;"/>\r
+    </Style>\r
+    <Style x:Key="BookmarksAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="BookmarksAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Bookmarks"/>\r
+        <Setter Property="Content" Value="&#xE12F;"/>\r
+    </Style>\r
+    <Style x:Key="DocumentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DocumentAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Document"/>\r
+        <Setter Property="Content" Value="&#xE130;"/>\r
+    </Style>\r
+    <Style x:Key="ProtectedDocumentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ProtectedDocumentAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Protected Document"/>\r
+        <Setter Property="Content" Value="&#xE131;"/>\r
+    </Style>\r
+    <Style x:Key="PageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PageAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Page"/>\r
+        <Setter Property="Content" Value="&#xE132;"/>\r
+    </Style>\r
+    <Style x:Key="BulletsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="BulletsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Bullets"/>\r
+        <Setter Property="Content" Value="&#xE133;"/>\r
+    </Style>\r
+    <Style x:Key="CommentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CommentAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Comment"/>\r
+        <Setter Property="Content" Value="&#xE134;"/>\r
+    </Style>\r
+    <Style x:Key="Mail2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="Mail2AppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Mail2"/>\r
+        <Setter Property="Content" Value="&#xE135;"/>\r
+    </Style>\r
+    <Style x:Key="ContactInfoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactInfoAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Contact Info"/>\r
+        <Setter Property="Content" Value="&#xE136;"/>\r
+    </Style>\r
+    <Style x:Key="HangUpAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="HangUpAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Hang Up"/>\r
+        <Setter Property="Content" Value="&#xE137;"/>\r
+    </Style>\r
+    <Style x:Key="ViewAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ViewAllAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="View All"/>\r
+        <Setter Property="Content" Value="&#xE138;"/>\r
+    </Style>\r
+    <Style x:Key="MapPinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MapPinAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Map Pin"/>\r
+        <Setter Property="Content" Value="&#xE139;"/>\r
+    </Style>\r
+    <Style x:Key="PhoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PhoneAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Phone"/>\r
+        <Setter Property="Content" Value="&#xE13A;"/>\r
+    </Style>\r
+    <Style x:Key="VideoChatAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="VideoChatAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Video Chat"/>\r
+        <Setter Property="Content" Value="&#xE13B;"/>\r
+    </Style>\r
+    <Style x:Key="SwitchAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SwitchAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Switch"/>\r
+        <Setter Property="Content" Value="&#xE13C;"/>\r
+    </Style>\r
+    <Style x:Key="ContactAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Contact"/>\r
+        <Setter Property="Content" Value="&#xE13D;"/>\r
+    </Style>\r
+\r
+    -->\r
+\r
+    <!--\r
+\r
+    <Style x:Key="RenameAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="RenameAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Rename"/>\r
+        <Setter Property="Content" Value="&#xE13E;"/>\r
+    </Style>\r
+    <Style x:Key="PinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PinAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Pin"/>\r
+        <Setter Property="Content" Value="&#xE141;"/>\r
+    </Style>\r
+    <Style x:Key="MusicInfoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MusicInfoAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Music Info"/>\r
+        <Setter Property="Content" Value="&#xE142;"/>\r
+    </Style>\r
+    <Style x:Key="GoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="GoAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Go"/>\r
+        <Setter Property="Content" Value="&#xE143;"/>\r
+    </Style>\r
+    <Style x:Key="KeyboardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="KeyboardAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Keyboard"/>\r
+        <Setter Property="Content" Value="&#xE144;"/>\r
+    </Style>\r
+    <Style x:Key="DockLeftAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DockLeftAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Dock Left"/>\r
+        <Setter Property="Content" Value="&#xE145;"/>\r
+    </Style>\r
+    <Style x:Key="DockRightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DockRightAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Dock Right"/>\r
+        <Setter Property="Content" Value="&#xE146;"/>\r
+    </Style>\r
+    <Style x:Key="DockBottomAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DockBottomAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Dock Bottom"/>\r
+        <Setter Property="Content" Value="&#xE147;"/>\r
+    </Style>\r
+    <Style x:Key="RemoteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="RemoteAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Remote"/>\r
+        <Setter Property="Content" Value="&#xE148;"/>\r
+    </Style>\r
+    <Style x:Key="SyncAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SyncAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Sync"/>\r
+        <Setter Property="Content" Value="&#xE149;"/>\r
+    </Style>\r
+    <Style x:Key="RotateAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="RotateAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Rotate"/>\r
+        <Setter Property="Content" Value="&#xE14A;"/>\r
+    </Style>\r
+    <Style x:Key="ShuffleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ShuffleAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Shuffle"/>\r
+        <Setter Property="Content" Value="&#xE14B;"/>\r
+    </Style>\r
+    <Style x:Key="ListAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ListAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="List"/>\r
+        <Setter Property="Content" Value="&#xE14C;"/>\r
+    </Style>\r
+    <Style x:Key="ShopAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ShopAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Shop"/>\r
+        <Setter Property="Content" Value="&#xE14D;"/>\r
+    </Style>\r
+    <Style x:Key="SelectAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SelectAllAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Select All"/>\r
+        <Setter Property="Content" Value="&#xE14E;"/>\r
+    </Style>\r
+    <Style x:Key="OrientationAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="OrientationAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Orientation"/>\r
+        <Setter Property="Content" Value="&#xE14F;"/>\r
+    </Style>\r
+    <Style x:Key="ImportAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Import"/>\r
+        <Setter Property="Content" Value="&#xE150;"/>\r
+    </Style>\r
+    <Style x:Key="ImportAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportAllAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Import All"/>\r
+        <Setter Property="Content" Value="&#xE151;"/>\r
+    </Style>\r
+    <Style x:Key="BrowsePhotosAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="BrowsePhotosAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Browse Photos"/>\r
+        <Setter Property="Content" Value="&#xE155;"/>\r
+    </Style>\r
+    <Style x:Key="WebcamAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="WebcamAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Webcam"/>\r
+        <Setter Property="Content" Value="&#xE156;"/>\r
+    </Style>\r
+    -->\r
+\r
+    <!--\r
+    <Style x:Key="PicturesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PicturesAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Pictures"/>\r
+        <Setter Property="Content" Value="&#xE158;"/>\r
+    </Style>\r
+    <Style x:Key="SaveLocalAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SaveLocalAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Save Local"/>\r
+        <Setter Property="Content" Value="&#xE159;"/>\r
+    </Style>\r
+    <Style x:Key="CaptionAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CaptionAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Caption"/>\r
+        <Setter Property="Content" Value="&#xE15A;"/>\r
+    </Style>\r
+    <Style x:Key="StopAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="StopAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Stop"/>\r
+        <Setter Property="Content" Value="&#xE15B;"/>\r
+    </Style>\r
+    <Style x:Key="ShowResultsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ShowResultsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Show Results"/>\r
+        <Setter Property="Content" Value="&#xE15C;"/>\r
+    </Style>\r
+    <Style x:Key="VolumeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="VolumeAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Volume"/>\r
+        <Setter Property="Content" Value="&#xE15D;"/>\r
+    </Style>\r
+    <Style x:Key="RepairAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="RepairAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Repair"/>\r
+        <Setter Property="Content" Value="&#xE15E;"/>\r
+    </Style>\r
+    <Style x:Key="MessageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MessageAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Message"/>\r
+        <Setter Property="Content" Value="&#xE15F;"/>\r
+    </Style>\r
+    <Style x:Key="Page2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="Page2AppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Page2"/>\r
+        <Setter Property="Content" Value="&#xE160;"/>\r
+    </Style>\r
+    <Style x:Key="CalendarDayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarDayAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Day"/>\r
+        <Setter Property="Content" Value="&#xE161;"/>\r
+    </Style>\r
+    <Style x:Key="CalendarWeekAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarWeekAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Week"/>\r
+        <Setter Property="Content" Value="&#xE162;"/>\r
+    </Style>\r
+    <Style x:Key="CalendarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Calendar"/>\r
+        <Setter Property="Content" Value="&#xE163;"/>\r
+    </Style>\r
+    <Style x:Key="CharactersAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CharactersAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Characters"/>\r
+        <Setter Property="Content" Value="&#xE164;"/>\r
+    </Style>\r
+    <Style x:Key="MailReplyAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MailReplyAllAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Reply All"/>\r
+        <Setter Property="Content" Value="&#xE165;"/>\r
+    </Style>\r
+    <Style x:Key="ReadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ReadAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Read"/>\r
+        <Setter Property="Content" Value="&#xE166;"/>\r
+    </Style>\r
+    <Style x:Key="LinkAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="LinkAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Link"/>\r
+        <Setter Property="Content" Value="&#xE167;"/>\r
+    </Style>\r
+    <Style x:Key="AccountsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AccountsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Accounts"/>\r
+        <Setter Property="Content" Value="&#xE168;"/>\r
+    </Style>\r
+    <Style x:Key="ShowBccAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ShowBccAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Show Bcc"/>\r
+        <Setter Property="Content" Value="&#xE169;"/>\r
+    </Style>\r
+    <Style x:Key="HideBccAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="HideBccAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Hide Bcc"/>\r
+        <Setter Property="Content" Value="&#xE16A;"/>\r
+    </Style>\r
+    -->\r
+\r
+    <!--\r
+    <Style x:Key="CutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CutAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Cut"/>\r
+        <Setter Property="Content" Value="&#xE16B;"/>\r
+    </Style>\r
+    <Style x:Key="AttachAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AttachAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Attach"/>\r
+        <Setter Property="Content" Value="&#xE16C;"/>\r
+    </Style>\r
+    <Style x:Key="PasteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PasteAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Paste"/>\r
+        <Setter Property="Content" Value="&#xE16D;"/>\r
+    </Style>\r
+    <Style x:Key="FilterAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FilterAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Filter"/>\r
+        <Setter Property="Content" Value="&#xE16E;"/>\r
+    </Style>\r
+    <Style x:Key="CopyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CopyAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Copy"/>\r
+        <Setter Property="Content" Value="&#xE16F;"/>\r
+    </Style>\r
+    <Style x:Key="Emoji2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="Emoji2AppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Emoji2"/>\r
+        <Setter Property="Content" Value="&#xE170;"/>\r
+    </Style>\r
+    <Style x:Key="ImportantAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportantAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Important"/>\r
+        <Setter Property="Content" Value="&#xE171;"/>\r
+    </Style>\r
+    <Style x:Key="MailReplyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MailReplyAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Reply"/>\r
+        <Setter Property="Content" Value="&#xE172;"/>\r
+    </Style>\r
+    <Style x:Key="SlideShowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SlideShowAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Slideshow"/>\r
+        <Setter Property="Content" Value="&#xE173;"/>\r
+    </Style>\r
+    <Style x:Key="SortAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SortAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Sort"/>\r
+        <Setter Property="Content" Value="&#xE174;"/>\r
+    </Style>\r
+    <Style x:Key="ManageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ManageAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Manage"/>\r
+        <Setter Property="Content" Value="&#xE178;"/>\r
+    </Style>\r
+    <Style x:Key="AllAppsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AllAppsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="All Apps"/>\r
+        <Setter Property="Content" Value="&#xE179;"/>\r
+    </Style>\r
+    <Style x:Key="DisconnectDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DisconnectDriveAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Disconnect Drive"/>\r
+        <Setter Property="Content" Value="&#xE17A;"/>\r
+    </Style>\r
+    <Style x:Key="MapDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MapDriveAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Map Drive"/>\r
+        <Setter Property="Content" Value="&#xE17B;"/>\r
+    </Style>\r
+    <Style x:Key="NewWindowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="NewWindowAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="New Window"/>\r
+        <Setter Property="Content" Value="&#xE17C;"/>\r
+    </Style>\r
+    <Style x:Key="OpenWithAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenWithAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Open With"/>\r
+        <Setter Property="Content" Value="&#xE17D;"/>\r
+    </Style>\r
+    <Style x:Key="ContactPresenceAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactPresenceAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Presence"/>\r
+        <Setter Property="Content" Value="&#xE181;"/>\r
+    </Style>\r
+    <Style x:Key="PriorityAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PriorityAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Priority"/>\r
+        <Setter Property="Content" Value="&#xE182;"/>\r
+    </Style>\r
+    <Style x:Key="UploadSkyDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="UploadSkyDriveAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Skydrive"/>\r
+        <Setter Property="Content" Value="&#xE183;"/>\r
+    </Style>\r
+    <Style x:Key="GoToTodayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="GoToTodayAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Today"/>\r
+        <Setter Property="Content" Value="&#xE184;"/>\r
+    </Style>\r
+    <Style x:Key="FontAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FontAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Font"/>\r
+        <Setter Property="Content" Value="&#xE185;"/>\r
+    </Style>\r
+\r
+    -->\r
+\r
+    <!--\r
+\r
+    <Style x:Key="FontColorAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FontColorAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Font Color"/>\r
+        <Setter Property="Content" Value="&#xE186;"/>\r
+    </Style>\r
+    <Style x:Key="Contact2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="Contact2AppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Contact"/>\r
+        <Setter Property="Content" Value="&#xE187;"/>\r
+    </Style>\r
+    <Style x:Key="FolderppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FolderAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Folder"/>\r
+        <Setter Property="Content" Value="&#xE188;"/>\r
+    </Style>\r
+    <Style x:Key="AudioAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AudioAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Audio"/>\r
+        <Setter Property="Content" Value="&#xE189;"/>\r
+    </Style>\r
+    <Style x:Key="PlaceholderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PlaceholderAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Placeholder"/>\r
+        <Setter Property="Content" Value="&#xE18A;"/>\r
+    </Style>\r
+    <Style x:Key="ViewAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ViewAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="View"/>\r
+        <Setter Property="Content" Value="&#xE18B;"/>\r
+    </Style>\r
+    <Style x:Key="SetLockScreenAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SetLockscreenAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Set Lockscreen"/>\r
+        <Setter Property="Content" Value="&#xE18C;"/>\r
+    </Style>\r
+    <Style x:Key="SetTitleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SetTitleAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Set Title"/>\r
+        <Setter Property="Content" Value="&#xE18D;"/>\r
+    </Style>\r
+    <Style x:Key="CcAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CcAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Cc"/>\r
+        <Setter Property="Content" Value="&#xE190;"/>\r
+    </Style>\r
+    <Style x:Key="StopSlideShowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="StopSlideshowAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Stop Slideshow"/>\r
+        <Setter Property="Content" Value="&#xE191;"/>\r
+    </Style>\r
+    <Style x:Key="PermissionsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PermissionsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Permisions"/>\r
+        <Setter Property="Content" Value="&#xE192;"/>\r
+    </Style>\r
+    <Style x:Key="HighlightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="HighlightAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Highlight"/>\r
+        <Setter Property="Content" Value="&#xE193;"/>\r
+    </Style>\r
+    <Style x:Key="DisableUpdatesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DisableUpdatesAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Disable Updates"/>\r
+        <Setter Property="Content" Value="&#xE194;"/>\r
+    </Style>\r
+    <Style x:Key="UnfavoriteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="UnfavoriteAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Unfavorite"/>\r
+        <Setter Property="Content" Value="&#xE195;"/>\r
+    </Style>\r
+    <Style x:Key="UnPinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="UnPinAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Unpin"/>\r
+        <Setter Property="Content" Value="&#xE196;"/>\r
+    </Style>\r
+    <Style x:Key="OpenLocalAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenLocalAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Open Loal"/>\r
+        <Setter Property="Content" Value="&#xE197;"/>\r
+    </Style>\r
+    <Style x:Key="MuteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MuteAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Mute"/>\r
+        <Setter Property="Content" Value="&#xE198;"/>\r
+    </Style>\r
+    <Style x:Key="ItalicAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ItalicAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Italic"/>\r
+        <Setter Property="Content" Value="&#xE199;"/>\r
+    </Style>\r
+    -->\r
+\r
+    <!--\r
+    <Style x:Key="UnderlineAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="UnderlineAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Underline"/>\r
+        <Setter Property="Content" Value="&#xE19A;"/>\r
+    </Style>\r
+    <Style x:Key="BoldAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="BoldAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Bold"/>\r
+        <Setter Property="Content" Value="&#xE19B;"/>\r
+    </Style>\r
+    <Style x:Key="MoveToFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MoveToFolderAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Move to Folder"/>\r
+        <Setter Property="Content" Value="&#xE19C;"/>\r
+    </Style>\r
+    <Style x:Key="LikeDislikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="LikeDislikeAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Like/Dislike"/>\r
+        <Setter Property="Content" Value="&#xE19D;"/>\r
+    </Style>\r
+    <Style x:Key="DislikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DislikeAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Dislike"/>\r
+        <Setter Property="Content" Value="&#xE19E;"/>\r
+    </Style>\r
+    <Style x:Key="LikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="LikeAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Like"/>\r
+        <Setter Property="Content" Value="&#xE19F;"/>\r
+    </Style>\r
+    <Style x:Key="AlignRightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignRightAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Align Right"/>\r
+        <Setter Property="Content" Value="&#xE1A0;"/>\r
+    </Style>\r
+    <Style x:Key="AlignCenterAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignCenterAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Align Center"/>\r
+        <Setter Property="Content" Value="&#xE1A1;"/>\r
+    </Style>\r
+    <Style x:Key="AlignLeftAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignLeftAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Align Left"/>\r
+        <Setter Property="Content" Value="&#xE1A2;"/>\r
+    </Style>\r
+    <Style x:Key="ZoomAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Zoom"/>\r
+        <Setter Property="Content" Value="&#xE1A3;"/>\r
+    </Style>\r
+    <Style x:Key="ZoomOutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomOutAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Zoom Out"/>\r
+        <Setter Property="Content" Value="&#xE1A4;"/>\r
+    </Style>\r
+    <Style x:Key="OpenFileAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenFileAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Open File"/>\r
+        <Setter Property="Content" Value="&#xE1A5;"/>\r
+    </Style>\r
+    <Style x:Key="OtherUserAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="OtherUserAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Other User"/>\r
+        <Setter Property="Content" Value="&#xE1A6;"/>\r
+    </Style>\r
+    <Style x:Key="AdminAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AdminAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Admin"/>\r
+        <Setter Property="Content" Value="&#xE1A7;"/>\r
+    </Style>\r
+    <Style x:Key="StreetAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="StreetAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Street"/>\r
+        <Setter Property="Content" Value="&#xE1C3;"/>\r
+    </Style>\r
+    <Style x:Key="MapAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MapAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Map"/>\r
+        <Setter Property="Content" Value="&#xE1C4;"/>\r
+    </Style>\r
+    <Style x:Key="ClearSelectionAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ClearSelectionAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Clear Selection"/>\r
+        <Setter Property="Content" Value="&#xE1C5;"/>\r
+    </Style>\r
+    <Style x:Key="FontDecreaseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FontDecreaseAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Decrease Font"/>\r
+        <Setter Property="Content" Value="&#xE1C6;"/>\r
+    </Style>\r
+    <Style x:Key="FontIncreaseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FontIncreaseAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Increase Font"/>\r
+        <Setter Property="Content" Value="&#xE1C7;"/>\r
+    </Style>\r
+    <Style x:Key="FontSizeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FontSizeAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Font Size"/>\r
+        <Setter Property="Content" Value="&#xE1C8;"/>\r
+    </Style>\r
+    -->\r
+\r
+    <!--\r
+    <Style x:Key="CellphoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CellphoneAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Cellphone"/>\r
+        <Setter Property="Content" Value="&#xE1C9;"/>\r
+    </Style>\r
+    <Style x:Key="ReshareAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ReshareAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Reshare"/>\r
+        <Setter Property="Content" Value="&#xE1CA;"/>\r
+    </Style>\r
+    <Style x:Key="TagAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="TagAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Tag"/>\r
+        <Setter Property="Content" Value="&#xE1CB;"/>\r
+    </Style>\r
+    <Style x:Key="RepeatOneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="RepeatOneAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Repeat Once"/>\r
+        <Setter Property="Content" Value="&#xE1CC;"/>\r
+    </Style>\r
+    <Style x:Key="RepeatAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="RepeatAllAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Repeat All"/>\r
+        <Setter Property="Content" Value="&#xE1CD;"/>\r
+    </Style>\r
+    <Style x:Key="OutlineStarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="OutlineStarAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Outline Star"/>\r
+        <Setter Property="Content" Value="&#xE1CE;"/>\r
+    </Style>\r
+    <Style x:Key="SolidStarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SolidStarAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Solid Star"/>\r
+        <Setter Property="Content" Value="&#xE1CF;"/>\r
+    </Style>\r
+    <Style x:Key="CalculatorAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CalculatorAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Calculator"/>\r
+        <Setter Property="Content" Value="&#xE1D0;"/>\r
+    </Style>\r
+    <Style x:Key="DirectionsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="DirectionsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Directions"/>\r
+        <Setter Property="Content" Value="&#xE1D1;"/>\r
+    </Style>\r
+    <Style x:Key="TargetAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="TargetAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Target"/>\r
+        <Setter Property="Content" Value="&#xE1D2;"/>\r
+    </Style>\r
+    <Style x:Key="LibraryAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="LibraryAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Library"/>\r
+        <Setter Property="Content" Value="&#xE1D3;"/>\r
+    </Style>\r
+    <Style x:Key="PhonebookAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PhonebookAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Phonebook"/>\r
+        <Setter Property="Content" Value="&#xE1D4;"/>\r
+    </Style>\r
+    <Style x:Key="MemoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MemoAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Memo"/>\r
+        <Setter Property="Content" Value="&#xE1D5;"/>\r
+    </Style>\r
+    <Style x:Key="MicrophoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="MicrophoneAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Microphone"/>\r
+        <Setter Property="Content" Value="&#xE1D6;"/>\r
+    </Style>\r
+    <Style x:Key="PostUpdateAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="PostUpdateAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Post Update"/>\r
+        <Setter Property="Content" Value="&#xE1D7;"/>\r
+    </Style>\r
+    <Style x:Key="BackToWindowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="BackToWindowAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Back to Window"/>\r
+        <Setter Property="Content" Value="&#xE1D8;"/>\r
+    </Style>\r
+    -->\r
+\r
+    <!--\r
+    <Style x:Key="FullScreenAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FullScreenAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Full Screen"/>\r
+        <Setter Property="Content" Value="&#xE1D9;"/>\r
+    </Style>\r
+    <Style x:Key="NewFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="NewFolderAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="New Folder"/>\r
+        <Setter Property="Content" Value="&#xE1DA;"/>\r
+    </Style>\r
+    <Style x:Key="CalendarReplyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarReplyAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Calendar Reply"/>\r
+        <Setter Property="Content" Value="&#xE1DB;"/>\r
+    </Style>\r
+    <Style x:Key="UnsyncFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="UnsyncFolderAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Unsync Folder"/>\r
+        <Setter Property="Content" Value="&#xE1DD;"/>\r
+    </Style>\r
+    <Style x:Key="ReportHackedAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ReportHackedAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Report Hacked"/>\r
+        <Setter Property="Content" Value="&#xE1DE;"/>\r
+    </Style>\r
+    <Style x:Key="SyncFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SyncFolderAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Sync Folder"/>\r
+        <Setter Property="Content" Value="&#xE1DF;"/>\r
+    </Style>\r
+    <Style x:Key="BlockContactAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="Block ContactAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="BlockContact"/>\r
+        <Setter Property="Content" Value="&#xE1E0;"/>\r
+    </Style>\r
+    <Style x:Key="SwitchAppsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="SwitchAppsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Switch Apps"/>\r
+        <Setter Property="Content" Value="&#xE1E1;"/>\r
+    </Style>\r
+    <Style x:Key="AddFriendAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="AddFriendAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Add Friend"/>\r
+        <Setter Property="Content" Value="&#xE1E2;"/>\r
+    </Style>\r
+    <Style x:Key="TouchPointerAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="TouchPointerAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Touch Pointer"/>\r
+        <Setter Property="Content" Value="&#xE1E3;"/>\r
+    </Style>\r
+    <Style x:Key="GoToStartAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="GoToStartAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Go to Start"/>\r
+        <Setter Property="Content" Value="&#xE1E4;"/>\r
+    </Style>\r
+    <Style x:Key="ZeroBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ZeroBarsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Zero Bars"/>\r
+        <Setter Property="Content" Value="&#xE1E5;"/>\r
+    </Style>\r
+    <Style x:Key="OneBarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="OneBarAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="One Bar"/>\r
+        <Setter Property="Content" Value="&#xE1E6;"/>\r
+    </Style>\r
+    <Style x:Key="TwoBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="TwoBarsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Two Bars"/>\r
+        <Setter Property="Content" Value="&#xE1E7;"/>\r
+    </Style>\r
+    <Style x:Key="ThreeBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="ThreeBarsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Three Bars"/>\r
+        <Setter Property="Content" Value="&#xE1E8;"/>\r
+    </Style>\r
+    <Style x:Key="FourBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">\r
+        <Setter Property="AutomationProperties.AutomationId" Value="FourBarsAppBarButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Four Bars"/>\r
+        <Setter Property="Content" Value="&#xE1E9;"/>\r
+    </Style>\r
+\r
+    -->\r
+\r
+    <!-- Title area styles -->\r
+\r
+    <Style x:Key="PageHeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource HeaderTextStyle}">\r
+        <Setter Property="TextWrapping" Value="NoWrap"/>\r
+        <Setter Property="VerticalAlignment" Value="Bottom"/>\r
+        <Setter Property="Margin" Value="0,0,30,40"/>\r
+    </Style>\r
+\r
+    <Style x:Key="PageSubheaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource SubheaderTextStyle}">\r
+        <Setter Property="TextWrapping" Value="NoWrap"/>\r
+        <Setter Property="VerticalAlignment" Value="Bottom"/>\r
+        <Setter Property="Margin" Value="0,0,0,40"/>\r
+    </Style>\r
+\r
+    <Style x:Key="SnappedPageHeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource PageSubheaderTextStyle}">\r
+        <Setter Property="Margin" Value="0,0,18,40"/>\r
+    </Style>\r
+\r
+    <!--\r
+        BackButtonStyle is used to style a Button for use in the title area of a page.  Margins appropriate for\r
+        the conventional page layout are included as part of the style.\r
+    -->\r
+    <Style x:Key="BackButtonStyle" TargetType="Button">\r
+        <Setter Property="MinWidth" Value="0"/>\r
+        <Setter Property="Width" Value="48"/>\r
+        <Setter Property="Height" Value="48"/>\r
+        <Setter Property="Margin" Value="36,0,36,36"/>\r
+        <Setter Property="VerticalAlignment" Value="Bottom"/>\r
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>\r
+        <Setter Property="FontWeight" Value="Normal"/>\r
+        <Setter Property="FontSize" Value="56"/>\r
+        <Setter Property="AutomationProperties.AutomationId" Value="BackButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Back"/>\r
+        <Setter Property="AutomationProperties.ItemType" Value="Navigation Button"/>\r
+        <Setter Property="Template">\r
+            <Setter.Value>\r
+                <ControlTemplate TargetType="Button">\r
+                    <Grid x:Name="RootGrid">\r
+                        <Grid Margin="-1,-16,0,0">\r
+                            <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" Foreground="{StaticResource BackButtonBackgroundThemeBrush}"/>\r
+                            <TextBlock x:Name="NormalGlyph" Text="{StaticResource BackButtonGlyph}" Foreground="{StaticResource BackButtonForegroundThemeBrush}"/>\r
+                            <TextBlock x:Name="ArrowGlyph" Text="&#xE0A6;" Foreground="{StaticResource BackButtonPressedForegroundThemeBrush}" Opacity="0"/>\r
+                        </Grid>\r
+                        <Rectangle\r
+                            x:Name="FocusVisualWhite"\r
+                            IsHitTestVisible="False"\r
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"\r
+                            StrokeEndLineCap="Square"\r
+                            StrokeDashArray="1,1"\r
+                            Opacity="0"\r
+                            StrokeDashOffset="1.5"/>\r
+                        <Rectangle\r
+                            x:Name="FocusVisualBlack"\r
+                            IsHitTestVisible="False"\r
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"\r
+                            StrokeEndLineCap="Square"\r
+                            StrokeDashArray="1,1"\r
+                            Opacity="0"\r
+                            StrokeDashOffset="0.5"/>\r
+\r
+                        <VisualStateManager.VisualStateGroups>\r
+                            <VisualStateGroup x:Name="CommonStates">\r
+                                <VisualState x:Name="Normal" />\r
+                                <VisualState x:Name="PointerOver">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverBackgroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Pressed">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <DoubleAnimation\r
+                                            Storyboard.TargetName="ArrowGlyph"\r
+                                            Storyboard.TargetProperty="Opacity"\r
+                                            To="1"\r
+                                            Duration="0"/>\r
+                                        <DoubleAnimation\r
+                                            Storyboard.TargetName="NormalGlyph"\r
+                                            Storyboard.TargetProperty="Opacity"\r
+                                            To="0"\r
+                                            Duration="0"/>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Disabled">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                            </VisualStateGroup>\r
+                            <VisualStateGroup x:Name="FocusStates">\r
+                                <VisualState x:Name="Focused">\r
+                                    <Storyboard>\r
+                                        <DoubleAnimation\r
+                                            Storyboard.TargetName="FocusVisualWhite"\r
+                                            Storyboard.TargetProperty="Opacity"\r
+                                            To="1"\r
+                                            Duration="0"/>\r
+                                        <DoubleAnimation\r
+                                            Storyboard.TargetName="FocusVisualBlack"\r
+                                            Storyboard.TargetProperty="Opacity"\r
+                                            To="1"\r
+                                            Duration="0"/>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Unfocused" />\r
+                                <VisualState x:Name="PointerFocused" />\r
+                            </VisualStateGroup>\r
+                        </VisualStateManager.VisualStateGroups>\r
+                    </Grid>\r
+                </ControlTemplate>\r
+            </Setter.Value>\r
+        </Setter>\r
+    </Style>\r
+\r
+    <!--\r
+        PortraitBackButtonStyle is used to style a Button for use in the title area of a portrait page.  Margins appropriate\r
+        for the conventional page layout are included as part of the style.\r
+    -->\r
+    <Style x:Key="PortraitBackButtonStyle" TargetType="Button" BasedOn="{StaticResource BackButtonStyle}">\r
+        <Setter Property="Margin" Value="26,0,26,36"/>\r
+    </Style>\r
+\r
+    <!--\r
+        SnappedBackButtonStyle is used to style a Button for use in the title area of a snapped page.  Margins appropriate\r
+        for the conventional page layout are included as part of the style.\r
+        \r
+        The obvious duplication here is necessary as the glyphs used in snapped are not merely smaller versions of the same\r
+        glyph but are actually distinct.\r
+    -->\r
+    <Style x:Key="SnappedBackButtonStyle" TargetType="Button">\r
+        <Setter Property="MinWidth" Value="0"/>\r
+        <Setter Property="Margin" Value="20,0,0,0"/>\r
+        <Setter Property="VerticalAlignment" Value="Bottom"/>\r
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>\r
+        <Setter Property="FontWeight" Value="Normal"/>\r
+        <Setter Property="FontSize" Value="26.66667"/>\r
+        <Setter Property="AutomationProperties.AutomationId" Value="BackButton"/>\r
+        <Setter Property="AutomationProperties.Name" Value="Back"/>\r
+        <Setter Property="AutomationProperties.ItemType" Value="Navigation Button"/>\r
+        <Setter Property="Template">\r
+            <Setter.Value>\r
+                <ControlTemplate TargetType="Button">\r
+                    <Grid x:Name="RootGrid" Width="36" Height="36" Margin="-3,0,7,33">\r
+                        <Grid Margin="-1,-1,0,0">\r
+                            <TextBlock x:Name="BackgroundGlyph" Text="&#xE0D4;" Foreground="{StaticResource BackButtonBackgroundThemeBrush}"/>\r
+                            <TextBlock x:Name="NormalGlyph" Text="{StaticResource BackButtonSnappedGlyph}" Foreground="{StaticResource BackButtonForegroundThemeBrush}"/>\r
+                            <TextBlock x:Name="ArrowGlyph" Text="&#xE0C4;" Foreground="{StaticResource BackButtonPressedForegroundThemeBrush}" Opacity="0"/>\r
+                        </Grid>\r
+                        <Rectangle\r
+                            x:Name="FocusVisualWhite"\r
+                            IsHitTestVisible="False"\r
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"\r
+                            StrokeEndLineCap="Square"\r
+                            StrokeDashArray="1,1"\r
+                            Opacity="0"\r
+                            StrokeDashOffset="1.5"/>\r
+                        <Rectangle\r
+                            x:Name="FocusVisualBlack"\r
+                            IsHitTestVisible="False"\r
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"\r
+                            StrokeEndLineCap="Square"\r
+                            StrokeDashArray="1,1"\r
+                            Opacity="0"\r
+                            StrokeDashOffset="0.5"/>\r
+\r
+                        <VisualStateManager.VisualStateGroups>\r
+                            <VisualStateGroup x:Name="CommonStates">\r
+                                <VisualState x:Name="Normal" />\r
+                                <VisualState x:Name="PointerOver">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverBackgroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Pressed">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonForegroundThemeBrush}"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                        <DoubleAnimation\r
+                                            Storyboard.TargetName="ArrowGlyph"\r
+                                            Storyboard.TargetProperty="Opacity"\r
+                                            To="1"\r
+                                            Duration="0"/>\r
+                                        <DoubleAnimation\r
+                                            Storyboard.TargetName="NormalGlyph"\r
+                                            Storyboard.TargetProperty="Opacity"\r
+                                            To="0"\r
+                                            Duration="0"/>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Disabled">\r
+                                    <Storyboard>\r
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">\r
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>\r
+                                        </ObjectAnimationUsingKeyFrames>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                            </VisualStateGroup>\r
+                            <VisualStateGroup x:Name="FocusStates">\r
+                                <VisualState x:Name="Focused">\r
+                                    <Storyboard>\r
+                                        <DoubleAnimation\r
+                                            Storyboard.TargetName="FocusVisualWhite"\r
+                                            Storyboard.TargetProperty="Opacity"\r
+                                            To="1"\r
+                                            Duration="0"/>\r
+                                        <DoubleAnimation\r
+                                            Storyboard.TargetName="FocusVisualBlack"\r
+                                            Storyboard.TargetProperty="Opacity"\r
+                                            To="1"\r
+                                            Duration="0"/>\r
+                                    </Storyboard>\r
+                                </VisualState>\r
+                                <VisualState x:Name="Unfocused" />\r
+                                <VisualState x:Name="PointerFocused" />\r
+                            </VisualStateGroup>\r
+                        </VisualStateManager.VisualStateGroups>\r
+                    </Grid>\r
+                </ControlTemplate>\r
+            </Setter.Value>\r
+        </Setter>\r
+    </Style>\r
+\r
+    <!-- Item templates -->\r
+\r
+    <!-- Grid-appropriate 250 pixel square item template as seen in the GroupedItemsPage and ItemsPage -->\r
+    <DataTemplate x:Key="Standard250x250ItemTemplate">\r
+        <Grid HorizontalAlignment="Left" Width="250" Height="250">\r
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">\r
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>\r
+            </Border>\r
+            <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">\r
+                <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>\r
+                <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>\r
+            </StackPanel>\r
+        </Grid>\r
+    </DataTemplate>\r
+\r
+    <!-- Grid-appropriate 500 by 130 pixel item template as seen in the GroupDetailPage -->\r
+    <DataTemplate x:Key="Standard500x130ItemTemplate">\r
+        <Grid Height="110" Width="480" Margin="10">\r
+            <Grid.ColumnDefinitions>\r
+                <ColumnDefinition Width="Auto"/>\r
+                <ColumnDefinition Width="*"/>\r
+            </Grid.ColumnDefinitions>\r
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">\r
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>\r
+            </Border>\r
+            <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">\r
+                <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>\r
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>\r
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>\r
+            </StackPanel>\r
+        </Grid>\r
+    </DataTemplate>\r
+\r
+    <!-- List-appropriate 130 pixel high item template as seen in the SplitPage -->\r
+    <DataTemplate x:Key="Standard130ItemTemplate">\r
+        <Grid Height="110" Margin="6">\r
+            <Grid.ColumnDefinitions>\r
+                <ColumnDefinition Width="Auto"/>\r
+                <ColumnDefinition Width="*"/>\r
+            </Grid.ColumnDefinitions>\r
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">\r
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>\r
+            </Border>\r
+            <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">\r
+                <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>\r
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>\r
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>\r
+            </StackPanel>\r
+        </Grid>\r
+    </DataTemplate>\r
+\r
+    <!--\r
+        List-appropriate 80 pixel high item template as seen in the SplitPage when Filled, and\r
+        the following pages when snapped: GroupedItemsPage, GroupDetailPage, and ItemsPage\r
+    -->\r
+    <DataTemplate x:Key="Standard80ItemTemplate">\r
+        <Grid Margin="6">\r
+            <Grid.ColumnDefinitions>\r
+                <ColumnDefinition Width="Auto"/>\r
+                <ColumnDefinition Width="*"/>\r
+            </Grid.ColumnDefinitions>\r
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="60" Height="60">\r
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>\r
+            </Border>\r
+            <StackPanel Grid.Column="1" Margin="10,0,0,0">\r
+                <TextBlock Text="{Binding Title}" Style="{StaticResource ItemTextStyle}" MaxHeight="40"/>\r
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>\r
+            </StackPanel>\r
+        </Grid>\r
+    </DataTemplate>\r
+\r
+    <!-- Grid-appropriate 300 by 70 pixel item template as seen in the SearchResultsPage -->\r
+    <DataTemplate x:Key="StandardSmallIcon300x70ItemTemplate">\r
+        <Grid Width="294" Margin="6">\r
+            <Grid.ColumnDefinitions>\r
+                <ColumnDefinition Width="Auto"/>\r
+                <ColumnDefinition Width="*"/>\r
+            </Grid.ColumnDefinitions>\r
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,0,0,10" Width="40" Height="40">\r
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>\r
+            </Border>\r
+            <StackPanel Grid.Column="1" Margin="10,-10,0,0">\r
+                <TextBlock Text="{Binding Title}" Style="{StaticResource BodyTextStyle}" TextWrapping="NoWrap"/>\r
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>\r
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>\r
+            </StackPanel>\r
+        </Grid>\r
+    </DataTemplate>\r
+\r
+    <!-- List-appropriate 70 pixel high item template as seen in the SearchResultsPage when Snapped -->\r
+    <DataTemplate x:Key="StandardSmallIcon70ItemTemplate">\r
+        <Grid Margin="6">\r
+            <Grid.ColumnDefinitions>\r
+                <ColumnDefinition Width="Auto"/>\r
+                <ColumnDefinition Width="*"/>\r
+            </Grid.ColumnDefinitions>\r
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,0,0,10" Width="40" Height="40">\r
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>\r
+            </Border>\r
+            <StackPanel Grid.Column="1" Margin="10,-10,0,0">\r
+                <TextBlock Text="{Binding Title}" Style="{StaticResource BodyTextStyle}" TextWrapping="NoWrap"/>\r
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>\r
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>\r
+            </StackPanel>\r
+        </Grid>\r
+    </DataTemplate>\r
+\r
+    <!--\r
+      190x130 pixel item template for displaying file previews as seen in the FileOpenPickerPage\r
+      Includes an elaborate tooltip to display title and description text\r
+  -->\r
+    <DataTemplate x:Key="StandardFileWithTooltip190x130ItemTemplate">\r
+        <Grid>\r
+            <Grid Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">\r
+                <Image\r
+                    Source="{Binding Image}"\r
+                    Width="190"\r
+                    Height="130"\r
+                    HorizontalAlignment="Center"\r
+                    VerticalAlignment="Center"\r
+                    Stretch="Uniform"/>\r
+            </Grid>\r
+            <ToolTipService.Placement>Mouse</ToolTipService.Placement>\r
+            <ToolTipService.ToolTip>\r
+                <ToolTip>\r
+                    <ToolTip.Style>\r
+                        <Style TargetType="ToolTip">\r
+                            <Setter Property="BorderBrush" Value="{StaticResource ToolTipBackgroundThemeBrush}" />\r
+                            <Setter Property="Padding" Value="0" />\r
+                        </Style>\r
+                    </ToolTip.Style>\r
+\r
+                    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">\r
+                        <Grid.ColumnDefinitions>\r
+                            <ColumnDefinition Width="Auto"/>\r
+                            <ColumnDefinition Width="*"/>\r
+                        </Grid.ColumnDefinitions>\r
+\r
+                        <Grid Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="20">\r
+                            <Image\r
+                                Source="{Binding Image}"\r
+                                Width="160"\r
+                                Height="160"\r
+                                HorizontalAlignment="Center"\r
+                                VerticalAlignment="Center"\r
+                                Stretch="Uniform"/>\r
+                        </Grid>\r
+                        <StackPanel Width="200" Grid.Column="1" Margin="0,20,20,20">\r
+                            <TextBlock Text="{Binding Title}" TextWrapping="NoWrap" Style="{StaticResource BodyTextStyle}"/>\r
+                            <TextBlock Text="{Binding Description}" MaxHeight="140" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" Style="{StaticResource BodyTextStyle}"/>\r
+                        </StackPanel>\r
+                    </Grid>\r
+                </ToolTip>\r
+            </ToolTipService.ToolTip>\r
+        </Grid>\r
+    </DataTemplate>\r
+\r
+    <!-- ScrollViewer styles -->\r
+\r
+    <Style x:Key="HorizontalScrollViewerStyle" TargetType="ScrollViewer">\r
+        <Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>\r
+        <Setter Property="VerticalScrollBarVisibility" Value="Disabled"/>\r
+        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Enabled" />\r
+        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" />\r
+        <Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />\r
+    </Style>\r
+\r
+    <Style x:Key="VerticalScrollViewerStyle" TargetType="ScrollViewer">\r
+        <Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>\r
+        <Setter Property="VerticalScrollBarVisibility" Value="Auto"/>\r
+        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />\r
+        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Enabled" />\r
+        <Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />\r
+    </Style>\r
+\r
+    <!-- Page layout roots typically use entrance animations and a theme-appropriate background color -->\r
+\r
+    <Style x:Key="LayoutRootStyle" TargetType="Panel">\r
+        <Setter Property="Background" Value="{StaticResource ApplicationPageBackgroundThemeBrush}"/>\r
+        <Setter Property="ChildrenTransitions">\r
+            <Setter.Value>\r
+                <TransitionCollection>\r
+                    <EntranceThemeTransition/>\r
+                </TransitionCollection>\r
+            </Setter.Value>\r
+        </Setter>\r
+    </Style>\r
+</ResourceDictionary>\r
diff --git a/Release/samples/OAuth2Live/MainPage.xaml b/Release/samples/OAuth2Live/MainPage.xaml
new file mode 100644 (file)
index 0000000..a94222c
--- /dev/null
@@ -0,0 +1,37 @@
+ļ»æ<Page\r
+    x:Class="OAuth2Live.MainPage"\r
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"\r
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"\r
+    xmlns:local="using:OAuth2Live"\r
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"\r
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"\r
+    mc:Ignorable="d">\r
+\r
+    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">\r
+        <Grid.RowDefinitions>\r
+            <RowDefinition Height="Auto"/>\r
+            <RowDefinition Height="*"/>\r
+        </Grid.RowDefinitions>\r
+        <Grid x:Name="Input" Grid.Row="0" Margin="8,8,8,8">\r
+            <StackPanel>\r
+                <StackPanel Orientation="Horizontal" Margin="0,4,0,4">\r
+                    <TextBlock TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" Text="OAuth 2.0 Live sample." VerticalAlignment="Center" Margin="4,0,4,0"/>\r
+                    <Button x:Name="GetTokenButton" Content="Get token (authorize)" Click="GetTokenButtonClick" Margin="4,0,4,0"/>\r
+                    <CheckBox x:Name="ImplicitGrantCheckBox" Content="Implicit grant" Unchecked="ImplicitGrantUnchecked" Checked="ImplicitGrantChecked"/>\r
+                    <Button x:Name="RefreshTokenButton" Content="Refresh token" Click="RefreshTokenButtonClick" Margin="4,0,4,0" IsEnabled="False"/>\r
+                </StackPanel>\r
+                <StackPanel Orientation="Horizontal" Margin="0,4,0,4">\r
+                    <TextBlock TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" Text="Actions:" VerticalAlignment="Center" Margin="4,0,4,0"/>\r
+                    <Button x:Name="GetInfoButton" Content="Get user info" Click="GetInfoButtonClick" Margin="4,0,4,0" IsEnabled="False"/>\r
+                    <Button x:Name="GetContactsButton" Content="Get contacts" Click="GetContactsButtonClick" Margin="4,0,4,0" IsEnabled="False"/>\r
+                    <Button x:Name="GetEventsButton" Content="Get events" Click="GetEventsButtonClick" Margin="4,0,4,0" IsEnabled="False"/>\r
+                </StackPanel>\r
+                <TextBlock TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" Text="Token:" Margin="4,4,4,4"/>\r
+                <TextBox x:Name="AccessToken" Height="128" VerticalAlignment="Top" Width="Auto" Text="" IsEnabled="False" TextWrapping="Wrap" Margin="4,4,4,4" TextChanged="AccessTokenTextChanged"/>\r
+                <TextBox x:Name="DebugArea" Height="400" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="Auto" Margin="4,4,4,4"\r
+                     ScrollViewer.VerticalScrollBarVisibility="Auto"\r
+                     AcceptsReturn="True"/>\r
+            </StackPanel>\r
+        </Grid>\r
+    </Grid>\r
+</Page>\r
diff --git a/Release/samples/OAuth2Live/MainPage.xaml.cpp b/Release/samples/OAuth2Live/MainPage.xaml.cpp
new file mode 100644 (file)
index 0000000..fef5d2d
--- /dev/null
@@ -0,0 +1,237 @@
+ļ»æ//\r
+// MainPage.xaml.cpp\r
+// Implementation of the MainPage class.\r
+//\r
+\r
+#include "pch.h"\r
+\r
+#include "MainPage.xaml.h"\r
+\r
+using namespace OAuth2Live;\r
+\r
+using namespace Concurrency;\r
+using namespace Platform;\r
+using namespace Windows::Foundation;\r
+using namespace Windows::Foundation::Collections;\r
+using namespace Windows::UI::Xaml;\r
+using namespace Windows::UI::Xaml::Controls;\r
+using namespace Windows::UI::Xaml::Controls::Primitives;\r
+using namespace Windows::UI::Xaml::Data;\r
+using namespace Windows::UI::Xaml::Input;\r
+using namespace Windows::UI::Xaml::Media;\r
+using namespace Windows::UI::Xaml::Navigation;\r
+using namespace Windows::Security::Authentication::Web;\r
+\r
+using namespace web::http;\r
+using namespace web::http::client;\r
+using namespace web::http::oauth2::experimental;\r
+\r
+//\r
+// NOTE: You must set this Live key and secret for app to work.\r
+//\r
+static const utility::string_t s_live_key;\r
+static const utility::string_t s_live_secret;\r
+\r
+MainPage::MainPage()\r
+    : m_live_oauth2_config(s_live_key,\r
+                           s_live_secret,\r
+                           L"https://login.live.com/oauth20_authorize.srf",\r
+                           L"https://login.live.com/oauth20_token.srf",\r
+                           L"https://login.live.com/oauth20_desktop.srf")\r
+{\r
+    InitializeComponent();\r
+}\r
+\r
+/// <summary>\r
+/// Invoked when this page is about to be displayed in a Frame.\r
+/// </summary>\r
+/// <param name="e">Event data that describes how this page was reached.  The Parameter\r
+/// property is typically used to configure the page.</param>\r
+void MainPage::OnNavigatedTo(NavigationEventArgs ^ e)\r
+{\r
+    (void)e; // Unused parameter\r
+}\r
+\r
+void OAuth2Live::MainPage::_UpdateButtonState()\r
+{\r
+    const bool has_access_token = !m_live_oauth2_config.token().access_token().empty();\r
+    GetInfoButton->IsEnabled = has_access_token;\r
+    GetContactsButton->IsEnabled = has_access_token;\r
+    GetEventsButton->IsEnabled = has_access_token;\r
+\r
+    const bool has_refresh_token = !m_live_oauth2_config.token().refresh_token().empty();\r
+    RefreshTokenButton->IsEnabled = has_refresh_token;\r
+}\r
+\r
+void OAuth2Live::MainPage::_GetToken()\r
+{\r
+    m_live_oauth2_config.set_scope(L"wl.basic wl.calendars");\r
+\r
+    // Start over, clear tokens and button state.\r
+    m_live_oauth2_config.set_token(oauth2_token());\r
+    AccessToken->Text.clear();\r
+    _UpdateButtonState();\r
+\r
+    String ^ authURI = ref new String(m_live_oauth2_config.build_authorization_uri(true).c_str());\r
+    auto startURI = ref new Uri(authURI);\r
+    String ^ redirectURI = ref new String(m_live_oauth2_config.redirect_uri().c_str());\r
+    auto endURI = ref new Uri(redirectURI);\r
+\r
+    try\r
+    {\r
+        DebugArea->Text += "> Navigating WebAuthenticationBroker to " + authURI + "\n";\r
+\r
+#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP\r
+        WebAuthenticationBroker::AuthenticateAndContinue(startURI, endURI, nullptr, WebAuthenticationOptions::None);\r
+#else\r
+        concurrency::create_task(\r
+            WebAuthenticationBroker::AuthenticateAsync(WebAuthenticationOptions::None, startURI, endURI))\r
+            .then([this](WebAuthenticationResult ^ result) {\r
+                String ^ statusString;\r
+\r
+                DebugArea->Text += "< WebAuthenticationBroker returned: ";\r
+                switch (result->ResponseStatus)\r
+                {\r
+                    case WebAuthenticationStatus::ErrorHttp:\r
+                    {\r
+                        DebugArea->Text += "ErrorHttp: " + result->ResponseErrorDetail + "\n";\r
+                        break;\r
+                    }\r
+                    case WebAuthenticationStatus::Success:\r
+                    {\r
+                        DebugArea->Text += "Success\n";\r
+                        utility::string_t data = result->ResponseData->Data();\r
+                        DebugArea->Text += "Redirected URI:\n" + result->ResponseData + "\n";\r
+                        DebugArea->Text += "> Obtaining token using the redirected URI\n";\r
+                        m_live_oauth2_config.token_from_redirected_uri(data).then(\r
+                            [this](pplx::task<void> token_task) {\r
+                                try\r
+                                {\r
+                                    token_task.wait();\r
+                                    DebugArea->Text += "< Got token\n";\r
+                                    AccessToken->Text =\r
+                                        ref new String(m_live_oauth2_config.token().access_token().c_str());\r
+                                }\r
+                                catch (const oauth2_exception& e)\r
+                                {\r
+                                    DebugArea->Text += "< Failed to get token\n";\r
+                                    String ^ error =\r
+                                        ref new String(utility::conversions::to_string_t(e.what()).c_str());\r
+                                    DebugArea->Text += "Error: " + error + "\n";\r
+                                }\r
+                            },\r
+                            pplx::task_continuation_context::use_current());\r
+                        break;\r
+                    }\r
+                    default:\r
+                    case WebAuthenticationStatus::UserCancel:\r
+                    {\r
+                        DebugArea->Text += "UserCancel\n";\r
+                        break;\r
+                    }\r
+                }\r
+            });\r
+#endif\r
+    }\r
+    catch (Exception ^ ex)\r
+    {\r
+        DebugArea->Text += "< Error launching WebAuthenticationBroker: " + ex->Message + "\n";\r
+    }\r
+}\r
+\r
+void OAuth2Live::MainPage::GetTokenButtonClick(Platform::Object ^ sender,\r
+                                               Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e)\r
+{\r
+    if (m_live_oauth2_config.client_key().empty() || m_live_oauth2_config.client_secret().empty())\r
+    {\r
+        DebugArea->Text +=\r
+            "Error: Cannot get token because Live app key or secret is empty. Please see instructions.\n";\r
+    }\r
+    else\r
+    {\r
+        _GetToken();\r
+    }\r
+}\r
+\r
+void OAuth2Live::MainPage::GetInfoButtonClick(Platform::Object ^ sender,\r
+                                              Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e)\r
+{\r
+    DebugArea->Text += "> Get user info\n";\r
+    m_live_client->request(methods::GET, U("me"))\r
+        .then([](http_response resp) { return resp.extract_json(); })\r
+        .then(\r
+            [this](web::json::value j) -> void {\r
+                String ^ json_code = ref new String(j.serialize().c_str());\r
+                DebugArea->Text += "< User info (JSON): " + json_code + "\n";\r
+            },\r
+            pplx::task_continuation_context::use_current());\r
+}\r
+\r
+void OAuth2Live::MainPage::GetContactsButtonClick(Platform::Object ^ sender,\r
+                                                  Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e)\r
+{\r
+    DebugArea->Text += "> Get user contacts\n";\r
+    m_live_client->request(methods::GET, U("me/contacts"))\r
+        .then([](http_response resp) { return resp.extract_json(); })\r
+        .then(\r
+            [this](web::json::value j) -> void {\r
+                String ^ json_code = ref new String(j.serialize().c_str());\r
+                DebugArea->Text += "< User contacts (JSON): " + json_code + "\n";\r
+            },\r
+            pplx::task_continuation_context::use_current());\r
+}\r
+\r
+void OAuth2Live::MainPage::GetEventsButtonClick(Platform::Object ^ sender,\r
+                                                Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e)\r
+{\r
+    DebugArea->Text += "> Get user events\n";\r
+    m_live_client->request(methods::GET, U("me/events"))\r
+        .then([](http_response resp) { return resp.extract_json(); })\r
+        .then(\r
+            [this](web::json::value j) -> void {\r
+                String ^ json_code = ref new String(j.serialize().c_str());\r
+                DebugArea->Text += "< User calendar events (JSON): " + json_code + "\n";\r
+            },\r
+            pplx::task_continuation_context::use_current());\r
+}\r
+\r
+void OAuth2Live::MainPage::AccessTokenTextChanged(Platform::Object ^ sender,\r
+                                                  Windows::UI::Xaml::Controls::TextChangedEventArgs ^ e)\r
+{\r
+    http_client_config http_config;\r
+    http_config.set_oauth2(m_live_oauth2_config);\r
+    m_live_client.reset(new http_client(U("https://apis.live.net/v5.0/"), http_config));\r
+    _UpdateButtonState();\r
+}\r
+\r
+void OAuth2Live::MainPage::ImplicitGrantUnchecked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)\r
+{\r
+    m_live_oauth2_config.set_implicit_grant(false);\r
+}\r
+\r
+void OAuth2Live::MainPage::ImplicitGrantChecked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)\r
+{\r
+    m_live_oauth2_config.set_implicit_grant(true);\r
+}\r
+\r
+void OAuth2Live::MainPage::RefreshTokenButtonClick(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)\r
+{\r
+    DebugArea->Text += "> Refreshing token\n";\r
+\r
+    m_live_oauth2_config.token_from_refresh().then(\r
+        [this](pplx::task<void> refresh_task) {\r
+            try\r
+            {\r
+                refresh_task.wait();\r
+                DebugArea->Text += "< Got token\n";\r
+                AccessToken->Text = ref new String(m_live_oauth2_config.token().access_token().c_str());\r
+            }\r
+            catch (const oauth2_exception& e)\r
+            {\r
+                DebugArea->Text += "< Failed to get token\n";\r
+                String ^ error = ref new String(utility::conversions::to_string_t(e.what()).c_str());\r
+                DebugArea->Text += "Error: " + error + "\n";\r
+            }\r
+        },\r
+        pplx::task_continuation_context::use_current());\r
+}\r
diff --git a/Release/samples/OAuth2Live/MainPage.xaml.h b/Release/samples/OAuth2Live/MainPage.xaml.h
new file mode 100644 (file)
index 0000000..1b4cbb4
--- /dev/null
@@ -0,0 +1,45 @@
+ļ»æ//\r
+// MainPage.xaml.h\r
+// Declaration of the MainPage class.\r
+//\r
+\r
+#pragma once\r
+\r
+#include "MainPage.g.h"\r
+\r
+using web::http::client::http_client;\r
+using web::http::oauth2::experimental::oauth2_config;\r
+\r
+namespace OAuth2Live\r
+{\r
+/// <summary>\r
+/// An empty page that can be used on its own or navigated to within a Frame.\r
+/// </summary>\r
+public\r
+ref class MainPage sealed\r
+{\r
+public:\r
+    MainPage();\r
+\r
+protected:\r
+    virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e) override;\r
+\r
+private:\r
+    oauth2_config m_live_oauth2_config;\r
+    std::unique_ptr<http_client> m_live_client;\r
+\r
+    void _UpdateButtonState();\r
+    void _GetToken();\r
+\r
+    void GetTokenButtonClick(Platform::Object ^ sender, Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e);\r
+    void RefreshTokenButtonClick(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);\r
+    void ImplicitGrantUnchecked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);\r
+    void ImplicitGrantChecked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);\r
+\r
+    void GetInfoButtonClick(Platform::Object ^ sender, Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e);\r
+    void GetContactsButtonClick(Platform::Object ^ sender, Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e);\r
+    void GetEventsButtonClick(Platform::Object ^ sender, Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e);\r
+\r
+    void AccessTokenTextChanged(Platform::Object ^ sender, Windows::UI::Xaml::Controls::TextChangedEventArgs ^ e);\r
+};\r
+} // namespace OAuth2Live\r
diff --git a/Release/samples/OAuth2Live/Package.appxmanifest b/Release/samples/OAuth2Live/Package.appxmanifest
new file mode 100644 (file)
index 0000000..fc11716
--- /dev/null
@@ -0,0 +1,42 @@
+ļ»æ<?xml version="1.0" encoding="utf-8"?>
+<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest">
+
+  <Identity Name="2887A786-B818-4B3D-94EF-21EFD6AFDC22"
+            Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
+            Version="1.0.0.0" />
+
+  <Properties>
+    <DisplayName>OAuth2Live</DisplayName>
+    <PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
+    <Logo>Assets\StoreLogo.png</Logo>
+  </Properties>
+
+  <Prerequisites>
+    <OSMinVersion>6.2.1</OSMinVersion>
+    <OSMaxVersionTested>6.2.1</OSMaxVersionTested>
+  </Prerequisites>
+
+  <Resources>
+    <Resource Language="x-generate"/>
+  </Resources>
+
+  <Applications>
+    <Application Id="App"
+        Executable="$targetnametoken$.exe"
+        EntryPoint="OAuth2Live.App">
+        <VisualElements
+            DisplayName="OAuth2Live"
+            Logo="Assets\Logo.png"
+            SmallLogo="Assets\SmallLogo.png"
+            Description="OAuth2Live"
+            ForegroundText="light"
+            BackgroundColor="#464646">
+            <DefaultTile ShowName="allLogos" />
+            <SplashScreen Image="Assets\SplashScreen.png" />
+        </VisualElements>
+    </Application>
+  </Applications>
+  <Capabilities>
+    <Capability Name="internetClient" />
+  </Capabilities>
+</Package>
\ No newline at end of file
diff --git a/Release/samples/OAuth2Live/Package.uwp.appxmanifest b/Release/samples/OAuth2Live/Package.uwp.appxmanifest
new file mode 100644 (file)
index 0000000..1c43d54
--- /dev/null
@@ -0,0 +1,34 @@
+ļ»æ<?xml version="1.0" encoding="utf-8"?>
+<Package
+  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
+  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
+  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
+  IgnorableNamespaces="uap mp">
+  <Identity Name="64521d51-da30-445f-ba7b-d79f27cf8c7c" Publisher="CN=casaserv" Version="1.0.0.0" />
+  <Properties>
+    <DisplayName>OAuth2Live</DisplayName>
+    <PublisherDisplayName>Andy</PublisherDisplayName>
+    <Logo>Assets\StoreLogo.png</Logo>
+  </Properties>
+
+  <mp:PhoneIdentity PhoneProductId="51cd24bd-4a31-4002-a587-b5ca8c380f24" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
+
+  <Dependencies>
+    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
+  </Dependencies>
+
+  <Resources>
+    <Resource Language="x-generate" />
+  </Resources>
+  <Applications>
+    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="OAuth2Live.App">
+      <uap:VisualElements DisplayName="OAuth2Live" Description="OAuth2Live" BackgroundColor="#464646" Square150x150Logo="Assets\Logo.png" Square44x44Logo="Assets\SmallLogo.png">
+        <uap:SplashScreen Image="Assets\SplashScreen.png" />
+      </uap:VisualElements>
+    </Application>
+  </Applications>
+
+  <Capabilities>
+    <Capability Name="internetClient" />
+  </Capabilities>
+</Package>
\ No newline at end of file
diff --git a/Release/samples/OAuth2Live/pch.cpp b/Release/samples/OAuth2Live/pch.cpp
new file mode 100644 (file)
index 0000000..f3963b9
--- /dev/null
@@ -0,0 +1,6 @@
+ļ»æ//\r
+// pch.cpp\r
+// Include the standard header and generate the precompiled header.\r
+//\r
+\r
+#include "pch.h"\r
diff --git a/Release/samples/OAuth2Live/pch.h b/Release/samples/OAuth2Live/pch.h
new file mode 100644 (file)
index 0000000..8aae530
--- /dev/null
@@ -0,0 +1,15 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ ****/
+
+#pragma once
+
+#include "App.xaml.h"
+#include "MainPage.xaml.h"
+#include <collection.h>
+#include <cpprest/http_client.h>
+#include <cpprest/oauth2.h>
+#include <cpprest/uri.h>
+#include <ppltasks.h>
diff --git a/Release/samples/Oauth1Client/CMakeLists.txt b/Release/samples/Oauth1Client/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e77d3fd
--- /dev/null
@@ -0,0 +1,7 @@
+if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE)
+  add_executable(oauth1client
+    Oauth1Client.cpp
+    )
+
+  target_link_libraries(oauth1client cpprest)
+endif()
diff --git a/Release/samples/Oauth1Client/Oauth1Client.cpp b/Release/samples/Oauth1Client/Oauth1Client.cpp
new file mode 100644 (file)
index 0000000..dec9b34
--- /dev/null
@@ -0,0 +1,298 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Oauth1Client.cpp : Defines the entry point for the console application
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+/*
+
+INSTRUCTIONS
+
+This sample performs authorization on various OAuth 1.0 services and
+then requests basic user information.
+
+This sample is for Windows Desktop, OS X and Linux.
+Execute with administrator privileges.
+
+Set the app key & secret strings below (i.e. s_linkedin_key, s_linkedin_secret, etc.)
+To get key & secret, register an app in the corresponding service.
+
+Set following entry in the hosts file:
+127.0.0.1    testhost.local
+
+*/
+#include "cpprest/http_client.h"
+#include <mutex>
+
+#if defined(_WIN32) && !defined(__cplusplus_winrt)
+// Extra includes for Windows desktop.
+#include <windows.h>
+
+#include <Shellapi.h>
+#endif
+
+#include "cpprest/http_client.h"
+#include "cpprest/http_listener.h"
+
+using namespace utility;
+using namespace web;
+using namespace web::http;
+using namespace web::http::client;
+using namespace web::http::oauth1::experimental;
+using namespace web::http::experimental::listener;
+
+//
+// Set key & secret pair to enable session for that service.
+//
+static const utility::string_t s_linkedin_key;
+static const utility::string_t s_linkedin_secret;
+
+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.
+//
+static void open_browser(utility::string_t auth_uri)
+{
+#if defined(_WIN32) && !defined(__cplusplus_winrt)
+    // NOTE: Windows desktop only.
+    auto r = ShellExecuteA(NULL, "open", conversions::utf16_to_utf8(auth_uri).c_str(), NULL, NULL, SW_SHOWNORMAL);
+#elif defined(__APPLE__)
+    // NOTE: OS X only.
+    string_t browser_cmd(U("open \"") + auth_uri + U("\""));
+    (void)system(browser_cmd.c_str());
+#else
+    // NOTE: Linux/X11 only.
+    string_t browser_cmd(U("xdg-open \"") + auth_uri + U("\""));
+    (void)system(browser_cmd.c_str());
+#endif
+}
+
+//
+// A simple listener class to capture OAuth 1.0 HTTP redirect to localhost.
+// The listener captures redirected URI and obtains the token.
+// This type of listener can be implemented in the back-end to capture and store tokens.
+//
+class oauth1_code_listener
+{
+public:
+    oauth1_code_listener(uri listen_uri, oauth1_config& config)
+        : 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(""))
+            {
+                m_resplock.lock();
+
+                m_config.token_from_redirected_uri(request.request_uri())
+                    .then([this, request](pplx::task<void> token_task) -> void {
+                        try
+                        {
+                            token_task.wait();
+                            m_tce.set(true);
+                        }
+                        catch (const oauth1_exception& e)
+                        {
+                            ucout << "Error: " << e.what() << std::endl;
+                            m_tce.set(false);
+                        }
+                    });
+
+                request.reply(status_codes::OK, U("Ok."));
+
+                m_resplock.unlock();
+            }
+            else
+            {
+                request.reply(status_codes::NotFound, U("Not found."));
+            }
+        });
+
+        m_listener->open().wait();
+    }
+
+    ~oauth1_code_listener() { m_listener->close().wait(); }
+
+    pplx::task<bool> listen_for_code() { return pplx::create_task(m_tce); }
+
+private:
+    std::unique_ptr<http_listener> m_listener;
+    pplx::task_completion_event<bool> m_tce;
+    oauth1_config& m_config;
+    std::mutex m_resplock;
+};
+
+//
+// Base class for OAuth 1.0 sessions of this sample.
+//
+class oauth1_session_sample
+{
+public:
+    oauth1_session_sample(utility::string_t name,
+                          utility::string_t consumer_key,
+                          utility::string_t consumer_secret,
+                          utility::string_t temp_endpoint,
+                          utility::string_t auth_endpoint,
+                          utility::string_t token_endpoint,
+                          utility::string_t callback_uri)
+        : m_oauth1_config(consumer_key,
+                          consumer_secret,
+                          temp_endpoint,
+                          auth_endpoint,
+                          token_endpoint,
+                          callback_uri,
+                          oauth1_methods::hmac_sha1)
+        , m_name(name)
+        , m_listener(new oauth1_code_listener(callback_uri, m_oauth1_config))
+    {
+    }
+
+    void run()
+    {
+        if (is_enabled())
+        {
+            ucout << "Running " << m_name.c_str() << " session..." << std::endl;
+
+            if (!m_oauth1_config.token().is_valid_access_token())
+            {
+                if (do_authorization().get())
+                {
+                    m_http_config.set_oauth1(m_oauth1_config);
+                }
+                else
+                {
+                    ucout << "Authorization failed for " << m_name.c_str() << "." << std::endl;
+                }
+            }
+
+            run_internal();
+        }
+        else
+        {
+            ucout << "Skipped " << m_name.c_str()
+                  << " session sample because app key or secret is empty. Please see instructions." << std::endl;
+        }
+    }
+
+protected:
+    virtual void run_internal() = 0;
+
+    pplx::task<bool> do_authorization()
+    {
+        if (open_browser_auth())
+        {
+            return m_listener->listen_for_code();
+        }
+        else
+        {
+            return pplx::create_task([]() { return false; });
+        }
+    }
+
+    http_client_config m_http_config;
+    oauth1_config m_oauth1_config;
+
+private:
+    bool is_enabled() const
+    {
+        return !m_oauth1_config.consumer_key().empty() && !m_oauth1_config.consumer_secret().empty();
+    }
+
+    bool open_browser_auth()
+    {
+        auto auth_uri_task(m_oauth1_config.build_authorization_uri());
+        try
+        {
+            auto auth_uri(auth_uri_task.get());
+            ucout << "Opening browser in URI:" << std::endl;
+            ucout << auth_uri << std::endl;
+            open_browser(auth_uri);
+            return true;
+        }
+        catch (const oauth1_exception& e)
+        {
+            ucout << "Error: " << e.what() << std::endl;
+            return false;
+        }
+    }
+
+    utility::string_t m_name;
+    std::unique_ptr<oauth1_code_listener> m_listener;
+};
+
+//
+// Specialized class for LinkedIn OAuth 1.0 session.
+//
+class linkedin_session_sample : public oauth1_session_sample
+{
+public:
+    linkedin_session_sample()
+        : oauth1_session_sample(U("LinkedIn"),
+                                s_linkedin_key,
+                                s_linkedin_secret,
+                                U("https://api.linkedin.com/uas/oauth/requestToken"),
+                                U("https://www.linkedin.com/uas/oauth/authenticate"),
+                                U("https://api.linkedin.com/uas/oauth/accessToken"),
+                                U("http://localhost:8888/"))
+    {
+    }
+
+protected:
+    void run_internal() override
+    {
+        http_client api(U("https://api.linkedin.com/v1/people/"), m_http_config);
+        ucout << "Requesting user information:" << std::endl;
+        ucout << "Information: " << api.request(methods::GET, U("~?format=json")).get().extract_json().get()
+              << std::endl;
+    }
+};
+
+//
+// Specialized class for Twitter OAuth 1.0 session.
+//
+class twitter_session_sample : public oauth1_session_sample
+{
+public:
+    twitter_session_sample()
+        : oauth1_session_sample(U("Twitter"),
+                                s_twitter_key,
+                                s_twitter_secret,
+                                U("https://api.twitter.com/oauth/request_token"),
+                                U("https://api.twitter.com/oauth/authorize"),
+                                U("https://api.twitter.com/oauth/access_token"),
+                                U("http://testhost.local:8890/"))
+    {
+    }
+
+protected:
+    void run_internal() override
+    {
+        http_client api(U("https://api.twitter.com/1.1/"), m_http_config);
+        ucout << "Requesting account information:" << std::endl;
+        ucout << api.request(methods::GET, U("account/settings.json")).get().extract_json().get() << std::endl;
+    }
+};
+
+#ifdef _WIN32
+int wmain(int argc, wchar_t* argv[])
+#else
+int main(int argc, char* argv[])
+#endif
+{
+    ucout << "Running OAuth 1.0 client sample..." << std::endl;
+
+    linkedin_session_sample linkedin;
+    twitter_session_sample twitter;
+
+    linkedin.run();
+    twitter.run();
+
+    ucout << "Done." << std::endl;
+    return 0;
+}
diff --git a/Release/samples/Oauth2Client/CMakeLists.txt b/Release/samples/Oauth2Client/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4da12bb
--- /dev/null
@@ -0,0 +1,7 @@
+if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE)
+  add_executable(oauth2client
+    Oauth2Client.cpp
+    )
+
+  target_link_libraries(oauth2client cpprest)
+endif()
diff --git a/Release/samples/Oauth2Client/Oauth2Client.cpp b/Release/samples/Oauth2Client/Oauth2Client.cpp
new file mode 100644 (file)
index 0000000..19072cf
--- /dev/null
@@ -0,0 +1,313 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Oauth2Client.cpp : Defines the entry point for the console application
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+/*
+
+INSTRUCTIONS
+
+This sample performs authorization code grant flow on various OAuth 2.0
+services and then requests basic user information.
+
+This sample is for Windows Desktop, OS X and Linux.
+Execute with administrator privileges.
+
+Set the app key & secret strings below (i.e. s_dropbox_key, s_dropbox_secret, etc.)
+To get key & secret, register an app in the corresponding service.
+
+Set following entry in the hosts file:
+127.0.0.1    testhost.local
+
+*/
+#include "cpprest/http_client.h"
+#include <mutex>
+
+#if defined(_WIN32) && !defined(__cplusplus_winrt)
+// Extra includes for Windows desktop.
+#include <windows.h>
+
+#include <Shellapi.h>
+#endif
+
+#include "cpprest/http_client.h"
+#include "cpprest/http_listener.h"
+
+using namespace utility;
+using namespace web;
+using namespace web::http;
+using namespace web::http::client;
+using namespace web::http::oauth2::experimental;
+using namespace web::http::experimental::listener;
+
+//
+// Set key & secret pair to enable session for that service.
+//
+static const utility::string_t s_dropbox_key;
+static const utility::string_t s_dropbox_secret;
+
+static const utility::string_t s_linkedin_key;
+static const utility::string_t s_linkedin_secret;
+
+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.
+//
+static void open_browser(utility::string_t auth_uri)
+{
+#if defined(_WIN32) && !defined(__cplusplus_winrt)
+    // NOTE: Windows desktop only.
+    auto r = ShellExecuteA(NULL, "open", conversions::utf16_to_utf8(auth_uri).c_str(), NULL, NULL, SW_SHOWNORMAL);
+#elif defined(__APPLE__)
+    // NOTE: OS X only.
+    string_t browser_cmd(U("open \"") + auth_uri + U("\""));
+    (void)system(browser_cmd.c_str());
+#else
+    // NOTE: Linux/X11 only.
+    string_t browser_cmd(U("xdg-open \"") + auth_uri + U("\""));
+    (void)system(browser_cmd.c_str());
+#endif
+}
+
+//
+// A simple listener class to capture OAuth 2.0 HTTP redirect to localhost.
+// The listener captures redirected URI and obtains the token.
+// This type of listener can be implemented in the back-end to capture and store tokens.
+//
+class oauth2_code_listener
+{
+public:
+    oauth2_code_listener(uri listen_uri, oauth2_config& config)
+        : 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().empty())
+            {
+                m_resplock.lock();
+
+                m_config.token_from_redirected_uri(request.request_uri())
+                    .then([this, request](pplx::task<void> token_task) -> void {
+                        try
+                        {
+                            token_task.wait();
+                            m_tce.set(true);
+                        }
+                        catch (const oauth2_exception& e)
+                        {
+                            ucout << "Error: " << e.what() << std::endl;
+                            m_tce.set(false);
+                        }
+                    });
+
+                request.reply(status_codes::OK, U("Ok."));
+
+                m_resplock.unlock();
+            }
+            else
+            {
+                request.reply(status_codes::NotFound, U("Not found."));
+            }
+        });
+
+        m_listener->open().wait();
+    }
+
+    ~oauth2_code_listener() { m_listener->close().wait(); }
+
+    pplx::task<bool> listen_for_code() { return pplx::create_task(m_tce); }
+
+private:
+    std::unique_ptr<http_listener> m_listener;
+    pplx::task_completion_event<bool> m_tce;
+    oauth2_config& m_config;
+    std::mutex m_resplock;
+};
+
+//
+// Base class for OAuth 2.0 sessions of this sample.
+//
+class oauth2_session_sample
+{
+public:
+    oauth2_session_sample(utility::string_t name,
+                          utility::string_t client_key,
+                          utility::string_t client_secret,
+                          utility::string_t auth_endpoint,
+                          utility::string_t token_endpoint,
+                          utility::string_t redirect_uri)
+        : m_oauth2_config(client_key, client_secret, auth_endpoint, token_endpoint, redirect_uri)
+        , m_name(name)
+        , m_listener(new oauth2_code_listener(redirect_uri, m_oauth2_config))
+    {
+    }
+
+    void run()
+    {
+        if (is_enabled())
+        {
+            ucout << "Running " << m_name.c_str() << " session..." << std::endl;
+
+            if (!m_oauth2_config.token().is_valid_access_token())
+            {
+                if (authorization_code_flow().get())
+                {
+                    m_http_config.set_oauth2(m_oauth2_config);
+                }
+                else
+                {
+                    ucout << "Authorization failed for " << m_name.c_str() << "." << std::endl;
+                }
+            }
+
+            run_internal();
+        }
+        else
+        {
+            ucout << "Skipped " << m_name.c_str()
+                  << " session sample because app key or secret is empty. Please see instructions." << std::endl;
+        }
+    }
+
+protected:
+    virtual void run_internal() = 0;
+
+    pplx::task<bool> authorization_code_flow()
+    {
+        open_browser_auth();
+        return m_listener->listen_for_code();
+    }
+
+    http_client_config m_http_config;
+    oauth2_config m_oauth2_config;
+
+private:
+    bool is_enabled() const
+    {
+        return !m_oauth2_config.client_key().empty() && !m_oauth2_config.client_secret().empty();
+    }
+
+    void open_browser_auth()
+    {
+        auto auth_uri(m_oauth2_config.build_authorization_uri(true));
+        ucout << "Opening browser in URI:" << std::endl;
+        ucout << auth_uri << std::endl;
+        open_browser(auth_uri);
+    }
+
+    utility::string_t m_name;
+    std::unique_ptr<oauth2_code_listener> m_listener;
+};
+
+//
+// Specialized class for Dropbox OAuth 2.0 session.
+//
+class dropbox_session_sample : public oauth2_session_sample
+{
+public:
+    dropbox_session_sample()
+        : oauth2_session_sample(U("Dropbox"),
+                                s_dropbox_key,
+                                s_dropbox_secret,
+                                U("https://www.dropbox.com/1/oauth2/authorize"),
+                                U("https://api.dropbox.com/1/oauth2/token"),
+                                U("http://localhost:8889/"))
+    {
+        // Dropbox uses "default" OAuth 2.0 settings.
+    }
+
+protected:
+    void run_internal() override
+    {
+        http_client api(U("https://api.dropbox.com/1/"), m_http_config);
+        ucout << "Requesting account information:" << std::endl;
+        ucout << "Information: " << api.request(methods::GET, U("account/info")).get().extract_json().get()
+              << std::endl;
+    }
+};
+
+//
+// Specialized class for LinkedIn OAuth 2.0 session.
+//
+class linkedin_session_sample : public oauth2_session_sample
+{
+public:
+    linkedin_session_sample()
+        : oauth2_session_sample(U("LinkedIn"),
+                                s_linkedin_key,
+                                s_linkedin_secret,
+                                U("https://www.linkedin.com/uas/oauth2/authorization"),
+                                U("https://www.linkedin.com/uas/oauth2/accessToken"),
+                                U("http://localhost:8888/"))
+    {
+        // LinkedIn doesn't use bearer auth.
+        m_oauth2_config.set_bearer_auth(false);
+        // Also doesn't use HTTP Basic for token endpoint authentication.
+        m_oauth2_config.set_http_basic_auth(false);
+        // Also doesn't use the common "access_token", but "oauth2_access_token".
+        m_oauth2_config.set_access_token_key(U("oauth2_access_token"));
+    }
+
+protected:
+    void run_internal() override
+    {
+        http_client api(U("https://api.linkedin.com/v1/people/"), m_http_config);
+        ucout << "Requesting account information:" << std::endl;
+        ucout << "Information: " << api.request(methods::GET, U("~?format=json")).get().extract_json().get()
+              << std::endl;
+    }
+};
+
+//
+// Specialized class for Microsoft Live Connect OAuth 2.0 session.
+//
+class live_session_sample : public oauth2_session_sample
+{
+public:
+    live_session_sample()
+        : oauth2_session_sample(U("Live"),
+                                s_live_key,
+                                s_live_secret,
+                                U("https://login.live.com/oauth20_authorize.srf"),
+                                U("https://login.live.com/oauth20_token.srf"),
+                                U("http://testhost.local:8890/"))
+    {
+        // Scope "wl.basic" allows fetching user information.
+        m_oauth2_config.set_scope(U("wl.basic"));
+    }
+
+protected:
+    void run_internal() override
+    {
+        http_client api(U("https://apis.live.net/v5.0/"), m_http_config);
+        ucout << "Requesting account information:" << std::endl;
+        ucout << api.request(methods::GET, U("me")).get().extract_json().get() << std::endl;
+    }
+};
+
+#ifdef _WIN32
+int wmain(int argc, wchar_t* argv[])
+#else
+int main(int argc, char* argv[])
+#endif
+{
+    ucout << "Running OAuth 2.0 client sample..." << std::endl;
+
+    linkedin_session_sample linkedin;
+    dropbox_session_sample dropbox;
+    live_session_sample live;
+
+    linkedin.run();
+    dropbox.run();
+    live.run();
+
+    ucout << "Done." << std::endl;
+    return 0;
+}
diff --git a/Release/samples/SearchFile/CMakeLists.txt b/Release/samples/SearchFile/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7dd46a4
--- /dev/null
@@ -0,0 +1,4 @@
+if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE)
+  add_executable(SearchFile searchfile.cpp)
+  target_link_libraries(SearchFile cpprest)
+endif()
diff --git a/Release/samples/SearchFile/searchfile.cpp b/Release/samples/SearchFile/searchfile.cpp
new file mode 100644 (file)
index 0000000..dfcc38b
--- /dev/null
@@ -0,0 +1,187 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * searchfile.cpp - Simple cmd line application that uses a variety of streams features to search a file,
+ *      store the results, and write results back to a new file.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "cpprest/containerstream.h"
+#include "cpprest/filestream.h"
+#include "cpprest/producerconsumerstream.h"
+
+using namespace utility;
+using namespace concurrency::streams;
+
+/// <summary>
+/// A convenient helper function to loop asychronously until a condition is met.
+/// </summary>
+pplx::task<bool> _do_while_iteration(std::function<pplx::task<bool>(void)> func)
+{
+    pplx::task_completion_event<bool> ev;
+    func().then([=](bool guard) { ev.set(guard); });
+    return pplx::create_task(ev);
+}
+pplx::task<bool> _do_while_impl(std::function<pplx::task<bool>(void)> func)
+{
+    return _do_while_iteration(func).then([=](bool guard) -> pplx::task<bool> {
+        if (guard)
+        {
+            return ::_do_while_impl(func);
+        }
+        else
+        {
+            return pplx::task_from_result(false);
+        }
+    });
+}
+pplx::task<void> do_while(std::function<pplx::task<bool>(void)> func)
+{
+    return _do_while_impl(func).then([](bool) {});
+}
+
+/// <summary>
+/// Structure used to store individual line results.
+/// </summary>
+typedef std::vector<std::string> matched_lines;
+namespace Concurrency
+{
+namespace streams
+{
+/// <summary>
+/// Parser implementation for 'matched_lines' type.
+/// </summary>
+template<typename CharType>
+class type_parser<CharType, matched_lines>
+{
+public:
+    static pplx::task<matched_lines> parse(streambuf<CharType> buffer)
+    {
+        basic_istream<CharType> in(buffer);
+        auto lines = std::make_shared<matched_lines>();
+        return do_while([=]() {
+                   container_buffer<std::string> line;
+                   return in.read_line(line).then([=](const size_t bytesRead) {
+                       if (bytesRead == 0 && in.is_eof())
+                       {
+                           return false;
+                       }
+                       else
+                       {
+                           lines->push_back(std::move(line.collection()));
+                           return true;
+                       }
+                   });
+               })
+            .then([=]() { return matched_lines(std::move(*lines)); });
+    }
+};
+} // namespace streams
+} // namespace Concurrency
+/// <summary>
+/// Function to create in data from a file and search for a given string writing all lines containing the string to
+/// memory_buffer.
+/// </summary>
+static pplx::task<void> find_matches_in_file(const string_t& fileName,
+                                             const std::string& searchString,
+                                             basic_ostream<char> results)
+{
+    return file_stream<char>::open_istream(fileName).then([=](basic_istream<char> inFile) {
+        auto lineNumber = std::make_shared<int>(1);
+        return ::do_while([=]() {
+                   container_buffer<std::string> inLine;
+                   return inFile.read_line(inLine).then([=](size_t bytesRead) {
+                       if (bytesRead == 0 && inFile.is_eof())
+                       {
+                           return pplx::task_from_result(false);
+                       }
+
+                       else if (inLine.collection().find(searchString) != std::string::npos)
+                       {
+                           results.print("line ");
+                           results.print((*lineNumber)++);
+                           return results.print(":")
+                               .then([=](size_t) {
+                                   container_buffer<std::string> outLine(std::move(inLine.collection()));
+                                   return results.write(outLine, outLine.collection().size());
+                               })
+                               .then([=](size_t) { return results.print("\r\n"); })
+                               .then([=](size_t) { return true; });
+                       }
+
+                       else
+                       {
+                           ++(*lineNumber);
+                           return pplx::task_from_result(true);
+                       }
+                   });
+               })
+            .then([=]() {
+                // Close the file and results stream.
+                return inFile.close() && results.close();
+            });
+    });
+}
+
+/// <summary>
+/// Function to write out results from matched_lines type to file
+/// </summary>
+static pplx::task<void> write_matches_to_file(const string_t& fileName, matched_lines results)
+{
+    // Create a shared pointer to the matched_lines structure to copying repeatedly.
+    auto sharedResults = std::make_shared<matched_lines>(std::move(results));
+
+    return file_stream<char>::open_ostream(fileName, std::ios::trunc).then([=](basic_ostream<char> outFile) {
+        auto currentIndex = std::make_shared<size_t>(0);
+        return ::do_while([=]() {
+                   if (*currentIndex >= sharedResults->size())
+                   {
+                       return pplx::task_from_result(false);
+                   }
+
+                   container_buffer<std::string> lineData((*sharedResults)[(*currentIndex)++]);
+                   outFile.write(lineData, lineData.collection().size());
+                   return outFile.print("\r\n").then([](size_t) { return true; });
+               })
+            .then([=]() { return outFile.close(); });
+    });
+}
+
+#ifdef _WIN32
+int wmain(int argc, wchar_t* args[])
+#else
+int main(int argc, char* args[])
+#endif
+{
+    if (argc != 4)
+    {
+        printf("Usage: SearchFile.exe input_file search_string output_file\n");
+        return -1;
+    }
+    const string_t inFileName = args[1];
+    const std::string searchString = utility::conversions::to_utf8string(args[2]);
+    const string_t outFileName = args[3];
+    producer_consumer_buffer<char> lineResultsBuffer;
+
+    // Find all matches in file.
+    basic_ostream<char> outLineResults(lineResultsBuffer);
+    find_matches_in_file(inFileName, searchString, outLineResults)
+
+        // Write matches into custom data structure.
+        .then([&]() {
+            basic_istream<char> inLineResults(lineResultsBuffer);
+            return inLineResults.extract<matched_lines>();
+        })
+
+        // Write out stored match data to a new file.
+        .then([&](matched_lines lines) { return write_matches_to_file(outFileName, std::move(lines)); })
+
+        // Wait for everything to complete.
+        .wait();
+
+    return 0;
+}
diff --git a/Release/samples/WindowsLiveAuth/App.xaml b/Release/samples/WindowsLiveAuth/App.xaml
new file mode 100644 (file)
index 0000000..dbee957
--- /dev/null
@@ -0,0 +1,20 @@
+ļ»æ<Application
+    x:Class="WindowsLiveAuth.App"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:WindowsLiveAuth">
+
+    <Application.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+
+                <!-- 
+                    Styles that define common aspects of the platform look and feel
+                    Required by Visual Studio project and item templates
+                 -->
+                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
+            </ResourceDictionary.MergedDictionaries>
+
+        </ResourceDictionary>
+    </Application.Resources>
+</Application>
diff --git a/Release/samples/WindowsLiveAuth/App.xaml.cpp b/Release/samples/WindowsLiveAuth/App.xaml.cpp
new file mode 100644 (file)
index 0000000..8d41c74
--- /dev/null
@@ -0,0 +1,107 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ ****/
+
+#include "pch.h"
+
+#include "MainPage.xaml.h"
+
+using namespace WindowsLiveAuth;
+
+using namespace Platform;
+using namespace Windows::ApplicationModel;
+using namespace Windows::ApplicationModel::Activation;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Interop;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227
+
+/// <summary>
+/// Initializes the singleton application object.  This is the first line of authored code
+/// executed, and as such is the logical equivalent of main() or WinMain().
+/// </summary>
+App::App()
+{
+    InitializeComponent();
+    Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);
+}
+
+/// <summary>
+/// Invoked when the application is launched normally by the end user.  Other entry points
+/// will be used when the application is launched to open a specific file, to display
+/// search results, and so forth.
+/// </summary>
+/// <param name="args">Details about the launch request and process.</param>
+void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^ args)
+{
+    auto rootFrame = dynamic_cast<Frame ^>(Window::Current->Content);
+
+    // Do not repeat app initialization when the Window already has content,
+    // just ensure that the window is active
+    if (rootFrame == nullptr)
+    {
+        // Create a Frame to act as the navigation context and associate it with
+        // a SuspensionManager key
+        rootFrame = ref new Frame();
+
+        if (args->PreviousExecutionState == ApplicationExecutionState::Terminated)
+        {
+            // TODO: Restore the saved session state only when appropriate, scheduling the
+            // final launch steps after the restore is complete
+        }
+
+        if (rootFrame->Content == nullptr)
+        {
+            // When the navigation stack isn't restored navigate to the first page,
+            // configuring the new page by passing required information as a navigation
+            // parameter
+            if (!rootFrame->Navigate(TypeName(MainPage::typeid), args->Arguments))
+            {
+                throw ref new FailureException("Failed to create initial page");
+            }
+        }
+        // Place the frame in the current Window
+        Window::Current->Content = rootFrame;
+        // Ensure the current window is active
+        Window::Current->Activate();
+    }
+    else
+    {
+        if (rootFrame->Content == nullptr)
+        {
+            // When the navigation stack isn't restored navigate to the first page,
+            // configuring the new page by passing required information as a navigation
+            // parameter
+            if (!rootFrame->Navigate(TypeName(MainPage::typeid), args->Arguments))
+            {
+                throw ref new FailureException("Failed to create initial page");
+            }
+        }
+        // Ensure the current window is active
+        Window::Current->Activate();
+    }
+}
+
+/// <summary>
+/// Invoked when application execution is being suspended.  Application state is saved
+/// without knowing whether the application will be terminated or resumed with the contents
+/// of memory still intact.
+/// </summary>
+/// <param name="sender">The source of the suspend request.</param>
+/// <param name="e">Details about the suspend request.</param>
+void App::OnSuspending(Object ^ sender, SuspendingEventArgs ^ e)
+{
+    (void)sender; // Unused parameter
+    (void)e;      // Unused parameter
+
+    // TODO: Save application state and stop any background activity
+}
diff --git a/Release/samples/WindowsLiveAuth/App.xaml.h b/Release/samples/WindowsLiveAuth/App.xaml.h
new file mode 100644 (file)
index 0000000..dd871c9
--- /dev/null
@@ -0,0 +1,24 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ ****/
+
+#pragma once
+
+#include "App.g.h"
+
+namespace WindowsLiveAuth
+{
+/// <summary>
+/// Provides application-specific behavior to supplement the default Application class.
+/// </summary>
+ref class App sealed
+{
+public:
+    App();
+    virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^ args) override;
+
+private:
+    void OnSuspending(Platform::Object ^ sender, Windows::ApplicationModel::SuspendingEventArgs ^ e);
+};
+} // namespace WindowsLiveAuth
diff --git a/Release/samples/WindowsLiveAuth/Assets/Logo.png b/Release/samples/WindowsLiveAuth/Assets/Logo.png
new file mode 100644 (file)
index 0000000..e26771c
Binary files /dev/null and b/Release/samples/WindowsLiveAuth/Assets/Logo.png differ
diff --git a/Release/samples/WindowsLiveAuth/Assets/SmallLogo.png b/Release/samples/WindowsLiveAuth/Assets/SmallLogo.png
new file mode 100644 (file)
index 0000000..1eb0d9d
Binary files /dev/null and b/Release/samples/WindowsLiveAuth/Assets/SmallLogo.png differ
diff --git a/Release/samples/WindowsLiveAuth/Assets/SplashScreen.png b/Release/samples/WindowsLiveAuth/Assets/SplashScreen.png
new file mode 100644 (file)
index 0000000..c951e03
Binary files /dev/null and b/Release/samples/WindowsLiveAuth/Assets/SplashScreen.png differ
diff --git a/Release/samples/WindowsLiveAuth/Assets/StoreLogo.png b/Release/samples/WindowsLiveAuth/Assets/StoreLogo.png
new file mode 100644 (file)
index 0000000..dcb6727
Binary files /dev/null and b/Release/samples/WindowsLiveAuth/Assets/StoreLogo.png differ
diff --git a/Release/samples/WindowsLiveAuth/Common/StandardStyles.xaml b/Release/samples/WindowsLiveAuth/Common/StandardStyles.xaml
new file mode 100644 (file)
index 0000000..85f4ed6
--- /dev/null
@@ -0,0 +1,1829 @@
+ļ»æ<!--
+    This file contains XAML styles that simplify application development.
+
+    These are not merely convenient, but are required by most Visual Studio project and item templates.
+    Removing, renaming, or otherwise modifying the content of these files may result in a project that
+    does not build, or that will not build once additional pages are added.  If variations on these
+    styles are desired it is recommended that you copy the content under a new name and modify your
+    private copy.
+-->
+
+<ResourceDictionary
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+
+    <!-- Non-brush values that vary across themes -->
+
+    <ResourceDictionary.ThemeDictionaries>
+        <ResourceDictionary x:Key="Default">
+            <x:String x:Key="BackButtonGlyph">&#xE071;</x:String>
+            <x:String x:Key="BackButtonSnappedGlyph">&#xE0BA;</x:String>
+        </ResourceDictionary>
+
+        <ResourceDictionary x:Key="HighContrast">
+            <x:String x:Key="BackButtonGlyph">&#xE071;</x:String>
+            <x:String x:Key="BackButtonSnappedGlyph">&#xE0C4;</x:String>
+        </ResourceDictionary>
+    </ResourceDictionary.ThemeDictionaries>
+
+    <x:String x:Key="ChevronGlyph">&#xE26B;</x:String>
+
+    <!-- RichTextBlock styles -->
+
+    <Style x:Key="BasicRichTextStyle" TargetType="RichTextBlock">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationForegroundThemeBrush}"/>
+        <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>
+        <Setter Property="TextWrapping" Value="Wrap"/>
+        <Setter Property="Typography.StylisticSet20" Value="True"/>
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>
+    </Style>
+
+    <Style x:Key="BaselineRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BasicRichTextStyle}">
+        <Setter Property="LineHeight" Value="20"/>
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
+        <!-- Properly align text along its baseline -->
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="4"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="ItemRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaselineRichTextStyle}"/>
+
+    <Style x:Key="BodyRichTextStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaselineRichTextStyle}">
+        <Setter Property="FontWeight" Value="SemiLight"/>
+    </Style>
+
+    <!-- TextBlock styles -->
+
+    <Style x:Key="BasicTextStyle" TargetType="TextBlock">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationForegroundThemeBrush}"/>
+        <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>
+        <Setter Property="TextWrapping" Value="Wrap"/>
+        <Setter Property="Typography.StylisticSet20" Value="True"/>
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>
+    </Style>
+
+    <Style x:Key="BaselineTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BasicTextStyle}">
+        <Setter Property="LineHeight" Value="20"/>
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
+        <!-- Properly align text along its baseline -->
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="4"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="HeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontSize" Value="56"/>
+        <Setter Property="FontWeight" Value="Light"/>
+        <Setter Property="LineHeight" Value="40"/>
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-2" Y="8"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="SubheaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontSize" Value="26.667"/>
+        <Setter Property="FontWeight" Value="Light"/>
+        <Setter Property="LineHeight" Value="30"/>
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="6"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="TitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontWeight" Value="SemiBold"/>
+    </Style>
+
+    <Style x:Key="SubtitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontWeight" Value="Normal"/>
+    </Style>
+
+    <Style x:Key="ItemTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}"/>
+
+    <Style x:Key="BodyTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontWeight" Value="SemiLight"/>
+    </Style>
+
+    <Style x:Key="CaptionTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaselineTextStyle}">
+        <Setter Property="FontSize" Value="12"/>
+        <Setter Property="Foreground" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>
+    </Style>
+
+    <Style x:Key="GroupHeaderTextStyle" TargetType="TextBlock">
+        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
+        <Setter Property="TextTrimming" Value="WordEllipsis"/>
+        <Setter Property="TextWrapping" Value="NoWrap"/>
+        <Setter Property="Typography.StylisticSet20" Value="True"/>
+        <Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
+        <Setter Property="Typography.CaseSensitiveForms" Value="True"/>
+        <Setter Property="FontSize" Value="26.667"/>
+        <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
+        <Setter Property="FontWeight" Value="Light"/>
+        <Setter Property="LineHeight" Value="30"/>
+        <Setter Property="RenderTransform">
+            <Setter.Value>
+                <TranslateTransform X="-1" Y="6"/>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!-- Button styles -->
+    
+    <!--
+        TextButtonStyle is used to style a Button using subheader-styled text with no other adornment.  There
+        are two styles that are based on TextButtonStyle (TextPrimaryButtonStyle and TextSecondaryButtonStyle)
+        which are used in the GroupedItemsPage as a group header and in the FileOpenPickerPage for triggering
+        commands.
+    -->
+    <Style x:Key="TextButtonStyle" TargetType="ButtonBase">
+        <Setter Property="MinWidth" Value="0"/>
+        <Setter Property="MinHeight" Value="0"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="ButtonBase">
+                    <Grid Background="Transparent">
+                        <ContentPresenter x:Name="Text" Content="{TemplateBinding Content}" />
+                        <Rectangle
+                            x:Name="FocusVisualWhite"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="1.5"/>
+                        <Rectangle
+                            x:Name="FocusVisualBlack"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="0.5"/>
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal"/>
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetName="FocusVisualWhite" Storyboard.TargetProperty="Opacity"/>
+                                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetName="FocusVisualBlack" Storyboard.TargetProperty="Opacity"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused"/>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="CheckStates">
+                                <VisualState x:Name="Checked"/>
+                                <VisualState x:Name="Unchecked">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Indeterminate"/>
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="TextPrimaryButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource TextButtonStyle}">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationHeaderForegroundThemeBrush}"/>
+    </Style>
+
+    <Style x:Key="TextSecondaryButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource TextButtonStyle}">
+        <Setter Property="Foreground" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}"/>
+    </Style>
+
+    <!--
+        TextRadioButtonStyle is used to style a RadioButton using subheader-styled text with no other adornment.
+        This style is used in the SearchResultsPage to allow selection among filters.
+    -->
+    <Style x:Key="TextRadioButtonStyle" TargetType="RadioButton" BasedOn="{StaticResource TextButtonStyle}">
+        <Setter Property="Margin" Value="0,0,30,0"/>
+    </Style>
+
+    <!--
+        AppBarButtonStyle is used to style a Button (or ToggleButton) for use in an App Bar.  Content will be centered 
+        and should fit within the 40 pixel radius glyph provided.  16-point Segoe UI Symbol is used for content text 
+        to simplify the use of glyphs from that font.  AutomationProperties.Name is used for the text below the glyph.
+    -->
+    <Style x:Key="AppBarButtonStyle" TargetType="ButtonBase">
+        <Setter Property="Foreground" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+        <Setter Property="VerticalAlignment" Value="Stretch"/>
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
+        <Setter Property="FontWeight" Value="Normal"/>
+        <Setter Property="FontSize" Value="20"/>
+        <Setter Property="AutomationProperties.ItemType" Value="App Bar Button"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="ButtonBase">
+                    <Grid x:Name="RootGrid" Width="100" Background="Transparent">
+                        <StackPanel VerticalAlignment="Top" Margin="0,12,0,11">
+                            <Grid Width="40" Height="40" Margin="0,0,0,5" HorizontalAlignment="Center">
+                                <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0" Foreground="{StaticResource AppBarItemBackgroundThemeBrush}"/>
+                                <TextBlock x:Name="OutlineGlyph" Text="&#xE0A7;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0"/>
+                                <ContentPresenter x:Name="Content" HorizontalAlignment="Center" Margin="-1,-1,0,0" VerticalAlignment="Center"/>
+                            </Grid>
+                            <TextBlock
+                                x:Name="TextLabel"
+                                Text="{TemplateBinding AutomationProperties.Name}"
+                                Foreground="{StaticResource AppBarItemForegroundThemeBrush}"
+                                Margin="0,0,2,0"
+                                FontSize="12"
+                                TextAlignment="Center"
+                                Width="88"
+                                MaxHeight="32"
+                                TextTrimming="WordEllipsis"
+                                Style="{StaticResource BasicTextStyle}"/>
+                        </StackPanel>
+                        <Rectangle
+                                x:Name="FocusVisualWhite"
+                                IsHitTestVisible="False"
+                                Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                                StrokeEndLineCap="Square"
+                                StrokeDashArray="1,1"
+                                Opacity="0"
+                                StrokeDashOffset="1.5"/>
+                        <Rectangle
+                                x:Name="FocusVisualBlack"
+                                IsHitTestVisible="False"
+                                Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                                StrokeEndLineCap="Square"
+                                StrokeDashArray="1,1"
+                                Opacity="0"
+                                StrokeDashOffset="0.5"/>
+
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="ApplicationViewStates">
+                                <VisualState x:Name="FullScreenLandscape"/>
+                                <VisualState x:Name="Filled"/>
+                                <VisualState x:Name="FullScreenPortrait">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Width">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="60"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Snapped">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Width">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="60"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal"/>
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPointerOverBackgroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation
+                                                Storyboard.TargetName="FocusVisualWhite"
+                                                Storyboard.TargetProperty="Opacity"
+                                                To="1"
+                                                Duration="0"/>
+                                        <DoubleAnimation
+                                                Storyboard.TargetName="FocusVisualBlack"
+                                                Storyboard.TargetProperty="Opacity"
+                                                To="1"
+                                                Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused" />
+                                <VisualState x:Name="PointerFocused" />
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="CheckStates">
+                                <VisualState x:Name="Checked">
+                                    <Storyboard>
+                                        <DoubleAnimation Duration="0" To="0" Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Opacity"/>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundCheckedGlyph" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unchecked"/>
+                                <VisualState x:Name="Indeterminate"/>
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!-- 
+        Standard AppBarButton Styles for use with Button and ToggleButton
+    
+        An AppBarButton Style is provided for each of the glyphs in the Segoe UI Symbol font.  
+        Uncomment any style you reference (as not all may be required).
+    -->
+
+    <!--
+    
+    <Style x:Key="SkipBackAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SkipBackAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Skip Back"/>
+        <Setter Property="Content" Value="&#xE100;"/>
+    </Style>
+    <Style x:Key="SkipAheadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SkipAheadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Skip Ahead"/>
+        <Setter Property="Content" Value="&#xE101;"/>
+    </Style>
+    <Style x:Key="PlayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PlayAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Play"/>
+        <Setter Property="Content" Value="&#xE102;"/>
+    </Style>
+    <Style x:Key="PauseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PauseAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Pause"/>
+        <Setter Property="Content" Value="&#xE103;"/>
+    </Style>
+    <Style x:Key="EditAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="EditAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Edit"/>
+        <Setter Property="Content" Value="&#xE104;"/>
+    </Style>
+    <Style x:Key="SaveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SaveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Save"/>
+        <Setter Property="Content" Value="&#xE105;"/>
+    </Style>
+    <Style x:Key="DeleteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DeleteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Delete"/>
+        <Setter Property="Content" Value="&#xE106;"/>
+    </Style>
+    <Style x:Key="DiscardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DiscardAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Discard"/>
+        <Setter Property="Content" Value="&#xE107;"/>
+    </Style>
+    <Style x:Key="RemoveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RemoveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Remove"/>
+        <Setter Property="Content" Value="&#xE108;"/>
+    </Style>
+    <Style x:Key="AddAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AddAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Add"/>
+        <Setter Property="Content" Value="&#xE109;"/>
+    </Style>
+    <Style x:Key="NoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="No"/>
+        <Setter Property="Content" Value="&#xE10A;"/>
+    </Style>
+    <Style x:Key="YesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="YesAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Yes"/>
+        <Setter Property="Content" Value="&#xE10B;"/>
+    </Style>
+    <Style x:Key="MoreAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MoreAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="More"/>
+        <Setter Property="Content" Value="&#xE10C;"/>
+    </Style>
+    <Style x:Key="RedoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RedoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Redo"/>
+        <Setter Property="Content" Value="&#xE10D;"/>
+    </Style>
+    <Style x:Key="UndoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UndoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Undo"/>
+        <Setter Property="Content" Value="&#xE10E;"/>
+    </Style>
+    <Style x:Key="HomeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HomeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Home"/>
+        <Setter Property="Content" Value="&#xE10F;"/>
+    </Style>
+    <Style x:Key="OutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OutAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Out"/>
+        <Setter Property="Content" Value="&#xE110;"/>
+    </Style>
+    <Style x:Key="NextAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NextAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Next"/>
+        <Setter Property="Content" Value="&#xE111;"/>
+    </Style>
+    <Style x:Key="PreviousAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PreviousAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Previous"/>
+        <Setter Property="Content" Value="&#xE112;"/>
+    </Style>
+    <Style x:Key="FavoriteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FavoriteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Favorite"/>
+        <Setter Property="Content" Value="&#xE113;"/>
+    </Style>
+    <Style x:Key="PhotoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PhotoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Photo"/>
+        <Setter Property="Content" Value="&#xE114;"/>
+    </Style>
+    <Style x:Key="SettingsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SettingsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Settings"/>
+        <Setter Property="Content" Value="&#xE115;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="VideoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="VideoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Video"/>
+        <Setter Property="Content" Value="&#xE116;"/>
+    </Style>
+    <Style x:Key="RefreshAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RefreshAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Refresh"/>
+        <Setter Property="Content" Value="&#xE117;"/>
+    </Style>
+    <Style x:Key="DownloadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DownloadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Download"/>
+        <Setter Property="Content" Value="&#xE118;"/>
+    </Style>
+    <Style x:Key="MailAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Mail"/>
+        <Setter Property="Content" Value="&#xE119;"/>
+    </Style>
+    <Style x:Key="SearchAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SearchAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Search"/>
+        <Setter Property="Content" Value="&#xE11A;"/>
+    </Style>
+    <Style x:Key="HelpAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HelpAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Help"/>
+        <Setter Property="Content" Value="&#xE11B;"/>
+    </Style>
+    <Style x:Key="UploadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UploadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Upload"/>
+        <Setter Property="Content" Value="&#xE11C;"/>
+    </Style>
+    <Style x:Key="EmojiAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="EmojiAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Emoji"/>
+        <Setter Property="Content" Value="&#xE11D;"/>
+    </Style>
+    <Style x:Key="TwoPageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TwoPageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Two Page"/>
+        <Setter Property="Content" Value="&#xE11E;"/>
+    </Style>
+    <Style x:Key="LeaveChatAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LeaveChatAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Upload"/>
+        <Setter Property="Content" Value="&#xE11F;"/>
+    </Style>
+    <Style x:Key="MailForwardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailForwardAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Forward Mail"/>
+        <Setter Property="Content" Value="&#xE120;"/>
+    </Style>
+    <Style x:Key="ClockAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ClockAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Clock"/>
+        <Setter Property="Content" Value="&#xE121;"/>
+    </Style>
+    <Style x:Key="SendAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SendAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Send"/>
+        <Setter Property="Content" Value="&#xE122;"/>
+    </Style>
+    <Style x:Key="CropAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CropAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Crop"/>
+        <Setter Property="Content" Value="&#xE123;"/>
+    </Style>
+    <Style x:Key="RotateCameraAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RotateCameraAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Rotate Camera"/>
+        <Setter Property="Content" Value="&#xE124;"/>
+    </Style>
+    <Style x:Key="PeopleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PeopleAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="People"/>
+        <Setter Property="Content" Value="&#xE125;"/>
+    </Style>
+    <Style x:Key="ClosePaneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ClosePaneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Close Pane"/>
+        <Setter Property="Content" Value="&#xE126;"/>
+    </Style>
+    <Style x:Key="OpenPaneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenPaneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open Pane"/>
+        <Setter Property="Content" Value="&#xE127;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="WorldAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="WorldAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="World"/>
+        <Setter Property="Content" Value="&#xE128;"/>
+    </Style>
+    <Style x:Key="FlagAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FlagAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Flag"/>
+        <Setter Property="Content" Value="&#xE129;"/>
+    </Style>
+    <Style x:Key="PreviewLinkAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PreviewLinkAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Preview Link"/>
+        <Setter Property="Content" Value="&#xE12A;"/>
+    </Style>
+    <Style x:Key="GlobeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GlobeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Globe"/>
+        <Setter Property="Content" Value="&#xE12B;"/>
+    </Style>
+    <Style x:Key="TrimAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TrimAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Trim"/>
+        <Setter Property="Content" Value="&#xE12C;"/>
+    </Style>
+    <Style x:Key="AttachCameraAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AttachCameraAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Attach Camera"/>
+        <Setter Property="Content" Value="&#xE12D;"/>
+    </Style>
+    <Style x:Key="ZoomInAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomInAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zoom In"/>
+        <Setter Property="Content" Value="&#xE12E;"/>
+    </Style>
+    <Style x:Key="BookmarksAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BookmarksAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Bookmarks"/>
+        <Setter Property="Content" Value="&#xE12F;"/>
+    </Style>
+    <Style x:Key="DocumentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DocumentAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Document"/>
+        <Setter Property="Content" Value="&#xE130;"/>
+    </Style>
+    <Style x:Key="ProtectedDocumentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ProtectedDocumentAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Protected Document"/>
+        <Setter Property="Content" Value="&#xE131;"/>
+    </Style>
+    <Style x:Key="PageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Page"/>
+        <Setter Property="Content" Value="&#xE132;"/>
+    </Style>
+    <Style x:Key="BulletsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BulletsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Bullets"/>
+        <Setter Property="Content" Value="&#xE133;"/>
+    </Style>
+    <Style x:Key="CommentAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CommentAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Comment"/>
+        <Setter Property="Content" Value="&#xE134;"/>
+    </Style>
+    <Style x:Key="Mail2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Mail2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Mail2"/>
+        <Setter Property="Content" Value="&#xE135;"/>
+    </Style>
+    <Style x:Key="ContactInfoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactInfoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Contact Info"/>
+        <Setter Property="Content" Value="&#xE136;"/>
+    </Style>
+    <Style x:Key="HangUpAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HangUpAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Hang Up"/>
+        <Setter Property="Content" Value="&#xE137;"/>
+    </Style>
+    <Style x:Key="ViewAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ViewAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="View All"/>
+        <Setter Property="Content" Value="&#xE138;"/>
+    </Style>
+    <Style x:Key="MapPinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MapPinAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Map Pin"/>
+        <Setter Property="Content" Value="&#xE139;"/>
+    </Style>
+    <Style x:Key="PhoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PhoneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Phone"/>
+        <Setter Property="Content" Value="&#xE13A;"/>
+    </Style>
+    <Style x:Key="VideoChatAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="VideoChatAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Video Chat"/>
+        <Setter Property="Content" Value="&#xE13B;"/>
+    </Style>
+    <Style x:Key="SwitchAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SwitchAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Switch"/>
+        <Setter Property="Content" Value="&#xE13C;"/>
+    </Style>
+    <Style x:Key="ContactAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Contact"/>
+        <Setter Property="Content" Value="&#xE13D;"/>
+    </Style>
+
+    -->
+
+    <!--
+
+    <Style x:Key="RenameAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RenameAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Rename"/>
+        <Setter Property="Content" Value="&#xE13E;"/>
+    </Style>
+    <Style x:Key="PinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PinAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Pin"/>
+        <Setter Property="Content" Value="&#xE141;"/>
+    </Style>
+    <Style x:Key="MusicInfoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MusicInfoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Music Info"/>
+        <Setter Property="Content" Value="&#xE142;"/>
+    </Style>
+    <Style x:Key="GoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Go"/>
+        <Setter Property="Content" Value="&#xE143;"/>
+    </Style>
+    <Style x:Key="KeyboardAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="KeyboardAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Keyboard"/>
+        <Setter Property="Content" Value="&#xE144;"/>
+    </Style>
+    <Style x:Key="DockLeftAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DockLeftAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dock Left"/>
+        <Setter Property="Content" Value="&#xE145;"/>
+    </Style>
+    <Style x:Key="DockRightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DockRightAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dock Right"/>
+        <Setter Property="Content" Value="&#xE146;"/>
+    </Style>
+    <Style x:Key="DockBottomAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DockBottomAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dock Bottom"/>
+        <Setter Property="Content" Value="&#xE147;"/>
+    </Style>
+    <Style x:Key="RemoteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RemoteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Remote"/>
+        <Setter Property="Content" Value="&#xE148;"/>
+    </Style>
+    <Style x:Key="SyncAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SyncAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Sync"/>
+        <Setter Property="Content" Value="&#xE149;"/>
+    </Style>
+    <Style x:Key="RotateAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RotateAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Rotate"/>
+        <Setter Property="Content" Value="&#xE14A;"/>
+    </Style>
+    <Style x:Key="ShuffleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShuffleAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Shuffle"/>
+        <Setter Property="Content" Value="&#xE14B;"/>
+    </Style>
+    <Style x:Key="ListAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ListAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="List"/>
+        <Setter Property="Content" Value="&#xE14C;"/>
+    </Style>
+    <Style x:Key="ShopAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShopAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Shop"/>
+        <Setter Property="Content" Value="&#xE14D;"/>
+    </Style>
+    <Style x:Key="SelectAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SelectAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Select All"/>
+        <Setter Property="Content" Value="&#xE14E;"/>
+    </Style>
+    <Style x:Key="OrientationAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OrientationAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Orientation"/>
+        <Setter Property="Content" Value="&#xE14F;"/>
+    </Style>
+    <Style x:Key="ImportAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Import"/>
+        <Setter Property="Content" Value="&#xE150;"/>
+    </Style>
+    <Style x:Key="ImportAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Import All"/>
+        <Setter Property="Content" Value="&#xE151;"/>
+    </Style>
+    <Style x:Key="BrowsePhotosAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BrowsePhotosAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Browse Photos"/>
+        <Setter Property="Content" Value="&#xE155;"/>
+    </Style>
+    <Style x:Key="WebcamAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="WebcamAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Webcam"/>
+        <Setter Property="Content" Value="&#xE156;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="PicturesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PicturesAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Pictures"/>
+        <Setter Property="Content" Value="&#xE158;"/>
+    </Style>
+    <Style x:Key="SaveLocalAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SaveLocalAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Save Local"/>
+        <Setter Property="Content" Value="&#xE159;"/>
+    </Style>
+    <Style x:Key="CaptionAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CaptionAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Caption"/>
+        <Setter Property="Content" Value="&#xE15A;"/>
+    </Style>
+    <Style x:Key="StopAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="StopAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Stop"/>
+        <Setter Property="Content" Value="&#xE15B;"/>
+    </Style>
+    <Style x:Key="ShowResultsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShowResultsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Show Results"/>
+        <Setter Property="Content" Value="&#xE15C;"/>
+    </Style>
+    <Style x:Key="VolumeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="VolumeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Volume"/>
+        <Setter Property="Content" Value="&#xE15D;"/>
+    </Style>
+    <Style x:Key="RepairAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RepairAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Repair"/>
+        <Setter Property="Content" Value="&#xE15E;"/>
+    </Style>
+    <Style x:Key="MessageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MessageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Message"/>
+        <Setter Property="Content" Value="&#xE15F;"/>
+    </Style>
+    <Style x:Key="Page2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Page2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Page2"/>
+        <Setter Property="Content" Value="&#xE160;"/>
+    </Style>
+    <Style x:Key="CalendarDayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarDayAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Day"/>
+        <Setter Property="Content" Value="&#xE161;"/>
+    </Style>
+    <Style x:Key="CalendarWeekAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarWeekAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Week"/>
+        <Setter Property="Content" Value="&#xE162;"/>
+    </Style>
+    <Style x:Key="CalendarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Calendar"/>
+        <Setter Property="Content" Value="&#xE163;"/>
+    </Style>
+    <Style x:Key="CharactersAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CharactersAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Characters"/>
+        <Setter Property="Content" Value="&#xE164;"/>
+    </Style>
+    <Style x:Key="MailReplyAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailReplyAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Reply All"/>
+        <Setter Property="Content" Value="&#xE165;"/>
+    </Style>
+    <Style x:Key="ReadAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ReadAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Read"/>
+        <Setter Property="Content" Value="&#xE166;"/>
+    </Style>
+    <Style x:Key="LinkAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LinkAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Link"/>
+        <Setter Property="Content" Value="&#xE167;"/>
+    </Style>
+    <Style x:Key="AccountsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AccountsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Accounts"/>
+        <Setter Property="Content" Value="&#xE168;"/>
+    </Style>
+    <Style x:Key="ShowBccAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ShowBccAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Show Bcc"/>
+        <Setter Property="Content" Value="&#xE169;"/>
+    </Style>
+    <Style x:Key="HideBccAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HideBccAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Hide Bcc"/>
+        <Setter Property="Content" Value="&#xE16A;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="CutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CutAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Cut"/>
+        <Setter Property="Content" Value="&#xE16B;"/>
+    </Style>
+    <Style x:Key="AttachAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AttachAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Attach"/>
+        <Setter Property="Content" Value="&#xE16C;"/>
+    </Style>
+    <Style x:Key="PasteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PasteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Paste"/>
+        <Setter Property="Content" Value="&#xE16D;"/>
+    </Style>
+    <Style x:Key="FilterAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FilterAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Filter"/>
+        <Setter Property="Content" Value="&#xE16E;"/>
+    </Style>
+    <Style x:Key="CopyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CopyAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Copy"/>
+        <Setter Property="Content" Value="&#xE16F;"/>
+    </Style>
+    <Style x:Key="Emoji2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Emoji2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Emoji2"/>
+        <Setter Property="Content" Value="&#xE170;"/>
+    </Style>
+    <Style x:Key="ImportantAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ImportantAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Important"/>
+        <Setter Property="Content" Value="&#xE171;"/>
+    </Style>
+    <Style x:Key="MailReplyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MailReplyAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Reply"/>
+        <Setter Property="Content" Value="&#xE172;"/>
+    </Style>
+    <Style x:Key="SlideShowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SlideShowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Slideshow"/>
+        <Setter Property="Content" Value="&#xE173;"/>
+    </Style>
+    <Style x:Key="SortAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SortAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Sort"/>
+        <Setter Property="Content" Value="&#xE174;"/>
+    </Style>
+    <Style x:Key="ManageAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ManageAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Manage"/>
+        <Setter Property="Content" Value="&#xE178;"/>
+    </Style>
+    <Style x:Key="AllAppsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AllAppsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="All Apps"/>
+        <Setter Property="Content" Value="&#xE179;"/>
+    </Style>
+    <Style x:Key="DisconnectDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DisconnectDriveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Disconnect Drive"/>
+        <Setter Property="Content" Value="&#xE17A;"/>
+    </Style>
+    <Style x:Key="MapDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MapDriveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Map Drive"/>
+        <Setter Property="Content" Value="&#xE17B;"/>
+    </Style>
+    <Style x:Key="NewWindowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NewWindowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="New Window"/>
+        <Setter Property="Content" Value="&#xE17C;"/>
+    </Style>
+    <Style x:Key="OpenWithAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenWithAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open With"/>
+        <Setter Property="Content" Value="&#xE17D;"/>
+    </Style>
+    <Style x:Key="ContactPresenceAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ContactPresenceAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Presence"/>
+        <Setter Property="Content" Value="&#xE181;"/>
+    </Style>
+    <Style x:Key="PriorityAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PriorityAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Priority"/>
+        <Setter Property="Content" Value="&#xE182;"/>
+    </Style>
+    <Style x:Key="UploadSkyDriveAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UploadSkyDriveAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Skydrive"/>
+        <Setter Property="Content" Value="&#xE183;"/>
+    </Style>
+    <Style x:Key="GoToTodayAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GoToTodayAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Today"/>
+        <Setter Property="Content" Value="&#xE184;"/>
+    </Style>
+    <Style x:Key="FontAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Font"/>
+        <Setter Property="Content" Value="&#xE185;"/>
+    </Style>
+
+    -->
+
+    <!--
+
+    <Style x:Key="FontColorAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontColorAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Font Color"/>
+        <Setter Property="Content" Value="&#xE186;"/>
+    </Style>
+    <Style x:Key="Contact2AppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Contact2AppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Contact"/>
+        <Setter Property="Content" Value="&#xE187;"/>
+    </Style>
+    <Style x:Key="FolderppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Folder"/>
+        <Setter Property="Content" Value="&#xE188;"/>
+    </Style>
+    <Style x:Key="AudioAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AudioAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Audio"/>
+        <Setter Property="Content" Value="&#xE189;"/>
+    </Style>
+    <Style x:Key="PlaceholderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PlaceholderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Placeholder"/>
+        <Setter Property="Content" Value="&#xE18A;"/>
+    </Style>
+    <Style x:Key="ViewAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ViewAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="View"/>
+        <Setter Property="Content" Value="&#xE18B;"/>
+    </Style>
+    <Style x:Key="SetLockScreenAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SetLockscreenAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Set Lockscreen"/>
+        <Setter Property="Content" Value="&#xE18C;"/>
+    </Style>
+    <Style x:Key="SetTitleAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SetTitleAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Set Title"/>
+        <Setter Property="Content" Value="&#xE18D;"/>
+    </Style>
+    <Style x:Key="CcAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CcAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Cc"/>
+        <Setter Property="Content" Value="&#xE190;"/>
+    </Style>
+    <Style x:Key="StopSlideShowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="StopSlideshowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Stop Slideshow"/>
+        <Setter Property="Content" Value="&#xE191;"/>
+    </Style>
+    <Style x:Key="PermissionsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PermissionsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Permisions"/>
+        <Setter Property="Content" Value="&#xE192;"/>
+    </Style>
+    <Style x:Key="HighlightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="HighlightAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Highlight"/>
+        <Setter Property="Content" Value="&#xE193;"/>
+    </Style>
+    <Style x:Key="DisableUpdatesAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DisableUpdatesAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Disable Updates"/>
+        <Setter Property="Content" Value="&#xE194;"/>
+    </Style>
+    <Style x:Key="UnfavoriteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnfavoriteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Unfavorite"/>
+        <Setter Property="Content" Value="&#xE195;"/>
+    </Style>
+    <Style x:Key="UnPinAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnPinAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Unpin"/>
+        <Setter Property="Content" Value="&#xE196;"/>
+    </Style>
+    <Style x:Key="OpenLocalAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenLocalAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open Loal"/>
+        <Setter Property="Content" Value="&#xE197;"/>
+    </Style>
+    <Style x:Key="MuteAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MuteAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Mute"/>
+        <Setter Property="Content" Value="&#xE198;"/>
+    </Style>
+    <Style x:Key="ItalicAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ItalicAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Italic"/>
+        <Setter Property="Content" Value="&#xE199;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="UnderlineAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnderlineAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Underline"/>
+        <Setter Property="Content" Value="&#xE19A;"/>
+    </Style>
+    <Style x:Key="BoldAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BoldAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Bold"/>
+        <Setter Property="Content" Value="&#xE19B;"/>
+    </Style>
+    <Style x:Key="MoveToFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MoveToFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Move to Folder"/>
+        <Setter Property="Content" Value="&#xE19C;"/>
+    </Style>
+    <Style x:Key="LikeDislikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LikeDislikeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Like/Dislike"/>
+        <Setter Property="Content" Value="&#xE19D;"/>
+    </Style>
+    <Style x:Key="DislikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DislikeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Dislike"/>
+        <Setter Property="Content" Value="&#xE19E;"/>
+    </Style>
+    <Style x:Key="LikeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LikeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Like"/>
+        <Setter Property="Content" Value="&#xE19F;"/>
+    </Style>
+    <Style x:Key="AlignRightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignRightAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Align Right"/>
+        <Setter Property="Content" Value="&#xE1A0;"/>
+    </Style>
+    <Style x:Key="AlignCenterAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignCenterAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Align Center"/>
+        <Setter Property="Content" Value="&#xE1A1;"/>
+    </Style>
+    <Style x:Key="AlignLeftAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AlignLeftAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Align Left"/>
+        <Setter Property="Content" Value="&#xE1A2;"/>
+    </Style>
+    <Style x:Key="ZoomAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zoom"/>
+        <Setter Property="Content" Value="&#xE1A3;"/>
+    </Style>
+    <Style x:Key="ZoomOutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZoomOutAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zoom Out"/>
+        <Setter Property="Content" Value="&#xE1A4;"/>
+    </Style>
+    <Style x:Key="OpenFileAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OpenFileAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Open File"/>
+        <Setter Property="Content" Value="&#xE1A5;"/>
+    </Style>
+    <Style x:Key="OtherUserAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OtherUserAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Other User"/>
+        <Setter Property="Content" Value="&#xE1A6;"/>
+    </Style>
+    <Style x:Key="AdminAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AdminAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Admin"/>
+        <Setter Property="Content" Value="&#xE1A7;"/>
+    </Style>
+    <Style x:Key="StreetAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="StreetAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Street"/>
+        <Setter Property="Content" Value="&#xE1C3;"/>
+    </Style>
+    <Style x:Key="MapAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MapAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Map"/>
+        <Setter Property="Content" Value="&#xE1C4;"/>
+    </Style>
+    <Style x:Key="ClearSelectionAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ClearSelectionAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Clear Selection"/>
+        <Setter Property="Content" Value="&#xE1C5;"/>
+    </Style>
+    <Style x:Key="FontDecreaseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontDecreaseAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Decrease Font"/>
+        <Setter Property="Content" Value="&#xE1C6;"/>
+    </Style>
+    <Style x:Key="FontIncreaseAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontIncreaseAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Increase Font"/>
+        <Setter Property="Content" Value="&#xE1C7;"/>
+    </Style>
+    <Style x:Key="FontSizeAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FontSizeAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Font Size"/>
+        <Setter Property="Content" Value="&#xE1C8;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="CellphoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CellphoneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Cellphone"/>
+        <Setter Property="Content" Value="&#xE1C9;"/>
+    </Style>
+    <Style x:Key="ReshareAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ReshareAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Reshare"/>
+        <Setter Property="Content" Value="&#xE1CA;"/>
+    </Style>
+    <Style x:Key="TagAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TagAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Tag"/>
+        <Setter Property="Content" Value="&#xE1CB;"/>
+    </Style>
+    <Style x:Key="RepeatOneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RepeatOneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Repeat Once"/>
+        <Setter Property="Content" Value="&#xE1CC;"/>
+    </Style>
+    <Style x:Key="RepeatAllAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="RepeatAllAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Repeat All"/>
+        <Setter Property="Content" Value="&#xE1CD;"/>
+    </Style>
+    <Style x:Key="OutlineStarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OutlineStarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Outline Star"/>
+        <Setter Property="Content" Value="&#xE1CE;"/>
+    </Style>
+    <Style x:Key="SolidStarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SolidStarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Solid Star"/>
+        <Setter Property="Content" Value="&#xE1CF;"/>
+    </Style>
+    <Style x:Key="CalculatorAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalculatorAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Calculator"/>
+        <Setter Property="Content" Value="&#xE1D0;"/>
+    </Style>
+    <Style x:Key="DirectionsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="DirectionsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Directions"/>
+        <Setter Property="Content" Value="&#xE1D1;"/>
+    </Style>
+    <Style x:Key="TargetAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TargetAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Target"/>
+        <Setter Property="Content" Value="&#xE1D2;"/>
+    </Style>
+    <Style x:Key="LibraryAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="LibraryAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Library"/>
+        <Setter Property="Content" Value="&#xE1D3;"/>
+    </Style>
+    <Style x:Key="PhonebookAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PhonebookAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Phonebook"/>
+        <Setter Property="Content" Value="&#xE1D4;"/>
+    </Style>
+    <Style x:Key="MemoAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MemoAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Memo"/>
+        <Setter Property="Content" Value="&#xE1D5;"/>
+    </Style>
+    <Style x:Key="MicrophoneAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="MicrophoneAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Microphone"/>
+        <Setter Property="Content" Value="&#xE1D6;"/>
+    </Style>
+    <Style x:Key="PostUpdateAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="PostUpdateAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Post Update"/>
+        <Setter Property="Content" Value="&#xE1D7;"/>
+    </Style>
+    <Style x:Key="BackToWindowAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="BackToWindowAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Back to Window"/>
+        <Setter Property="Content" Value="&#xE1D8;"/>
+    </Style>
+    -->
+
+    <!--
+    <Style x:Key="FullScreenAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FullScreenAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Full Screen"/>
+        <Setter Property="Content" Value="&#xE1D9;"/>
+    </Style>
+    <Style x:Key="NewFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="NewFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="New Folder"/>
+        <Setter Property="Content" Value="&#xE1DA;"/>
+    </Style>
+    <Style x:Key="CalendarReplyAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="CalendarReplyAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Calendar Reply"/>
+        <Setter Property="Content" Value="&#xE1DB;"/>
+    </Style>
+    <Style x:Key="UnsyncFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="UnsyncFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Unsync Folder"/>
+        <Setter Property="Content" Value="&#xE1DD;"/>
+    </Style>
+    <Style x:Key="ReportHackedAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ReportHackedAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Report Hacked"/>
+        <Setter Property="Content" Value="&#xE1DE;"/>
+    </Style>
+    <Style x:Key="SyncFolderAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SyncFolderAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Sync Folder"/>
+        <Setter Property="Content" Value="&#xE1DF;"/>
+    </Style>
+    <Style x:Key="BlockContactAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="Block ContactAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="BlockContact"/>
+        <Setter Property="Content" Value="&#xE1E0;"/>
+    </Style>
+    <Style x:Key="SwitchAppsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="SwitchAppsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Switch Apps"/>
+        <Setter Property="Content" Value="&#xE1E1;"/>
+    </Style>
+    <Style x:Key="AddFriendAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="AddFriendAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Add Friend"/>
+        <Setter Property="Content" Value="&#xE1E2;"/>
+    </Style>
+    <Style x:Key="TouchPointerAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TouchPointerAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Touch Pointer"/>
+        <Setter Property="Content" Value="&#xE1E3;"/>
+    </Style>
+    <Style x:Key="GoToStartAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="GoToStartAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Go to Start"/>
+        <Setter Property="Content" Value="&#xE1E4;"/>
+    </Style>
+    <Style x:Key="ZeroBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ZeroBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Zero Bars"/>
+        <Setter Property="Content" Value="&#xE1E5;"/>
+    </Style>
+    <Style x:Key="OneBarAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="OneBarAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="One Bar"/>
+        <Setter Property="Content" Value="&#xE1E6;"/>
+    </Style>
+    <Style x:Key="TwoBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="TwoBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Two Bars"/>
+        <Setter Property="Content" Value="&#xE1E7;"/>
+    </Style>
+    <Style x:Key="ThreeBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="ThreeBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Three Bars"/>
+        <Setter Property="Content" Value="&#xE1E8;"/>
+    </Style>
+    <Style x:Key="FourBarsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
+        <Setter Property="AutomationProperties.AutomationId" Value="FourBarsAppBarButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Four Bars"/>
+        <Setter Property="Content" Value="&#xE1E9;"/>
+    </Style>
+
+    -->
+
+    <!-- Title area styles -->
+
+    <Style x:Key="PageHeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource HeaderTextStyle}">
+        <Setter Property="TextWrapping" Value="NoWrap"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="Margin" Value="0,0,30,40"/>
+    </Style>
+
+    <Style x:Key="PageSubheaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource SubheaderTextStyle}">
+        <Setter Property="TextWrapping" Value="NoWrap"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="Margin" Value="0,0,0,40"/>
+    </Style>
+
+    <Style x:Key="SnappedPageHeaderTextStyle" TargetType="TextBlock" BasedOn="{StaticResource PageSubheaderTextStyle}">
+        <Setter Property="Margin" Value="0,0,18,40"/>
+    </Style>
+
+    <!--
+        BackButtonStyle is used to style a Button for use in the title area of a page.  Margins appropriate for
+        the conventional page layout are included as part of the style.
+    -->
+    <Style x:Key="BackButtonStyle" TargetType="Button">
+        <Setter Property="MinWidth" Value="0"/>
+        <Setter Property="Width" Value="48"/>
+        <Setter Property="Height" Value="48"/>
+        <Setter Property="Margin" Value="36,0,36,36"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
+        <Setter Property="FontWeight" Value="Normal"/>
+        <Setter Property="FontSize" Value="56"/>
+        <Setter Property="AutomationProperties.AutomationId" Value="BackButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Back"/>
+        <Setter Property="AutomationProperties.ItemType" Value="Navigation Button"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="Button">
+                    <Grid x:Name="RootGrid">
+                        <Grid Margin="-1,-16,0,0">
+                            <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" Foreground="{StaticResource BackButtonBackgroundThemeBrush}"/>
+                            <TextBlock x:Name="NormalGlyph" Text="{StaticResource BackButtonGlyph}" Foreground="{StaticResource BackButtonForegroundThemeBrush}"/>
+                            <TextBlock x:Name="ArrowGlyph" Text="&#xE0A6;" Foreground="{StaticResource BackButtonPressedForegroundThemeBrush}" Opacity="0"/>
+                        </Grid>
+                        <Rectangle
+                            x:Name="FocusVisualWhite"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="1.5"/>
+                        <Rectangle
+                            x:Name="FocusVisualBlack"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="0.5"/>
+
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal" />
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverBackgroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="ArrowGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="NormalGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="0"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualWhite"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualBlack"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused" />
+                                <VisualState x:Name="PointerFocused" />
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!--
+        PortraitBackButtonStyle is used to style a Button for use in the title area of a portrait page.  Margins appropriate
+        for the conventional page layout are included as part of the style.
+    -->
+    <Style x:Key="PortraitBackButtonStyle" TargetType="Button" BasedOn="{StaticResource BackButtonStyle}">
+        <Setter Property="Margin" Value="26,0,26,36"/>
+    </Style>
+
+    <!--
+        SnappedBackButtonStyle is used to style a Button for use in the title area of a snapped page.  Margins appropriate
+        for the conventional page layout are included as part of the style.
+        
+        The obvious duplication here is necessary as the glyphs used in snapped are not merely smaller versions of the same
+        glyph but are actually distinct.
+    -->
+    <Style x:Key="SnappedBackButtonStyle" TargetType="Button">
+        <Setter Property="MinWidth" Value="0"/>
+        <Setter Property="Margin" Value="20,0,0,0"/>
+        <Setter Property="VerticalAlignment" Value="Bottom"/>
+        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
+        <Setter Property="FontWeight" Value="Normal"/>
+        <Setter Property="FontSize" Value="26.66667"/>
+        <Setter Property="AutomationProperties.AutomationId" Value="BackButton"/>
+        <Setter Property="AutomationProperties.Name" Value="Back"/>
+        <Setter Property="AutomationProperties.ItemType" Value="Navigation Button"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="Button">
+                    <Grid x:Name="RootGrid" Width="36" Height="36" Margin="-3,0,7,33">
+                        <Grid Margin="-1,-1,0,0">
+                            <TextBlock x:Name="BackgroundGlyph" Text="&#xE0D4;" Foreground="{StaticResource BackButtonBackgroundThemeBrush}"/>
+                            <TextBlock x:Name="NormalGlyph" Text="{StaticResource BackButtonSnappedGlyph}" Foreground="{StaticResource BackButtonForegroundThemeBrush}"/>
+                            <TextBlock x:Name="ArrowGlyph" Text="&#xE0C4;" Foreground="{StaticResource BackButtonPressedForegroundThemeBrush}" Opacity="0"/>
+                        </Grid>
+                        <Rectangle
+                            x:Name="FocusVisualWhite"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="1.5"/>
+                        <Rectangle
+                            x:Name="FocusVisualBlack"
+                            IsHitTestVisible="False"
+                            Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
+                            StrokeEndLineCap="Square"
+                            StrokeDashArray="1,1"
+                            Opacity="0"
+                            StrokeDashOffset="0.5"/>
+
+                        <VisualStateManager.VisualStateGroups>
+                            <VisualStateGroup x:Name="CommonStates">
+                                <VisualState x:Name="Normal" />
+                                <VisualState x:Name="PointerOver">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverBackgroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Pressed">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonForegroundThemeBrush}"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="ArrowGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="NormalGlyph"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="0"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Disabled">
+                                    <Storyboard>
+                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
+                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
+                                        </ObjectAnimationUsingKeyFrames>
+                                    </Storyboard>
+                                </VisualState>
+                            </VisualStateGroup>
+                            <VisualStateGroup x:Name="FocusStates">
+                                <VisualState x:Name="Focused">
+                                    <Storyboard>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualWhite"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                        <DoubleAnimation
+                                            Storyboard.TargetName="FocusVisualBlack"
+                                            Storyboard.TargetProperty="Opacity"
+                                            To="1"
+                                            Duration="0"/>
+                                    </Storyboard>
+                                </VisualState>
+                                <VisualState x:Name="Unfocused" />
+                                <VisualState x:Name="PointerFocused" />
+                            </VisualStateGroup>
+                        </VisualStateManager.VisualStateGroups>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!-- Item templates -->
+
+    <!-- Grid-appropriate 250 pixel square item template as seen in the GroupedItemsPage and ItemsPage -->
+    <DataTemplate x:Key="Standard250x250ItemTemplate">
+        <Grid HorizontalAlignment="Left" Width="250" Height="250">
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
+            </Border>
+            <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
+                <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
+                <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- Grid-appropriate 500 by 130 pixel item template as seen in the GroupDetailPage -->
+    <DataTemplate x:Key="Standard500x130ItemTemplate">
+        <Grid Height="110" Width="480" Margin="10">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
+            </Border>
+            <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- List-appropriate 130 pixel high item template as seen in the SplitPage -->
+    <DataTemplate x:Key="Standard130ItemTemplate">
+        <Grid Height="110" Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
+                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
+            </Border>
+            <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!--
+        List-appropriate 80 pixel high item template as seen in the SplitPage when Filled, and
+        the following pages when snapped: GroupedItemsPage, GroupDetailPage, and ItemsPage
+    -->
+    <DataTemplate x:Key="Standard80ItemTemplate">
+        <Grid Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="60" Height="60">
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>
+            </Border>
+            <StackPanel Grid.Column="1" Margin="10,0,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource ItemTextStyle}" MaxHeight="40"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- Grid-appropriate 300 by 70 pixel item template as seen in the SearchResultsPage -->
+    <DataTemplate x:Key="StandardSmallIcon300x70ItemTemplate">
+        <Grid Width="294" Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,0,0,10" Width="40" Height="40">
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>
+            </Border>
+            <StackPanel Grid.Column="1" Margin="10,-10,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource BodyTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!-- List-appropriate 70 pixel high item template as seen in the SearchResultsPage when Snapped -->
+    <DataTemplate x:Key="StandardSmallIcon70ItemTemplate">
+        <Grid Margin="6">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,0,0,10" Width="40" Height="40">
+                <Image Source="{Binding Image}" Stretch="UniformToFill"/>
+            </Border>
+            <StackPanel Grid.Column="1" Margin="10,-10,0,0">
+                <TextBlock Text="{Binding Title}" Style="{StaticResource BodyTextStyle}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Subtitle}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+                <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
+            </StackPanel>
+        </Grid>
+    </DataTemplate>
+
+    <!--
+      190x130 pixel item template for displaying file previews as seen in the FileOpenPickerPage
+      Includes an elaborate tooltip to display title and description text
+  -->
+    <DataTemplate x:Key="StandardFileWithTooltip190x130ItemTemplate">
+        <Grid>
+            <Grid Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
+                <Image
+                    Source="{Binding Image}"
+                    Width="190"
+                    Height="130"
+                    HorizontalAlignment="Center"
+                    VerticalAlignment="Center"
+                    Stretch="Uniform"/>
+            </Grid>
+            <ToolTipService.Placement>Mouse</ToolTipService.Placement>
+            <ToolTipService.ToolTip>
+                <ToolTip>
+                    <ToolTip.Style>
+                        <Style TargetType="ToolTip">
+                            <Setter Property="BorderBrush" Value="{StaticResource ToolTipBackgroundThemeBrush}" />
+                            <Setter Property="Padding" Value="0" />
+                        </Style>
+                    </ToolTip.Style>
+
+                    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="Auto"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+
+                        <Grid Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="20">
+                            <Image
+                                Source="{Binding Image}"
+                                Width="160"
+                                Height="160"
+                                HorizontalAlignment="Center"
+                                VerticalAlignment="Center"
+                                Stretch="Uniform"/>
+                        </Grid>
+                        <StackPanel Width="200" Grid.Column="1" Margin="0,20,20,20">
+                            <TextBlock Text="{Binding Title}" TextWrapping="NoWrap" Style="{StaticResource BodyTextStyle}"/>
+                            <TextBlock Text="{Binding Description}" MaxHeight="140" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" Style="{StaticResource BodyTextStyle}"/>
+                        </StackPanel>
+                    </Grid>
+                </ToolTip>
+            </ToolTipService.ToolTip>
+        </Grid>
+    </DataTemplate>
+
+    <!-- ScrollViewer styles -->
+
+    <Style x:Key="HorizontalScrollViewerStyle" TargetType="ScrollViewer">
+        <Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
+        <Setter Property="VerticalScrollBarVisibility" Value="Disabled"/>
+        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Enabled" />
+        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" />
+        <Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
+    </Style>
+
+    <Style x:Key="VerticalScrollViewerStyle" TargetType="ScrollViewer">
+        <Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>
+        <Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
+        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
+        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Enabled" />
+        <Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
+    </Style>
+
+    <!-- Page layout roots typically use entrance animations and a theme-appropriate background color -->
+
+    <Style x:Key="LayoutRootStyle" TargetType="Panel">
+        <Setter Property="Background" Value="{StaticResource ApplicationPageBackgroundThemeBrush}"/>
+        <Setter Property="ChildrenTransitions">
+            <Setter.Value>
+                <TransitionCollection>
+                    <EntranceThemeTransition/>
+                </TransitionCollection>
+            </Setter.Value>
+        </Setter>
+    </Style>
+</ResourceDictionary>
diff --git a/Release/samples/WindowsLiveAuth/MainPage.xaml b/Release/samples/WindowsLiveAuth/MainPage.xaml
new file mode 100644 (file)
index 0000000..83daf46
--- /dev/null
@@ -0,0 +1,21 @@
+ļ»æ<Page
+    x:Class="WindowsLiveAuth.MainPage"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:WindowsLiveAuth"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d">
+
+    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
+        <Button Name="LogInButton" Content="Log In" HorizontalAlignment="Left" Margin="249,76,0,0" VerticalAlignment="Top" Click="Button_Click_1"/>
+        <Button Name="GetInfoButton" Content="Get Info" HorizontalAlignment="Left" Margin="249,149,0,0" VerticalAlignment="Top" Click="Button_Click_2" Visibility="Visible"/>
+        <TextBox Name="Box1" HorizontalAlignment="Left" Height="9" Margin="360,155,0,0" TextWrapping="Wrap" Text="me" FontSize="18" VerticalAlignment="Top" Width="442" Visibility="Visible"/>
+
+        <TextBlock Name="Block1" FontSize="20" HorizontalAlignment="Left" Height="455" Margin="32,234,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="1312" IsTextSelectionEnabled="True"/>
+        <Button Name="LogOutButton" Content="Log Out" HorizontalAlignment="Left" Margin="249,76,0,0" VerticalAlignment="Top" Visibility="Collapsed" Click="LogOutButton_Click"/>
+        <Button Name="UploadButton" Content="Upload" HorizontalAlignment="Left" Margin="1016,149,0,0" VerticalAlignment="Top" Click="UploadButton_Click" Visibility="Visible"/>
+        <Button Name="DownloadButton" Content="Download" HorizontalAlignment="Left" Margin="1146,149,0,0" VerticalAlignment="Top" Visibility="Visible" Click="DownloadButton_Click"/>
+
+    </Grid>
+</Page>
diff --git a/Release/samples/WindowsLiveAuth/MainPage.xaml.cpp b/Release/samples/WindowsLiveAuth/MainPage.xaml.cpp
new file mode 100644 (file)
index 0000000..c7280bf
--- /dev/null
@@ -0,0 +1,200 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ ****/
+
+#include "pch.h"
+
+#include "MainPage.xaml.h"
+
+#include "cpprest/filestream.h"
+
+using namespace WindowsLiveAuth;
+
+using namespace Platform;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+using namespace Platform::Collections;
+using namespace Windows::Security::Authentication::OnlineId;
+
+// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
+
+MainPage::MainPage() { InitializeComponent(); }
+
+/// <summary>
+/// Invoked when this page is about to be displayed in a Frame.
+/// </summary>
+/// <param name="e">Event data that describes how this page was reached.  The Parameter
+/// property is typically used to configure the page.</param>
+void MainPage::OnNavigatedTo(NavigationEventArgs ^ e)
+{
+    (void)e; // Unused parameter
+}
+
+static web::live::live_client lv_client;
+
+void MainPage::Button_Click_1(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
+{
+    try
+    {
+        auto ui_ctx = pplx::task_continuation_context::use_current();
+
+        std::vector<utility::string_t> scopes;
+        scopes.push_back(web::live::scopes::wl_basic);
+        scopes.push_back(web::live::scopes::wl_skydrive);
+        scopes.push_back(web::live::scopes::wl_skydrive_update);
+        lv_client.login(std::begin(scopes), std::end(scopes))
+            .then(
+                [this](bool ok) {
+                    if (ok)
+                    {
+                        this->LogOutButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
+                        this->LogInButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+
+                        this->Block1->Text =
+                            ref new Platform::String((L"access_token = \n" + lv_client.access_token()).c_str());
+                    }
+                },
+                ui_ctx);
+    }
+    catch (...)
+    {
+    }
+}
+
+void MainPage::LogOutButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
+{
+    auto ui_ctx = pplx::task_continuation_context::use_current();
+
+    lv_client.logout().then(
+        [this](bool) {
+            this->LogOutButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+            this->LogInButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
+        },
+        ui_ctx);
+}
+
+// The following functions let you get information for an arbitrary WL resource, upload a file, or download a file.
+// Use the Live Connect Interactive SDK on MSDN to explore your WL data and then try the same here.
+//
+// Some other things to try:
+//
+//  delete a file using lv_client.remove()
+//  copy or move a file using lv_client.copy() and lv_client.move().
+//  create a contact using lv_client.post()
+//  modify a calendar event using lv_client.put()
+//
+void MainPage::Button_Click_2(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
+{
+    auto ui_ctx = pplx::task_continuation_context::use_current();
+
+    lv_client.get(this->Box1->Text->Data())
+        .then(
+            [this](pplx::task<web::json::value> value) {
+                try
+                {
+                    auto str = value.get().serialize();
+                    this->Block1->Text = ref new Platform::String(str.c_str());
+                }
+                catch (std::exception& exc)
+                {
+                    this->Block1->Text =
+                        ref new Platform::String(utility::conversions::to_string_t(exc.what()).c_str());
+                }
+            },
+            ui_ctx);
+}
+
+void MainPage::UploadButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
+{
+    this->Block1->Text = ref new Platform::String(L"Processing request...");
+
+    auto ui_ctx = pplx::task_continuation_context::use_current();
+
+    auto filePicker = ref new Windows::Storage::Pickers::FileOpenPicker();
+    filePicker->ViewMode = Windows::Storage::Pickers::PickerViewMode::List;
+    filePicker->FileTypeFilter->Append(ref new Platform::String(L".txt"));
+    filePicker->FileTypeFilter->Append(ref new Platform::String(L".jpg"));
+    filePicker->FileTypeFilter->Append(ref new Platform::String(L".pdf"));
+    filePicker->FileTypeFilter->Append(ref new Platform::String(L".docx"));
+    filePicker->FileTypeFilter->Append(ref new Platform::String(L".doc"));
+
+    auto file = filePicker->PickSingleFileAsync();
+
+    utility::string_t path = this->Box1->Text->Data();
+
+    pplx::create_task(file)
+        .then([path](Windows::Storage::StorageFile ^ file) {
+            if (file == nullptr)
+            {
+                throw std::exception("No file was picked");
+            }
+
+            auto full_path = path + L"/" + file->Name->Data();
+
+            return lv_client.upload(full_path, file);
+        })
+        .then(
+            [this](pplx::task<web::json::value> response) {
+                try
+                {
+                    auto message = response.get().serialize();
+                    this->Block1->Text = ref new Platform::String(message.c_str());
+                }
+                catch (std::exception& exc)
+                {
+                    this->Block1->Text =
+                        ref new Platform::String(utility::conversions::to_string_t(exc.what()).c_str());
+                }
+            },
+            ui_ctx);
+}
+
+void MainPage::DownloadButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
+{
+    this->Block1->Text = ref new Platform::String(L"Processing request...");
+
+    auto ui_ctx = pplx::task_continuation_context::use_current();
+    utility::string_t path = this->Box1->Text->Data();
+
+    // Start by getting the file metadata from OneDrive. We need the file name.
+    lv_client.get(path)
+        .then([this](web::json::value file_info) {
+            if (!file_info.is_object()) throw std::exception("unexpected file info response format");
+
+            auto name = file_info[L"name"].as_string();
+
+            // Once we have the name, we can create a storage file in the downloads folder.
+
+            return pplx::create_task(Windows::Storage::DownloadsFolder::CreateFileAsync(
+                ref new Platform::String(name.c_str()), Windows::Storage::CreationCollisionOption::GenerateUniqueName));
+        })
+        .then([path, ui_ctx, this](Windows::Storage::StorageFile ^ file) {
+            if (file == nullptr) throw std::exception("unexpected file info response format");
+            auto name = file->Name;
+            // With a file reference in hand, we download the file.
+            return lv_client.download(path, file);
+        })
+        .then(
+            [this](pplx::task<size_t> response) {
+                try
+                {
+                    response.wait();
+                    this->Block1->Text = ref new Platform::String(L"Download complete.");
+                }
+                catch (std::exception& exc)
+                {
+                    this->Block1->Text =
+                        ref new Platform::String(utility::conversions::to_string_t(exc.what()).c_str());
+                }
+            },
+            ui_ctx);
+}
diff --git a/Release/samples/WindowsLiveAuth/MainPage.xaml.h b/Release/samples/WindowsLiveAuth/MainPage.xaml.h
new file mode 100644 (file)
index 0000000..092d513
--- /dev/null
@@ -0,0 +1,30 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ ****/
+#pragma once
+
+#include "MainPage.g.h"
+
+namespace WindowsLiveAuth
+{
+/// <summary>
+/// An empty page that can be used on its own or navigated to within a Frame.
+/// </summary>
+public
+ref class MainPage sealed
+{
+public:
+    MainPage();
+
+protected:
+    virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e) override;
+
+private:
+    void Button_Click_1(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
+    void Button_Click_2(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
+    void LogOutButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
+    void UploadButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
+    void DownloadButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
+};
+} // namespace WindowsLiveAuth
diff --git a/Release/samples/WindowsLiveAuth/Package.appxmanifest b/Release/samples/WindowsLiveAuth/Package.appxmanifest
new file mode 100644 (file)
index 0000000..568ed39
--- /dev/null
@@ -0,0 +1,31 @@
+ļ»æ<?xml version="1.0" encoding="utf-8"?>
+<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
+  <Identity Name="64521d51-da30-445f-ba7b-d79f27cf8c7c" Publisher="CN=stgates" Version="1.0.0.0" />
+  <Properties>
+    <DisplayName>WindowsLiveAuth</DisplayName>
+    <PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
+    <Logo>Assets\StoreLogo.png</Logo>
+  </Properties>
+  <Prerequisites>
+    <OSMinVersion>6.2.1</OSMinVersion>
+    <OSMaxVersionTested>6.2.1</OSMaxVersionTested>
+  </Prerequisites>
+  <Resources>
+    <Resource Language="x-generate" />
+  </Resources>
+  <Applications>
+    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="WindowsLiveAuth.App">
+      <m2:VisualElements DisplayName="WindowsLiveAuth" Description="WindowsLiveAuth" BackgroundColor="#464646" ForegroundText="light" Square150x150Logo="Assets\Logo.png" Square30x30Logo="Assets\SmallLogo.png">
+        <m2:DefaultTile>
+          <m2:ShowNameOnTiles>
+            <m2:ShowOn Tile="square150x150Logo" />
+          </m2:ShowNameOnTiles>
+        </m2:DefaultTile>
+        <m2:SplashScreen Image="Assets\SplashScreen.png" />
+      </m2:VisualElements>
+    </Application>
+  </Applications>
+  <Capabilities>
+    <Capability Name="internetClient" />
+  </Capabilities>
+</Package>
\ No newline at end of file
diff --git a/Release/samples/WindowsLiveAuth/live_connect.h b/Release/samples/WindowsLiveAuth/live_connect.h
new file mode 100644 (file)
index 0000000..325538c
--- /dev/null
@@ -0,0 +1,452 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * live_connect.h
+ *
+ * Simple API for connecting to Windows Live services, such as OneDrive and Hotmail.
+ * Only supported for App Store apps.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/filestream.h"
+#include "cpprest/http_client.h"
+#include "cpprest/streams.h"
+#include <collection.h>
+
+namespace web
+{
+namespace live
+{
+#if defined(__cplusplus_winrt)
+
+/// <summary>
+/// This namespace contains symbols for the standard Windows Live permission scopes, which
+/// determine what privileges the application has to access data. What operations are allowed
+/// and what details are returned will depend on privileges requested and granted.
+///
+/// See MSDN documentation for Live Connect at http://msdn.microsoft.com/en-us/live/ for details
+/// on the precise meaning of these scopes.
+/// </summary>
+namespace scopes
+{
+static const utility::string_t wl_birthday = U("wl.birthday");
+static const utility::string_t wl_basic = U("wl.basic");
+static const utility::string_t wl_calendars = U("wl.calendars");
+static const utility::string_t wl_calendars_update = U("wl.calendars_update");
+static const utility::string_t wl_contacts_birthday = U("wl.contacts_birthday");
+static const utility::string_t wl_contacts_create = U("wl.contacts_create");
+static const utility::string_t wl_contacts_calendars = U("wl.contacts_calendars");
+static const utility::string_t wl_contacts_photos = U("wl.contacts_photos");
+static const utility::string_t wl_contacts_skydrive = U("wl.contacts_skydrive");
+static const utility::string_t wl_emails = U("wl.emails");
+static const utility::string_t wl_events_create = U("wl.events_create");
+static const utility::string_t wl_messenger = U("wl.messenger");
+static const utility::string_t wl_offline_access = U("wl.offline_access");
+static const utility::string_t wl_phone_numbers = U("wl.phone_numbers");
+static const utility::string_t wl_photos = U("wl.photos");
+static const utility::string_t wl_postal_addresses = U("wl.postal_addresses");
+static const utility::string_t wl_share = U("wl.share");
+static const utility::string_t wl_signin = U("wl.signin");
+static const utility::string_t wl_skydrive = U("wl.skydrive");
+static const utility::string_t wl_skydrive_update = U("wl.skydrive_update");
+static const utility::string_t wl_work_profile = U("wl.work_profile");
+} // namespace scopes
+
+/// <summary>
+/// Represents a session connected to Windows Live services like Calendar, Contacts, OneDrive, and the
+/// user profile, by using the Live Connect REST API. It is a thin layer on top of the Casablanca HTTP
+/// library, tailored to the usage scenarios for Windows Live clients.
+/// </summary>
+/// <remarks>
+///   See http://msdn.microsoft.com/onedrive/ for details on using the OneDrive REST API.
+///
+///   Note: when passing resource paths into the functions in this class, it is not necessary to add the
+///   leading '/' path character. It will be added automatically when constructing the HTTP request.
+///
+///   Most Windows Live requests will return data in the form of plain text or JSON. The APIs in this
+///   class will typically return data as JSON. The most significant exception to this is download(),
+///   which will not return any data to be extracted, it will place the result in the stream or file
+///   passed into it. Also, remove() will return data only to provide error information: for non-error
+///   cases, the JSON value will be <c>null</c>.
+///
+///   This class relies on PPL tasks to represent asynchrony: the examples contained within the comments
+///   show synchronous forms of invoking these APIs. In real use, applications should be relying on
+///   .then() instead of .get() or .wait().
+/// </remarks>
+class live_client
+{
+public:
+    /// <summary>
+    /// Constructs a new Windows Live client
+    /// </summary>
+    live_client()
+        : m_client(L"https://apis.live.net/v5.0/")
+        , m_authenticator(ref new Windows::Security::Authentication::OnlineId::OnlineIdAuthenticator)
+    {
+    }
+
+    /// <summary>
+    /// Authenticates the current user and requests permission to access a listed Live services.
+    /// </summary>
+    /// <param name="services">A string containing a space-separated list of access scopes to request</param>
+    /// <returns><c>true</c> if authentication succeeded, <c>false</c> otherwise.</returns>
+    pplx::task<bool> login(utility::string_t scopes)
+    {
+        this->m_token.clear();
+
+        auto request = ref new Windows::Security::Authentication::OnlineId::OnlineIdServiceTicketRequest(
+            ref new Platform::String(scopes.c_str()), "DELEGATION");
+
+        return pplx::create_task(m_authenticator->AuthenticateUserAsync(request))
+            .then([this](Windows::Security::Authentication::OnlineId::UserIdentity ^ ident) {
+                if (ident->Tickets->Size > 0)
+                {
+                    auto ticket = ident->Tickets->GetAt(0);
+
+                    m_token = std::wstring(ticket->Value->Data());
+                    return true;
+                }
+                return false;
+            });
+    }
+
+    /// <summary>
+    /// Authenticates the current user and requests permission to access a listed Live services.
+    /// </summary>
+    /// <param name="begin">
+    ///   The starting point of an iterator over a std::string collection, (for example a vector of
+    ///   strings), holding the names of scopes to get permission for.
+    /// </param>
+    /// <param name="end">The end point for the same collection.</param>
+    /// <returns><c>true</c> if authentication succeeded, <c>false</c> otherwise.</returns>
+    template<typename Iter>
+    pplx::task<bool> login(Iter begin, Iter end)
+    {
+        utility::string_t services;
+
+        for (Iter iter = begin; iter != end; ++iter)
+        {
+            services.append(*iter);
+            services.append(utility::string_t(L" "));
+        }
+
+        return login(services);
+    }
+
+    /// <summary>
+    /// Dismisses any and all permissions that have been granted to the user.
+    /// </summary>
+    /// <returns><c>true</c> if logout succeeded, <c>false</c> otherwise.</returns>
+    /// <remarks>
+    ///   Whether the logout attempt was successful or not, the application will
+    ///   be required to log in again, since the access token will be cleared.
+    /// </remarks>
+    pplx::task<bool> logout()
+    {
+        this->m_token.clear();
+
+        if (!m_authenticator->CanSignOut) return pplx::task_from_result(false);
+
+        return pplx::create_task(m_authenticator->SignOutUserAsync()).then([this] { return true; });
+    }
+
+    /// <summary>
+    /// Retrieves the access token in use by this client instance.
+    /// </summary>
+    /// <returns>The current token: a string. An invalid token is indicated by an empty string.</returns>
+    const utility::string_t& access_token() const { return m_token; }
+
+    /// <summary>
+    /// Retrieves data from Windows Live.
+    /// </summary>
+    /// <param name="resource">The Windows Live resource to retrieve.</param>
+    /// <returns>The JSON value resulting from the request.</returns>
+    /// <example>To get the current user profile: <code>json::value profile =
+    /// live_clnt.get(L"me").get().extract_json().get();</code></example>
+    pplx::task<web::json::value> get(const utility::string_t& resource)
+    {
+        return _make_request(web::http::methods::GET, resource).then([](web::http::http_response response) {
+            return _json_extract(response);
+        });
+    }
+
+    /// <summary>
+    /// Deletes data from Windows Live.
+    /// </summary>
+    /// <param name="resource">The Windows Live resource to delete.</param>
+    /// <returns>The JSON value resulting from the request.</returns>
+    /// <example>To delete a file: <code>live_clnt.remove(L"file.NNNNNNNNNNNN").wait();</code>, where file.NNNNNNNNNNNN
+    /// is a file identifier.</example>
+    pplx::task<web::json::value> remove(const utility::string_t& resource)
+    {
+        return _make_request(web::http::methods::DEL, resource).then([](web::http::http_response response) {
+            return _json_extract(response);
+        });
+    }
+
+    /// <summary>
+    /// Modifies data in Windows Live.
+    /// </summary>
+    /// <param name="resource">The Windows Live resource to modify.</param>
+    /// <returns>The JSON value resulting from the request.</returns>
+    /// <remarks>In most cases, the response will contain data about the modified resource.</remarks>
+    pplx::task<web::json::value> put(const utility::string_t& resource, const web::json::value& data)
+    {
+        return _make_request(web::http::methods::PUT, resource, data).then([](web::http::http_response response) {
+            return _json_extract(response);
+        });
+    }
+
+    /// <summary>
+    /// Adds data to Windows Live.
+    /// </summary>
+    /// <param name="resource">The Windows Live resource (such as a folder) where data should be added.</param>
+    /// <returns>The JSON value resulting from the request.</returns>
+    /// <remarks>In most cases, the response will contain data about the added resource.</remarks>
+    /// <example>To add a contact: <code>json::value contact = ...; live_clnt.post(L"me/contacts",
+    /// contact).wait();</code></example>
+    pplx::task<web::json::value> post(const utility::string_t& resource, const web::json::value& data)
+    {
+        return _make_request(web::http::methods::POST, resource, data).then([](web::http::http_response response) {
+            return _json_extract(response);
+        });
+    }
+
+    /// <summary>
+    /// Copies data in Windows Live.
+    /// </summary>
+    /// <param name="resource">The Windows Live resource to copy.</param>
+    /// <param name="destination">The location where the resource copy should be placed.</param>
+    /// <returns>The JSON value resulting from the request.</returns>
+    /// <remarks>In most cases, the response will contain data about the added resource.</remarks>
+    /// <example>To copy a file: <code>live_clnt.copy(L"file.NNNNNNNNNNNN",
+    /// L"folder.MMMMMMMMMMMM").wait();</code></example>
+    pplx::task<web::json::value> copy(const utility::string_t& resource, const utility::string_t& destination)
+    {
+        return _make_request(U("COPY"), resource, destination).then([](web::http::http_response response) {
+            return _json_extract(response);
+        });
+    }
+
+    /// <summary>
+    /// Moves data in Windows Live.
+    /// </summary>
+    /// <param name="resource">The Windows Live resource to move.</param>
+    /// <param name="destination">The location where the resource should be placed.</param>
+    /// <returns>The JSON value resulting from the request.</returns>
+    /// <remarks>In most cases, the response will contain data about the resource in its new location.</remarks>
+    /// <example>To copy a file: <code>live_clnt.copy(L"file.NNNNNNNNNNNN",
+    /// L"folder.MMMMMMMMMMMM").wait();</code></example>
+    pplx::task<web::json::value> move(const utility::string_t& resource, const utility::string_t& destination)
+    {
+        return _make_request(U("MOVE"), resource, destination).then([](web::http::http_response response) {
+            return _json_extract(response);
+        });
+    }
+
+    /// <summary>
+    /// Download a file from OneDrive.
+    /// </summary>
+    /// <param name="file_id">The OneDrive file id to download.</param>
+    /// <param name="stream">A stream into which the contents of the file should be placed.</param>
+    /// <returns>The size of the downloaded resource.</returns>
+    /// <remarks></remarks>
+    /// <example>To download a file: <code>ostream stream = ...; live_clnt.download(L"file.NNNNNNNNNNNN",
+    /// ostream).get().content_ready().wait();</code></example>
+    pplx::task<size_t> download(const utility::string_t& file_id, concurrency::streams::ostream stream)
+    {
+        web::http::uri_builder bldr;
+        bldr.append(file_id);
+        bldr.append_path(U("content"));
+
+        web::http::http_request req(web::http::methods::GET);
+        req.set_request_uri(bldr.to_string());
+
+        return _make_request(req).then([stream](web::http::http_response response) -> pplx::task<size_t> {
+            if (response.status_code() >= 400)
+            {
+                return response.extract_string().then([](utility::string_t message) -> pplx::task<size_t> {
+                    return pplx::task_from_exception<size_t>(
+                        std::exception(utility::conversions::to_utf8string(message).c_str()));
+                });
+            }
+            return response.body().read_to_end(stream.streambuf());
+        });
+    }
+
+    /// <summary>
+    /// Download a file from OneDrive.
+    /// </summary>
+    /// <param name="file_id">The OneDrive file id to download.</param>
+    /// <param name="file">A StorageFile reference identifying the target for the downloaded data.</param>
+    /// <returns>The size of the downloaded resource.</returns>
+    /// <remarks></remarks>
+    pplx::task<size_t> download(const utility::string_t& file_id, Windows::Storage::StorageFile ^ file)
+    {
+        if (file == nullptr) throw std::invalid_argument("file reference cannot be null");
+
+        return concurrency::streams::file_stream<uint8_t>::open_ostream(file).then(
+            [file_id, this](concurrency::streams::ostream stream) {
+                web::http::uri_builder bldr;
+                bldr.append(file_id);
+                bldr.append_path(U("content"));
+
+                web::http::http_request req(web::http::methods::GET);
+                req.set_request_uri(bldr.to_string());
+
+                return _make_request(req)
+                    .then([stream](web::http::http_response response) -> pplx::task<size_t> {
+                        if (response.status_code() >= 400)
+                        {
+                            return response.extract_string().then([](utility::string_t message) -> pplx::task<size_t> {
+                                return pplx::task_from_exception<size_t>(
+                                    std::exception(utility::conversions::to_utf8string(message).c_str()));
+                            });
+                        }
+                        return response.body().read_to_end(stream.streambuf());
+                    })
+                    .then([stream](pplx::task<size_t> ret_task) {
+                        return stream.flush().then([stream, ret_task]() { return stream.close(); }).then([ret_task]() {
+                            return ret_task;
+                        });
+                    });
+            });
+    }
+
+    /// <summary>
+    /// Upload a file to OneDrive.
+    /// </summary>
+    /// <param name="path">The path of the file location in OneDrive. It should be of the form
+    /// "folder.NNNNNNNNNN/files/file_name.ext"</param> <param name="stream">A stream from which data for the file will
+    /// be read.</param> <param name="content_length">The size of the data to upload.</param> <returns>The JSON value
+    /// resulting from the request, containing metadata about the uploaded file.</returns> <remarks>
+    ///   The stream must contain at least as many bytes as indicated by 'content_length', starting from its current
+    ///   read position.
+    /// </remarks>
+    pplx::task<web::json::value> upload(const utility::string_t& path,
+                                        concurrency::streams::istream stream,
+                                        size_t content_length)
+    {
+        web::http::uri_builder bldr;
+        bldr.append(path);
+
+        web::http::http_request req(web::http::methods::PUT);
+        req.set_request_uri(bldr.to_string());
+        req.set_body(stream, content_length, utility::string_t{});
+
+        return _make_request(req).then([](web::http::http_response response) { return _json_extract(response); });
+    }
+
+    /// <summary>
+    /// Upload a file to OneDrive.
+    /// </summary>
+    /// <param name="path">The path of the file location in OneDrive. It should be of the form
+    /// "folder.NNNNNNNNNN/files/file_name.ext"</param> <param name="file">A StorageFile reference identifying the
+    /// source of the uploaded data.</param> <param name="content_length">The size of the data to upload.</param>
+    /// <returns>The JSON value resulting from the request, containing metadata about the uploaded file.</returns>
+    /// <remarks>The entire file will be uploaded.</remarks>
+    pplx::task<web::json::value> upload(const utility::string_t& path, Windows::Storage::StorageFile ^ file)
+    {
+        if (file == nullptr) throw std::invalid_argument("file reference cannot be null");
+
+        return pplx::create_task(file->GetBasicPropertiesAsync())
+            .then([path, this, file](Windows::Storage::FileProperties::BasicProperties ^ props) {
+                if (props == nullptr)
+                    throw std::exception("failed to retrieve file properties; cannot determine its size");
+                size_t size = (size_t)props->Size;
+
+                return concurrency::streams::file_stream<uint8_t>::open_istream(file).then(
+                    [path, this, size](concurrency::streams::istream stream) {
+                        web::http::uri_builder bldr;
+                        bldr.append(path);
+
+                        web::http::http_request req(web::http::methods::PUT);
+                        req.set_request_uri(bldr.to_string());
+                        req.set_body(stream, size, utility::string_t{});
+
+                        return _make_request(req)
+                            .then([](web::http::http_response response) { return response.content_ready(); })
+                            .then([stream](pplx::task<web::http::http_response> response) {
+                                return stream.close().then([response]() { return _json_extract(response.get()); });
+                            });
+                    });
+            });
+    }
+
+private:
+    static pplx::task<web::json::value> _json_extract(web::http::http_response response)
+    {
+        switch (response.status_code())
+        {
+            case web::http::status_codes::NoContent: return pplx::task_from_result(web::json::value::null());
+            default:
+                if (response.status_code() >= 400)
+                {
+                    return response.extract_string().then(
+                        [](utility::string_t message) -> pplx::task<web::json::value> {
+                            return pplx::task_from_exception<web::json::value>(
+                                std::exception(utility::conversions::to_utf8string(message).c_str()));
+                        });
+                }
+                return response.extract_json();
+        }
+    }
+
+    pplx::task<web::http::http_response> _make_request(web::http::method method, const utility::string_t& path)
+    {
+        web::http::uri_builder bldr;
+        bldr.append(path);
+
+        web::http::http_request req(method);
+        req.set_request_uri(bldr.to_string());
+        return _make_request(req);
+    }
+
+    pplx::task<web::http::http_response> _make_request(web::http::method method,
+                                                       const utility::string_t& path,
+                                                       const web::json::value& data)
+    {
+        web::http::uri_builder bldr;
+        bldr.append(path);
+
+        web::http::http_request req(method);
+        req.set_request_uri(bldr.to_string());
+        req.set_body(data);
+        return _make_request(req);
+    }
+
+    pplx::task<web::http::http_response> _make_request(web::http::method method,
+                                                       const utility::string_t& path,
+                                                       const utility::string_t& destination)
+    {
+        web::http::uri_builder bldr;
+        bldr.append(path);
+
+        web::json::value data;
+        data[U("destination")] = web::json::value::string(destination);
+
+        web::http::http_request req(method);
+        req.set_request_uri(bldr.to_string());
+        req.set_body(data);
+        return _make_request(req);
+    }
+
+    pplx::task<web::http::http_response> _make_request(web::http::http_request req)
+    {
+        if (!m_token.empty()) req.headers().add(U("Authorization"), U("Bearer ") + m_token);
+        return m_client.request(req);
+    }
+
+    Windows::Security::Authentication::OnlineId::OnlineIdAuthenticator ^ m_authenticator;
+    web::http::client::http_client m_client;
+    utility::string_t m_token;
+};
+#endif
+} // namespace live
+} // namespace web
diff --git a/Release/samples/WindowsLiveAuth/pch.cpp b/Release/samples/WindowsLiveAuth/pch.cpp
new file mode 100644 (file)
index 0000000..abe1fd5
--- /dev/null
@@ -0,0 +1,6 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ ****/
+
+#include "pch.h"
diff --git a/Release/samples/WindowsLiveAuth/pch.h b/Release/samples/WindowsLiveAuth/pch.h
new file mode 100644 (file)
index 0000000..9835464
--- /dev/null
@@ -0,0 +1,11 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ ****/
+
+#pragma once
+
+#include "App.xaml.h"
+#include "MainPage.xaml.h"
+#include "live_connect.h"
+#include <collection.h>
\ No newline at end of file
diff --git a/Release/src/.gitignore b/Release/src/.gitignore
new file mode 100644 (file)
index 0000000..211686a
--- /dev/null
@@ -0,0 +1 @@
+*.cmake
diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e15aeb7
--- /dev/null
@@ -0,0 +1,296 @@
+cmake_policy(SET CMP0022 NEW)
+
+file(GLOB HEADERS_CPPREST "../include/cpprest/*.h" "../include/cpprest/*.hpp" "../include/cpprest/*.dat")
+file(GLOB HEADERS_PPLX "../include/pplx/*.h" "../include/pplx/*.hpp")
+file(GLOB HEADERS_DETAILS "../include/cpprest/details/*.h" "../include/cpprest/details/*.hpp" "../include/cpprest/details/*.dat" "../include/pplx/*.hpp" "../include/pplx/*.dat")
+source_group("Header Files\\cpprest" FILES ${HEADERS_CPPREST})
+source_group("Header Files\\pplx" FILES ${HEADERS_PPLX})
+source_group("Header Files\\cpprest\\details" FILES ${HEADERS_DETAILS})
+
+file(GLOB HEADER_PPLX_THREADPOOL "../include/pplx/threadpool.h")
+list(REMOVE_ITEM HEADERS_PPLX ${HEADER_PPLX_THREADPOOL})
+
+set(SOURCES
+  ${HEADERS_CPPREST}
+  ${HEADERS_PPLX}
+  ${HEADERS_DETAILS}
+  pch/stdafx.h
+  http/client/http_client.cpp
+  http/client/http_client_impl.h
+  http/client/http_client_msg.cpp
+  http/common/connection_pool_helpers.h
+  http/common/http_compression.cpp
+  http/common/http_helpers.cpp
+  http/common/http_msg.cpp
+  http/common/internal_http_helpers.h
+  http/listener/http_listener.cpp
+  http/listener/http_listener_msg.cpp
+  http/listener/http_server_api.cpp
+  http/listener/http_server_impl.h
+  http/oauth/oauth1.cpp
+  http/oauth/oauth2.cpp
+  json/json.cpp
+  json/json_parsing.cpp
+  json/json_serialization.cpp
+  uri/uri.cpp
+  uri/uri_builder.cpp
+  utilities/asyncrt_utils.cpp
+  utilities/base64.cpp
+  utilities/web_utilities.cpp
+)
+
+add_library(cpprest ${SOURCES})
+target_include_directories(cpprest
+  PUBLIC
+    $<INSTALL_INTERFACE:include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
+  PRIVATE
+    pch
+)
+
+## Sub-components
+# Websockets component
+if(CPPREST_WEBSOCKETS_IMPL STREQUAL "none")
+  target_compile_definitions(cpprest PUBLIC -DCPPREST_EXCLUDE_WEBSOCKETS=1)
+elseif(CPPREST_WEBSOCKETS_IMPL STREQUAL "winrt")
+  target_sources(cpprest PRIVATE
+    websockets/client/ws_msg.cpp
+    websockets/client/ws_client.cpp
+    websockets/client/ws_client_impl.h
+    websockets/client/ws_client_winrt.cpp
+  )
+elseif(CPPREST_WEBSOCKETS_IMPL STREQUAL "wspp")
+  target_sources(cpprest PRIVATE
+    websockets/client/ws_msg.cpp
+    websockets/client/ws_client.cpp
+    websockets/client/ws_client_impl.h
+    websockets/client/ws_client_wspp.cpp
+  )
+  cpprest_find_websocketpp()
+  target_link_libraries(cpprest PRIVATE cpprestsdk_websocketpp_internal)
+  cpprest_find_boost()
+  cpprest_find_openssl()
+  target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal)
+else()
+  message(FATAL_ERROR "Invalid implementation")
+endif()
+
+# Compression component
+if(CPPREST_EXCLUDE_COMPRESSION)
+  if(NOT CPPREST_EXCLUDE_BROTLI)
+    message(FATAL_ERROR "Use of Brotli requires compression to be enabled")
+  endif()
+  target_compile_definitions(cpprest PRIVATE -DCPPREST_EXCLUDE_COMPRESSION=1)
+else()
+  cpprest_find_zlib()
+  target_link_libraries(cpprest PRIVATE cpprestsdk_zlib_internal)
+  if(CPPREST_EXCLUDE_BROTLI)
+    target_compile_definitions(cpprest PRIVATE -DCPPREST_EXCLUDE_BROTLI=1)
+  else()
+    cpprest_find_brotli()
+  endif()
+endif()
+
+# PPLX component
+if(CPPREST_PPLX_IMPL STREQUAL "apple")
+  find_library(COREFOUNDATION CoreFoundation "/")
+  find_library(SECURITY 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)
+  endif()
+elseif(CPPREST_PPLX_IMPL STREQUAL "linux")
+  target_sources(cpprest PRIVATE pplx/pplxlinux.cpp pplx/pplx.cpp pplx/threadpool.cpp ../include/pplx/threadpool.h)
+  if(CPPREST_INSTALL_HEADERS)
+    install(FILES ../include/pplx/threadpool.h DESTINATION include/pplx)
+  endif()
+elseif(CPPREST_PPLX_IMPL STREQUAL "win")
+  target_sources(cpprest PRIVATE pplx/pplxwin.cpp)
+  if(CPPREST_WEBSOCKETS_IMPL STREQUAL "wspp")
+    target_sources(cpprest PRIVATE pplx/threadpool.cpp ../include/pplx/threadpool.h)
+    if(CPPREST_INSTALL_HEADERS)
+      install(FILES ../include/pplx/threadpool.h DESTINATION include/pplx)
+    endif()
+  endif()
+elseif(CPPREST_PPLX_IMPL STREQUAL "winpplx")
+  target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_PPLX=1)
+  target_sources(cpprest PRIVATE pplx/pplxwin.cpp pplx/pplx.cpp pplx/threadpool.cpp ../include/pplx/threadpool.h)
+  if(CPPREST_INSTALL_HEADERS)
+    install(FILES ../include/pplx/threadpool.h DESTINATION include/pplx)
+  endif()
+elseif(CPPREST_PPLX_IMPL STREQUAL "winrt")
+  target_sources(cpprest PRIVATE pplx/pplxwin.cpp)
+else()
+  message(FATAL_ERROR "Invalid implementation")
+endif()
+
+# Http client component
+if(CPPREST_HTTP_CLIENT_IMPL STREQUAL "asio")
+  cpprest_find_boost()
+  cpprest_find_openssl()
+  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
+    Winhttp.lib
+  )
+  target_sources(cpprest PRIVATE http/client/http_client_winhttp.cpp)
+  if(CPPREST_WEBSOCKETS_IMPL STREQUAL "wspp")
+    target_sources(cpprest PRIVATE http/client/x509_cert_utilities.cpp)
+  endif()
+elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winrt")
+  target_sources(cpprest PRIVATE http/client/http_client_winrt.cpp)
+else()
+  message(FATAL_ERROR "Invalid implementation")
+endif()
+
+# fileio streams component
+if(CPPREST_FILEIO_IMPL STREQUAL "win32")
+  target_sources(cpprest PRIVATE streams/fileio_win32.cpp)
+elseif(CPPREST_FILEIO_IMPL STREQUAL "winrt")
+  target_sources(cpprest PRIVATE streams/fileio_winrt.cpp)
+elseif(CPPREST_FILEIO_IMPL STREQUAL "posix")
+  target_sources(cpprest PRIVATE streams/fileio_posix.cpp)
+else()
+  message(FATAL_ERROR "Invalid implementation")
+endif()
+
+# http listener component
+if(CPPREST_HTTP_LISTENER_IMPL STREQUAL "asio")
+  cpprest_find_boost()
+  cpprest_find_openssl()
+  target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_LISTENER_ASIO)
+  target_sources(cpprest PRIVATE http/listener/http_server_asio.cpp)
+  target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal)
+elseif(CPPREST_HTTP_LISTENER_IMPL STREQUAL "httpsys")
+  target_sources(cpprest PRIVATE
+    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()
+
+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)
+    target_compile_options(cpprest PRIVATE -Werror)
+  endif()
+  target_compile_options(cpprest PRIVATE -pedantic ${WARNINGS})
+elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
+  if(WERROR)
+    target_compile_options(cpprest PRIVATE /WX ${WARNINGS})
+  endif()
+else()
+  message(FATAL_ERROR "Unknown compiler")
+endif()
+
+if(WIN32)
+  if (BUILD_SHARED_LIBS)
+    target_compile_definitions(cpprest PRIVATE -D_ASYNCRT_EXPORT -D_PPLX_EXPORT -D_USRDLL)
+  else()
+    target_compile_definitions(cpprest PUBLIC -D_NO_ASYNCRTIMP -D_NO_PPLXIMP)
+  endif()
+elseif(ANDROID)
+  target_link_libraries(cpprest PRIVATE ${ANDROID_STL_FLAGS})
+endif()
+
+if (WIN32 AND NOT WINDOWS_STORE AND NOT WINDOWS_PHONE)
+  target_link_libraries(cpprest PRIVATE
+    bcrypt.lib
+    crypt32.lib
+  )
+elseif(WINDOWS_STORE)
+  if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*")
+    target_compile_definitions(cpprest PRIVATE -DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP)
+    get_target_property(LINK_FLAGS cpprest LINK_FLAGS)
+    if(NOT LINK_FLAGS)
+        set(LINK_FLAGS "")
+    endif()
+    set(LINK_FLAGS "${LINK_FLAGS} /APPCONTAINER")
+    set_target_properties(cpprest PROPERTIES LINK_FLAGS "${LINK_FLAGS}")
+  endif()
+endif()
+
+set_target_properties(cpprest PROPERTIES OUTPUT_NAME "cpprest${CPPREST_ABI_TAG}")
+if(WIN32)
+elseif(ANDROID)
+  # Do not use SOVERSION on android. It is completely unsupported (and causes problems).
+  # Perhaps revisit in the future? (NDK r9d, 8/7/14)
+else()
+  set_target_properties(cpprest PROPERTIES
+    SOVERSION ${CPPREST_VERSION_MAJOR}.${CPPREST_VERSION_MINOR})
+endif()
+
+if(CPPREST_INSTALL_HEADERS)
+  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)
+  set(CPPREST_USES_BOOST OFF)
+  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)
+    list(APPEND CPPREST_TARGETS cpprestsdk_boost_internal)
+    set(CPPREST_USES_BOOST ON)
+  endif()
+  if(TARGET cpprestsdk_zlib_internal)
+    list(APPEND CPPREST_TARGETS cpprestsdk_zlib_internal)
+    set(CPPREST_USES_ZLIB ON)
+  endif()
+  if(TARGET cpprestsdk_brotli_internal)
+    list(APPEND CPPREST_TARGETS cpprestsdk_brotli_internal)
+    set(CPPREST_USES_BROTLI ON)
+  endif()
+  if(TARGET cpprestsdk_openssl_internal)
+    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()
+  install(
+    TARGETS ${CPPREST_TARGETS}
+    EXPORT cpprestsdk-targets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+
+  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" "${CMAKE_CURRENT_BINARY_DIR}/cpprestsdk-config-version.cmake"
+    DESTINATION ${CMAKE_INSTALL_LIBDIR}/${CPPREST_EXPORT_DIR}
+  )
+  install(
+    EXPORT cpprestsdk-targets
+    FILE cpprestsdk-targets.cmake
+    NAMESPACE cpprestsdk::
+    DESTINATION ${CMAKE_INSTALL_LIBDIR}/${CPPREST_EXPORT_DIR}
+  )
+endif()
diff --git a/Release/src/http/client/http_client.cpp b/Release/src/http/client/http_client.cpp
new file mode 100644 (file)
index 0000000..09e3eed
--- /dev/null
@@ -0,0 +1,426 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Client-side APIs.
+ *
+ * This file contains shared code across all http_client implementations.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "http_client_impl.h"
+
+namespace web
+{
+namespace http
+{
+namespace client
+{
+// Helper function to check to make sure the uri is valid.
+static void verify_uri(const uri& uri)
+{
+    // Some things like proper URI schema are verified by the URI class.
+    // We only need to check certain things specific to HTTP.
+    if (uri.scheme() != _XPLATSTR("http") && uri.scheme() != _XPLATSTR("https"))
+    {
+        throw std::invalid_argument("URI scheme must be 'http' or 'https'");
+    }
+
+    if (uri.host().empty())
+    {
+        throw std::invalid_argument("URI must contain a hostname.");
+    }
+}
+
+namespace details
+{
+#if defined(_WIN32) || 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
+
+void request_context::complete_headers()
+{
+    // We have already read (and transmitted) the request body. Should we explicitly close the stream?
+    // Well, there are test cases that assumes that the istream is valid when t receives the response!
+    // For now, we will drop our reference which will close the stream if the user doesn't have one.
+    m_request.set_body(Concurrency::streams::istream());
+    m_request_completion.set(m_response);
+}
+
+void request_context::complete_request(utility::size64_t body_size)
+{
+    m_response._get_impl()->_complete(body_size);
+
+    finish();
+}
+
+void request_context::report_error(unsigned long error_code, const std::string& errorMessage)
+{
+    report_exception(http_exception(static_cast<int>(error_code), errorMessage));
+}
+
+#if defined(_WIN32)
+void request_context::report_error(unsigned long error_code, const std::wstring& errorMessage)
+{
+    report_exception(http_exception(static_cast<int>(error_code), errorMessage));
+}
+#endif
+
+void request_context::report_exception(std::exception_ptr exceptionPtr)
+{
+    auto response_impl = m_response._get_impl();
+
+    // If cancellation has been triggered then ignore any errors.
+    if (m_request._cancellation_token().is_canceled())
+    {
+        exceptionPtr =
+            std::make_exception_ptr(http_exception((int)std::errc::operation_canceled, std::generic_category()));
+    }
+
+    // First try to complete the headers with an exception.
+    if (m_request_completion.set_exception(exceptionPtr))
+    {
+        // Complete the request with no msg body. The exception
+        // should only be propagated to one of the tce.
+        response_impl->_complete(0);
+    }
+    else
+    {
+        // Complete the request with an exception
+        response_impl->_complete(0, exceptionPtr);
+    }
+
+    finish();
+}
+
+bool request_context::handle_compression()
+{
+    // If the response body is compressed we will read the encoding header and create a decompressor object which will
+    // later decompress the body
+    try
+    {
+        utility::string_t encoding;
+        http_headers& headers = m_response.headers();
+
+        // Note that some headers, for example "Transfer-Encoding: chunked", may legitimately not produce a decompressor
+        if (m_http_client->client_config().request_compressed_response() &&
+            headers.match(web::http::header_names::content_encoding, encoding))
+        {
+            // Note that, while Transfer-Encoding (chunked only) is valid with Content-Encoding,
+            // we don't need to look for it here because winhttp de-chunks for us in that case
+            m_decompressor = compression::details::get_decompressor_from_header(
+                encoding, compression::details::header_types::content_encoding, m_request.decompress_factories());
+        }
+        else if (!m_request.decompress_factories().empty() &&
+                 headers.match(web::http::header_names::transfer_encoding, encoding))
+        {
+            m_decompressor = compression::details::get_decompressor_from_header(
+                encoding, compression::details::header_types::transfer_encoding, m_request.decompress_factories());
+        }
+    }
+    catch (...)
+    {
+        report_exception(std::current_exception());
+        return false;
+    }
+
+    return true;
+}
+
+utility::string_t request_context::get_compression_header() const
+{
+    utility::string_t headers;
+
+    // Add the correct header needed to request a compressed response if supported
+    // on this platform and it has been specified in the config and/or request
+    if (m_http_client->client_config().request_compressed_response())
+    {
+        if (!m_request.decompress_factories().empty() || web::http::compression::builtin::supported())
+        {
+            // Accept-Encoding -- request Content-Encoding from the server
+            headers.append(header_names::accept_encoding + U(": "));
+            headers.append(compression::details::build_supported_header(
+                compression::details::header_types::accept_encoding, m_request.decompress_factories()));
+            headers.append(U("\r\n"));
+        }
+    }
+    else if (!m_request.decompress_factories().empty())
+    {
+        // TE -- request Transfer-Encoding from the server
+        headers.append(header_names::connection + U(": TE\r\n") + // Required by Section 4.3 of RFC-7230
+                       header_names::te + U(": "));
+        headers.append(compression::details::build_supported_header(compression::details::header_types::te,
+                                                                    m_request.decompress_factories()));
+        headers.append(U("\r\n"));
+    }
+
+    return headers;
+}
+
+concurrency::streams::streambuf<uint8_t> request_context::_get_readbuffer()
+{
+    auto instream = m_request.body();
+
+    _ASSERTE((bool)instream);
+    return instream.streambuf();
+}
+
+concurrency::streams::streambuf<uint8_t> request_context::_get_writebuffer()
+{
+    auto outstream = m_response._get_impl()->outstream();
+
+    _ASSERTE((bool)outstream);
+    return outstream.streambuf();
+}
+
+request_context::request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request)
+    : m_http_client(client), m_request(request), m_uploaded(0), m_downloaded(0)
+{
+    auto responseImpl = m_response._get_impl();
+
+    // Copy the user specified output stream over to the response
+    responseImpl->set_outstream(request._get_impl()->_response_stream(), false);
+
+    // Prepare for receiving data from the network. Ideally, this should be done after
+    // we receive the headers and determine that there is a response body. We will do it here
+    // since it is not immediately apparent where that would be in the callback handler
+    responseImpl->_prepare_to_receive_data();
+}
+
+void _http_client_communicator::async_send_request_impl(const std::shared_ptr<request_context>& request)
+{
+    auto self = std::static_pointer_cast<_http_client_communicator>(this->shared_from_this());
+    // Schedule a task to start sending.
+    pplx::create_task([self, request] {
+        try
+        {
+            self->send_request(request);
+        }
+        catch (...)
+        {
+            request->report_exception(std::current_exception());
+        }
+    });
+}
+
+void _http_client_communicator::async_send_request(const std::shared_ptr<request_context>& request)
+{
+    if (m_client_config.guarantee_order())
+    {
+        pplx::extensibility::scoped_critical_section_t l(m_client_lock);
+
+        if (m_outstanding)
+        {
+            m_requests_queue.push(request);
+        }
+        else
+        {
+            async_send_request_impl(request);
+            m_outstanding = true;
+        }
+    }
+    else
+    {
+        async_send_request_impl(request);
+    }
+}
+
+void _http_client_communicator::finish_request()
+{
+    // If guarantee order is specified we don't need to do anything.
+    if (m_client_config.guarantee_order())
+    {
+        pplx::extensibility::scoped_critical_section_t l(m_client_lock);
+
+        if (m_requests_queue.empty())
+        {
+            m_outstanding = false;
+        }
+        else
+        {
+            auto request = m_requests_queue.front();
+            m_requests_queue.pop();
+
+            async_send_request_impl(request);
+        }
+    }
+}
+
+const http_client_config& _http_client_communicator::client_config() const { return m_client_config; }
+
+const uri& _http_client_communicator::base_uri() const { return m_uri; }
+
+_http_client_communicator::_http_client_communicator(http::uri&& address, http_client_config&& client_config)
+    : m_uri(std::move(address)), m_client_config(std::move(client_config)), m_outstanding(false)
+{
+}
+
+inline void request_context::finish()
+{
+    // If cancellation is enabled and registration was performed, unregister.
+    if (m_cancellationRegistration != pplx::cancellation_token_registration())
+    {
+        _ASSERTE(m_request._cancellation_token() != pplx::cancellation_token::none());
+        m_request._cancellation_token().deregister_callback(m_cancellationRegistration);
+    }
+
+    m_http_client->finish_request();
+}
+
+} // namespace details
+
+/// <summary>
+/// Private implementation of http_client. Manages the http request processing pipeline.
+/// </summary>
+class http_pipeline
+{
+public:
+    http_pipeline(std::shared_ptr<details::_http_client_communicator> last) : m_last_stage(std::move(last)) {}
+
+    // pplx::extensibility::recursive_lock_t does not support move/copy, but does not delete the functions either.
+    http_pipeline(const http_pipeline&) = delete;
+    http_pipeline(http_pipeline&&) = delete;
+    http_pipeline& operator=(const http_pipeline&) = delete;
+    http_pipeline& operator=(http_pipeline&&) = delete;
+
+    /// <summary>
+    /// Initiate an http request into the pipeline
+    /// </summary>
+    /// <param name="request">Http request</param>
+    pplx::task<http_response> propagate(http_request request)
+    {
+        std::shared_ptr<http_pipeline_stage> first;
+        {
+            pplx::extensibility::scoped_recursive_lock_t l(m_lock);
+            first = (m_stages.size() > 0) ? m_stages[0] : m_last_stage;
+        }
+        return first->propagate(request);
+    }
+
+    /// <summary>
+    /// Adds an HTTP pipeline stage to the pipeline.
+    /// </summary>
+    /// <param name="stage">A pipeline stage.</param>
+    void append(const std::shared_ptr<http_pipeline_stage>& stage)
+    {
+        pplx::extensibility::scoped_recursive_lock_t l(m_lock);
+
+        if (m_stages.size() > 0)
+        {
+            std::shared_ptr<http_pipeline_stage> penultimate = m_stages[m_stages.size() - 1];
+            penultimate->set_next_stage(stage);
+        }
+        stage->set_next_stage(m_last_stage);
+
+        m_stages.push_back(stage);
+    }
+
+    // The last stage is always set up by the client or listener and cannot
+    // be changed. All application-defined stages are executed before the
+    // last stage, which is typically a send or dispatch.
+    const std::shared_ptr<details::_http_client_communicator> m_last_stage;
+
+private:
+    // The vector of pipeline stages.
+    std::vector<std::shared_ptr<http_pipeline_stage>> m_stages;
+
+    pplx::extensibility::recursive_lock_t m_lock;
+};
+
+void http_client::add_handler(
+    const std::function<pplx::task<http_response> __cdecl(http_request, std::shared_ptr<http::http_pipeline_stage>)>&
+        handler)
+{
+    class function_pipeline_wrapper : public http::http_pipeline_stage
+    {
+    public:
+        function_pipeline_wrapper(const std::function<pplx::task<http_response> __cdecl(
+                                      http_request, std::shared_ptr<http::http_pipeline_stage>)>& handler)
+            : m_handler(handler)
+        {
+        }
+
+        virtual pplx::task<http_response> propagate(http_request request) override
+        {
+            return m_handler(std::move(request), next_stage());
+        }
+
+    private:
+        std::function<pplx::task<http_response>(http_request, std::shared_ptr<http::http_pipeline_stage>)> m_handler;
+    };
+
+    m_pipeline->append(std::make_shared<function_pipeline_wrapper>(handler));
+}
+
+void http_client::add_handler(const std::shared_ptr<http::http_pipeline_stage>& stage) { m_pipeline->append(stage); }
+
+http_client::http_client(const uri& base_uri) : http_client(base_uri, http_client_config()) {}
+
+http_client::http_client(const uri& base_uri, const http_client_config& client_config)
+{
+    std::shared_ptr<details::_http_client_communicator> final_pipeline_stage;
+
+    if (base_uri.scheme().empty())
+    {
+        auto uribuilder = uri_builder(base_uri);
+        uribuilder.set_scheme(_XPLATSTR("http"));
+        uri uriWithScheme = uribuilder.to_uri();
+        verify_uri(uriWithScheme);
+        final_pipeline_stage =
+            details::create_platform_final_pipeline_stage(std::move(uriWithScheme), http_client_config(client_config));
+    }
+    else
+    {
+        verify_uri(base_uri);
+        final_pipeline_stage =
+            details::create_platform_final_pipeline_stage(uri(base_uri), http_client_config(client_config));
+    }
+
+    m_pipeline = std::make_shared<http_pipeline>(std::move(final_pipeline_stage));
+
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+    add_handler(std::static_pointer_cast<http::http_pipeline_stage>(
+        std::make_shared<oauth1::details::oauth1_handler>(client_config.oauth1())));
+#endif
+
+    add_handler(std::static_pointer_cast<http::http_pipeline_stage>(
+        std::make_shared<oauth2::details::oauth2_handler>(client_config.oauth2())));
+}
+
+http_client::~http_client() CPPREST_NOEXCEPT {}
+
+const http_client_config& http_client::client_config() const { return m_pipeline->m_last_stage->client_config(); }
+
+const uri& http_client::base_uri() const { return m_pipeline->m_last_stage->base_uri(); }
+
+// Macros to help build string at compile time and avoid overhead.
+#define STRINGIFY(x) _XPLATSTR(#x)
+#define TOSTRING(x) STRINGIFY(x)
+#define USERAGENT                                                                                                      \
+    _XPLATSTR("cpprestsdk/")                                                                                           \
+    TOSTRING(CPPREST_VERSION_MAJOR)                                                                                    \
+    _XPLATSTR(".") TOSTRING(CPPREST_VERSION_MINOR) _XPLATSTR(".") TOSTRING(CPPREST_VERSION_REVISION)
+
+pplx::task<http_response> http_client::request(http_request request, const pplx::cancellation_token& token)
+{
+    if (!request.headers().has(header_names::user_agent))
+    {
+        request.headers().add(header_names::user_agent, USERAGENT);
+    }
+
+    request._set_base_uri(base_uri());
+    request._set_cancellation_token(token);
+    return m_pipeline->propagate(request);
+}
+
+} // namespace client
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/client/http_client_asio.cpp b/Release/src/http/client/http_client_asio.cpp
new file mode 100644 (file)
index 0000000..07bb488
--- /dev/null
@@ -0,0 +1,2162 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Client-side APIs.
+ *
+ * This file contains a cross platform implementation based on Boost.ASIO.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "../common/connection_pool_helpers.h"
+#include "../common/internal_http_helpers.h"
+#include "cpprest/asyncrt_utils.h"
+#include <sstream>
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+#endif
+#include <boost/algorithm/string.hpp>
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+#include <boost/asio/ssl/error.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/bind.hpp>
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#if defined(BOOST_NO_CXX11_SMART_PTR)
+#error "Cpp rest SDK requires c++11 smart pointer support from boost"
+#endif
+
+#include "../common/x509_cert_utilities.h"
+#include "cpprest/base_uri.h"
+#include "cpprest/details/http_helpers.h"
+#include "http_client_impl.h"
+#include "pplx/threadpool.h"
+#include <memory>
+#include <unordered_set>
+
+#if defined(__GNUC__) && !defined(__clang__)
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+#define AND_CAPTURE_MEMBER_FUNCTION_POINTERS
+#else
+// GCC Bug 56222 - Pointer to member in lambda should not require this to be captured
+// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56222
+// GCC Bug 51494 - Legal program rejection - capturing "this" when using static method inside lambda
+// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51494
+#define AND_CAPTURE_MEMBER_FUNCTION_POINTERS , this
+#endif
+
+#elif defined(_MSC_VER)
+
+#if _MSC_VER >= 1900
+#define AND_CAPTURE_MEMBER_FUNCTION_POINTERS
+#else
+// This bug also afflicts VS2013 which incorrectly reports "warning C4573: the usage of 'symbol' requires the compiler
+// to capture 'this' but the current default capture mode does not allow it"
+#define AND_CAPTURE_MEMBER_FUNCTION_POINTERS , this
+#endif
+
+#else
+
+#define AND_CAPTURE_MEMBER_FUNCTION_POINTERS
+
+#endif
+
+using boost::asio::ip::tcp;
+
+#ifdef __ANDROID__
+using utility::conversions::details::to_string;
+#else
+using std::to_string;
+#endif
+
+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 result;
+    if (baseUri.scheme() == U("https"))
+    {
+        const utility::string_t* encResult;
+        const auto hostHeader = requestHeaders.find(_XPLATSTR("Host"));
+        if (hostHeader == requestHeaders.end())
+        {
+            encResult = &baseUri.host();
+        }
+        else
+        {
+            encResult = &hostHeader->second;
+        }
+
+        result = utility::conversions::to_utf8string(*encResult);
+        utility::details::inplace_tolower(result);
+    }
+
+    return result;
+}
+} // namespace
+
+namespace web
+{
+namespace http
+{
+namespace client
+{
+namespace details
+{
+enum class httpclient_errorcode_context
+{
+    none = 0,
+    connect,
+    handshake,
+    writeheader,
+    writebody,
+    readheader,
+    readbody,
+    close
+};
+
+static std::string generate_base64_userpass(const ::web::credentials& creds)
+{
+    auto userpass = creds.username() + U(":") + *creds._internal_decrypt();
+    auto&& u8_userpass = utility::conversions::to_utf8string(userpass);
+    std::vector<unsigned char> credentials_buffer(u8_userpass.begin(), u8_userpass.end());
+    return utility::conversions::to_utf8string(utility::conversions::to_base64(credentials_buffer));
+}
+
+class asio_connection_pool;
+
+class asio_connection
+{
+    friend class asio_client;
+
+public:
+    asio_connection(boost::asio::io_service& io_service)
+        : m_socket_lock()
+        , m_socket(io_service)
+        , m_ssl_stream()
+        , m_cn_hostname()
+        , m_is_reused(false)
+        , m_keep_alive(true)
+        , m_closed(false)
+    {
+    }
+
+    ~asio_connection() { close(); }
+
+    // This simply instantiates the internal state to support ssl. It does not perform the handshake.
+    void upgrade_to_ssl(std::string&& cn_hostname,
+                        const std::function<void(boost::asio::ssl::context&)>& ssl_context_callback)
+    {
+        std::lock_guard<std::mutex> lock(m_socket_lock);
+        assert(!is_ssl());
+        boost::asio::ssl::context ssl_context(boost::asio::ssl::context::sslv23);
+        ssl_context.set_default_verify_paths();
+        ssl_context.set_options(boost::asio::ssl::context::default_workarounds);
+        if (ssl_context_callback)
+        {
+            ssl_context_callback(ssl_context);
+        }
+        m_ssl_stream = utility::details::make_unique<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&>>(
+            m_socket, ssl_context);
+        m_cn_hostname = std::move(cn_hostname);
+    }
+
+    void close()
+    {
+        std::lock_guard<std::mutex> lock(m_socket_lock);
+
+        // Ensures closed connections owned by request_context will not be put to pool when they are released.
+        m_keep_alive = false;
+        m_closed = true;
+
+        boost::system::error_code error;
+        m_socket.shutdown(tcp::socket::shutdown_both, error);
+        m_socket.close(error);
+    }
+
+    boost::system::error_code cancel()
+    {
+        std::lock_guard<std::mutex> lock(m_socket_lock);
+        boost::system::error_code error;
+        m_socket.cancel(error);
+        return error;
+    }
+
+    bool is_reused() const { return m_is_reused; }
+    void set_keep_alive(bool keep_alive) { m_keep_alive = keep_alive; }
+    bool keep_alive() const { return m_keep_alive; }
+    bool is_ssl() const { return m_ssl_stream ? true : false; }
+    const std::string& cn_hostname() const { return m_cn_hostname; }
+
+    // Check if the error code indicates that the connection was closed by the
+    // server: this is used to detect if a connection in the pool was closed during
+    // its period of inactivity and we should reopen it.
+    bool was_reused_and_closed_by_server(const boost::system::error_code& ec) const
+    {
+        if (!is_reused())
+        {
+            // Don't bother reopening the connection if it's a new one: in this
+            // case, even if the connection was really lost, it's still a real
+            // error and we shouldn't try to reopen it.
+            return false;
+        }
+
+        // These errors tell if connection was closed.
+        if ((boost::asio::error::eof == ec) || (boost::asio::error::connection_reset == ec) ||
+            (boost::asio::error::connection_aborted == ec))
+        {
+            return true;
+        }
+
+        if (is_ssl())
+        {
+            // For SSL connections, we can also get a different error due to
+            // incorrect secure connection shutdown if it was closed by the
+            // server due to inactivity. Unfortunately, the exact error we get
+            // in this case depends on the Boost.Asio version used.
+#if BOOST_ASIO_VERSION >= 101008
+            if (boost::asio::ssl::error::stream_truncated == ec) return true;
+#else // Asio < 1.10.8 didn't have ssl::error::stream_truncated
+            if (boost::system::error_code(ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ),
+                                          boost::asio::error::get_ssl_category()) == ec)
+                return true;
+#endif
+        }
+
+        return false;
+    }
+
+    template<typename Iterator, typename Handler>
+    void async_connect(const Iterator& begin, const Handler& handler)
+    {
+        {
+            std::lock_guard<std::mutex> lock(m_socket_lock);
+            if (!m_closed)
+            {
+                m_socket.async_connect(begin, handler);
+                return;
+            }
+        } // unlock
+
+        handler(boost::asio::error::operation_aborted);
+    }
+
+    template<typename HandshakeHandler, typename CertificateHandler>
+    void async_handshake(boost::asio::ssl::stream_base::handshake_type type,
+                         const http_client_config& config,
+                         const HandshakeHandler& handshake_handler,
+                         const CertificateHandler& cert_handler)
+    {
+        std::lock_guard<std::mutex> lock(m_socket_lock);
+        assert(is_ssl());
+
+        // Check to turn on/off server certificate verification.
+        if (config.validate_certificates())
+        {
+            m_ssl_stream->set_verify_mode(boost::asio::ssl::context::verify_peer);
+            m_ssl_stream->set_verify_callback(cert_handler);
+        }
+        else
+        {
+            m_ssl_stream->set_verify_mode(boost::asio::ssl::context::verify_none);
+        }
+
+        // Check to set host name for Server Name Indication (SNI)
+        if (config.is_tlsext_sni_enabled())
+        {
+            SSL_set_tlsext_host_name(m_ssl_stream->native_handle(), &m_cn_hostname[0]);
+        }
+
+        m_ssl_stream->async_handshake(type, handshake_handler);
+    }
+
+    template<typename ConstBufferSequence, typename Handler>
+    void async_write(ConstBufferSequence& buffer, const Handler& writeHandler)
+    {
+        std::lock_guard<std::mutex> lock(m_socket_lock);
+        if (m_ssl_stream)
+        {
+            boost::asio::async_write(*m_ssl_stream, buffer, writeHandler);
+        }
+        else
+        {
+            boost::asio::async_write(m_socket, buffer, writeHandler);
+        }
+    }
+
+    template<typename MutableBufferSequence, typename CompletionCondition, typename Handler>
+    void async_read(MutableBufferSequence& buffer, const CompletionCondition& condition, const Handler& readHandler)
+    {
+        std::lock_guard<std::mutex> lock(m_socket_lock);
+        if (m_ssl_stream)
+        {
+            boost::asio::async_read(*m_ssl_stream, buffer, condition, readHandler);
+        }
+        else
+        {
+            boost::asio::async_read(m_socket, buffer, condition, readHandler);
+        }
+    }
+
+    template<typename Handler>
+    void async_read_until(boost::asio::streambuf& buffer, const std::string& delim, const Handler& readHandler)
+    {
+        std::lock_guard<std::mutex> lock(m_socket_lock);
+        if (m_ssl_stream)
+        {
+            boost::asio::async_read_until(*m_ssl_stream, buffer, delim, readHandler);
+        }
+        else
+        {
+            boost::asio::async_read_until(m_socket, buffer, delim, readHandler);
+        }
+    }
+
+    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
+    // as normal message processing.
+    std::mutex m_socket_lock;
+    tcp::socket m_socket;
+    std::unique_ptr<boost::asio::ssl::stream<tcp::socket&>> m_ssl_stream;
+    std::string m_cn_hostname;
+
+    bool m_is_reused;
+    bool m_keep_alive;
+    bool m_closed;
+};
+
+/// <summary>Implements a connection pool with adaptive connection removal</summary>
+/// <remarks>
+/// Every 30 seconds, the lambda in `start_epoch_interval` fires, triggering the
+/// cleanup of any connections that have resided in the pool since the last
+/// cleanup phase.
+///
+/// During the cleanup phase, connections are removed starting with the oldest. This
+/// ensures that if a high intensity workload is followed by a low intensity workload,
+/// the connection pool will correctly adapt to the low intensity workload.
+///
+/// Specifically, the following code will eventually result in a maximum of one pooled
+/// connection regardless of the initial number of pooled connections:
+/// <code>
+///   while(1)
+///   {
+///     auto conn = pool.try_acquire();
+///     if (!conn) conn = new_conn();
+///     pool.release(std::move(conn));
+///   }
+/// </code>
+/// </remarks>
+class asio_connection_pool final : public std::enable_shared_from_this<asio_connection_pool>
+{
+public:
+    asio_connection_pool()
+        : m_lock()
+        , m_connections()
+        , m_is_timer_running(false)
+        , m_pool_epoch_timer(crossplat::threadpool::shared_instance().service())
+    {
+    }
+
+    asio_connection_pool(const asio_connection_pool&) = delete;
+    asio_connection_pool& operator=(const asio_connection_pool&) = delete;
+
+    std::shared_ptr<asio_connection> try_acquire(const std::string& cn_hostname)
+    {
+        std::lock_guard<std::mutex> lock(m_lock);
+        if (m_connections.empty())
+        {
+            return nullptr;
+        }
+
+        auto conn = m_connections[cn_hostname].try_acquire();
+        if (conn)
+        {
+            conn->start_reuse();
+        }
+
+        return conn;
+    }
+
+    void release(std::shared_ptr<asio_connection>&& connection)
+    {
+        connection->cancel();
+        if (!connection->keep_alive())
+        {
+            connection.reset();
+            return;
+        }
+
+        std::lock_guard<std::mutex> lock(m_lock);
+        if (!m_is_timer_running)
+        {
+            start_epoch_interval(shared_from_this());
+            m_is_timer_running = true;
+        }
+
+        m_connections[connection->cn_hostname()].release(std::move(connection));
+    }
+
+private:
+    // Note: must be called under m_lock
+    static void start_epoch_interval(const std::shared_ptr<asio_connection_pool>& pool)
+    {
+        auto& self = *pool;
+        std::weak_ptr<asio_connection_pool> weak_pool = pool;
+
+        self.m_pool_epoch_timer.expires_from_now(boost::posix_time::seconds(30));
+        self.m_pool_epoch_timer.async_wait([weak_pool](const boost::system::error_code& ec) {
+            if (ec)
+            {
+                return;
+            }
+
+            auto pool = weak_pool.lock();
+            if (!pool)
+            {
+                return;
+            }
+
+            auto& self = *pool;
+            std::lock_guard<std::mutex> lock(self.m_lock);
+            bool restartTimer = false;
+            for (auto& entry : self.m_connections)
+            {
+                if (entry.second.free_stale_connections())
+                {
+                    restartTimer = true;
+                }
+            }
+
+            if (restartTimer)
+            {
+                start_epoch_interval(pool);
+            }
+            else
+            {
+                self.m_is_timer_running = false;
+            }
+        });
+    }
+
+    std::mutex m_lock;
+    std::map<std::string, connection_pool_stack<asio_connection>> m_connections;
+    bool m_is_timer_running;
+    boost::asio::deadline_timer m_pool_epoch_timer;
+};
+
+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_pool(std::make_shared<asio_connection_pool>())
+    {
+    }
+
+    virtual void send_request(const std::shared_ptr<request_context>& request_ctx) override;
+
+    void release_connection(std::shared_ptr<asio_connection>&& conn) { m_pool->release(std::move(conn)); }
+
+    std::shared_ptr<asio_connection> obtain_connection(const http_request& req)
+    {
+        std::string cn_host = calc_cn_host(base_uri(), req.headers());
+        std::shared_ptr<asio_connection> conn = m_pool->try_acquire(cn_host);
+        if (conn == nullptr)
+        {
+            // Pool was empty. Create a new connection
+            conn = std::make_shared<asio_connection>(crossplat::threadpool::shared_instance().service());
+            if (base_uri().scheme() == U("https") && !this->client_config().proxy().is_specified())
+            {
+                conn->upgrade_to_ssl(std::move(cn_host), this->client_config().get_ssl_context_callback());
+            }
+        }
+
+        return conn;
+    }
+
+    virtual pplx::task<http_response> propagate(http_request request) override;
+
+private:
+    const std::shared_ptr<asio_connection_pool> m_pool;
+};
+
+class asio_context final : public request_context, public std::enable_shared_from_this<asio_context>
+{
+    friend class asio_client;
+
+public:
+    asio_context(const std::shared_ptr<_http_client_communicator>& client,
+                 http_request& request,
+                 const std::shared_ptr<asio_connection>& connection)
+        : request_context(client, request)
+        , m_content_length(0)
+        , m_needChunked(false)
+        , m_timer(client->client_config().timeout<std::chrono::microseconds>())
+        , m_resolver(crossplat::threadpool::shared_instance().service())
+        , m_connection(connection)
+#ifdef CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+        , m_openssl_failed(false)
+#endif // CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+    {
+    }
+
+    virtual ~asio_context()
+    {
+        m_timer.stop();
+        // Release connection back to the pool. If connection was not closed, it will be put to the pool for reuse.
+        std::static_pointer_cast<asio_client>(m_http_client)->release_connection(std::move(m_connection));
+    }
+
+    static std::shared_ptr<request_context> create_request_context(std::shared_ptr<_http_client_communicator>& client,
+                                                                   http_request& request)
+    {
+        auto client_cast(std::static_pointer_cast<asio_client>(client));
+        auto connection(client_cast->obtain_connection(request));
+        auto ctx = std::make_shared<asio_context>(client, request, connection);
+        ctx->m_timer.set_ctx(std::weak_ptr<asio_context>(ctx));
+        return ctx;
+    }
+
+    class ssl_proxy_tunnel final : public std::enable_shared_from_this<ssl_proxy_tunnel>
+    {
+    public:
+        ssl_proxy_tunnel(std::shared_ptr<asio_context> context,
+                         std::function<void(std::shared_ptr<asio_context>)> ssl_tunnel_established)
+            : m_ssl_tunnel_established(ssl_tunnel_established), m_context(context)
+        {
+        }
+
+        void start_proxy_connect()
+        {
+            auto proxy = m_context->m_http_client->client_config().proxy();
+            auto proxy_uri = proxy.address();
+
+            utility::string_t proxy_host = proxy_uri.host();
+            int proxy_port = proxy_uri.port() == -1 ? 8080 : proxy_uri.port();
+
+            const auto& base_uri = m_context->m_http_client->base_uri();
+            const auto& host = utility::conversions::to_utf8string(base_uri.host());
+            const int portRaw = base_uri.port();
+            const int port = (portRaw != 0) ? portRaw : 443;
+
+            std::ostream request_stream(&m_request);
+            request_stream.imbue(std::locale::classic());
+
+            request_stream << "CONNECT " << host << ":" << port << " HTTP/1.1\r\n";
+            request_stream << "Host: " << host << ":" << port << CRLF;
+            request_stream << "Proxy-Connection: Keep-Alive\r\n";
+
+            if (m_context->m_http_client->client_config().proxy().credentials().is_set())
+            {
+                request_stream << m_context->generate_basic_proxy_auth_header();
+            }
+
+            request_stream << CRLF;
+
+            m_context->m_timer.start();
+
+            tcp::resolver::query query(utility::conversions::to_utf8string(proxy_host), to_string(proxy_port));
+
+            auto client = std::static_pointer_cast<asio_client>(m_context->m_http_client);
+            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:
+        void handle_resolve(const boost::system::error_code& ec, tcp::resolver::iterator endpoints)
+        {
+            if (ec)
+            {
+                m_context->report_error("Error resolving proxy address", ec, httpclient_errorcode_context::connect);
+            }
+            else
+            {
+                m_context->m_timer.reset();
+                auto endpoint = *endpoints;
+                m_context->m_connection->async_connect(endpoint,
+                                                       boost::bind(&ssl_proxy_tunnel::handle_tcp_connect,
+                                                                   shared_from_this(),
+                                                                   boost::asio::placeholders::error,
+                                                                   ++endpoints));
+            }
+        }
+
+        void handle_tcp_connect(const boost::system::error_code& ec, tcp::resolver::iterator endpoints)
+        {
+            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(),
+                                                                 boost::asio::placeholders::error));
+            }
+            else if (endpoints == tcp::resolver::iterator())
+            {
+                m_context->report_error(
+                    "Failed to connect to any resolved proxy endpoint", ec, httpclient_errorcode_context::connect);
+            }
+            else
+            {
+                m_context->m_timer.reset();
+                //// Replace the connection. This causes old connection object to go out of scope.
+                auto client = std::static_pointer_cast<asio_client>(m_context->m_http_client);
+                try
+                {
+                    m_context->m_connection = client->obtain_connection(m_context->m_request);
+                }
+                catch (...)
+                {
+                    m_context->report_exception(std::current_exception());
+                    return;
+                }
+
+                auto endpoint = *endpoints;
+                m_context->m_connection->async_connect(endpoint,
+                                                       boost::bind(&ssl_proxy_tunnel::handle_tcp_connect,
+                                                                   shared_from_this(),
+                                                                   boost::asio::placeholders::error,
+                                                                   ++endpoints));
+            }
+        }
+
+        void handle_write_request(const boost::system::error_code& err)
+        {
+            if (!err)
+            {
+                m_context->m_timer.reset();
+                m_context->m_connection->async_read_until(m_response,
+                                                          CRLF + CRLF,
+                                                          boost::bind(&ssl_proxy_tunnel::handle_status_line,
+                                                                      shared_from_this(),
+                                                                      boost::asio::placeholders::error));
+            }
+            else
+            {
+                m_context->report_error(
+                    "Failed to send connect request to proxy.", err, httpclient_errorcode_context::writebody);
+            }
+        }
+
+        void handle_status_line(const boost::system::error_code& ec)
+        {
+            if (!ec)
+            {
+                m_context->m_timer.reset();
+                std::istream response_stream(&m_response);
+                response_stream.imbue(std::locale::classic());
+                std::string http_version;
+                response_stream >> http_version;
+                status_code status_code;
+                response_stream >> status_code;
+
+                if (!response_stream || http_version.substr(0, 5) != "HTTP/")
+                {
+                    m_context->report_error("Invalid HTTP status line during proxy connection",
+                                            ec,
+                                            httpclient_errorcode_context::readheader);
+                    return;
+                }
+
+                if (status_code != 200)
+                {
+                    m_context->report_error("Expected a 200 response from proxy, received: " + to_string(status_code),
+                                            ec,
+                                            httpclient_errorcode_context::readheader);
+                    return;
+                }
+
+                try
+                {
+                    m_context->upgrade_to_ssl();
+                }
+                catch (...)
+                {
+                    m_context->report_exception(std::current_exception());
+                    return;
+                }
+
+                m_ssl_tunnel_established(m_context);
+            }
+            else
+            {
+                m_context->handle_failed_read_status_line(ec, "Failed to read HTTP status line from proxy");
+            }
+        }
+
+        std::function<void(std::shared_ptr<asio_context>)> m_ssl_tunnel_established;
+        std::shared_ptr<asio_context> m_context;
+
+        boost::asio::streambuf m_request;
+        boost::asio::streambuf m_response;
+    };
+
+    enum class http_proxy_type
+    {
+        none,
+        http,
+        ssl_tunnel
+    };
+
+    void start_request()
+    {
+        if (m_request._cancellation_token().is_canceled())
+        {
+            request_context::report_error(make_error_code(std::errc::operation_canceled).value(),
+                                          "Request canceled by user.");
+            return;
+        }
+
+        http_proxy_type proxy_type = http_proxy_type::none;
+        std::string proxy_host;
+        int proxy_port = -1;
+
+        // There is no support for auto-detection of proxies on non-windows platforms, it must be specified explicitly
+        // from the client code.
+        if (m_http_client->client_config().proxy().is_specified())
+        {
+            proxy_type =
+                m_http_client->base_uri().scheme() == U("https") ? http_proxy_type::ssl_tunnel : http_proxy_type::http;
+            auto proxy = m_http_client->client_config().proxy();
+            auto proxy_uri = proxy.address();
+            proxy_port = proxy_uri.port() == -1 ? 8080 : proxy_uri.port();
+            proxy_host = utility::conversions::to_utf8string(proxy_uri.host());
+        }
+
+        auto start_http_request_flow = [proxy_type, proxy_host, proxy_port AND_CAPTURE_MEMBER_FUNCTION_POINTERS](
+                                           std::shared_ptr<asio_context> ctx) {
+            if (ctx->m_request._cancellation_token().is_canceled())
+            {
+                ctx->request_context::report_error(make_error_code(std::errc::operation_canceled).value(),
+                                                   "Request canceled by user.");
+                return;
+            }
+
+            const auto& base_uri = ctx->m_http_client->base_uri();
+            const auto full_uri = uri_builder(base_uri).append(ctx->m_request.relative_uri()).to_uri();
+
+            // For a normal http proxy, we need to specify the full request uri, otherwise just specify the resource
+            auto encoded_resource =
+                proxy_type == http_proxy_type::http ? full_uri.to_string() : full_uri.resource().to_string();
+
+            if (encoded_resource.empty())
+            {
+                encoded_resource = U("/");
+            }
+
+            const auto& method = ctx->m_request.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))
+            {
+                ctx->report_exception(http_exception("The method string is invalid."));
+                return;
+            }
+
+            std::ostream request_stream(&ctx->m_body_buf);
+            request_stream.imbue(std::locale::classic());
+            const auto& host = utility::conversions::to_utf8string(base_uri.host());
+
+            request_stream << utility::conversions::to_utf8string(method) << " "
+                           << utility::conversions::to_utf8string(encoded_resource) << " "
+                           << "HTTP/1.1\r\n";
+
+            int port = base_uri.port();
+
+            if (base_uri.is_port_default())
+            {
+                port = (ctx->m_connection->is_ssl() ? 443 : 80);
+            }
+
+            // Add the Host header if user has not specified it explicitly
+            if (!ctx->m_request.headers().has(header_names::host))
+            {
+                request_stream << "Host: " << host;
+                if (!base_uri.is_port_default())
+                {
+                    request_stream << ":" << port;
+                }
+                request_stream << CRLF;
+            }
+
+            // Extra request headers are constructed here.
+            std::string extra_headers;
+
+            // Add header for basic proxy authentication
+            if (proxy_type == http_proxy_type::http &&
+                ctx->m_http_client->client_config().proxy().credentials().is_set())
+            {
+                extra_headers.append(ctx->generate_basic_proxy_auth_header());
+            }
+
+            if (ctx->m_http_client->client_config().credentials().is_set())
+            {
+                extra_headers.append(ctx->generate_basic_auth_header());
+            }
+
+            extra_headers += utility::conversions::to_utf8string(ctx->get_compression_header());
+
+            // Check user specified transfer-encoding.
+            std::string transferencoding;
+            if (ctx->m_request.headers().match(header_names::transfer_encoding, transferencoding) &&
+                boost::icontains(transferencoding, U("chunked")))
+            {
+                ctx->m_needChunked = true;
+            }
+            else if (!ctx->m_request.headers().match(header_names::content_length, ctx->m_content_length))
+            {
+                // Stream without content length is the signal of requiring transfer encoding chunked.
+                if (ctx->m_request.body())
+                {
+                    ctx->m_needChunked = true;
+                    extra_headers.append("Transfer-Encoding:chunked\r\n");
+                }
+                else if (ctx->m_request.method() == methods::POST || ctx->m_request.method() == methods::PUT)
+                {
+                    // Some servers do not accept POST/PUT requests with a content length of 0, such as
+                    // lighttpd - http://serverfault.com/questions/315849/curl-post-411-length-required
+                    // old apache versions - https://issues.apache.org/jira/browse/TS-2902
+                    extra_headers.append("Content-Length: 0\r\n");
+                }
+            }
+
+            if (proxy_type == http_proxy_type::http)
+            {
+                extra_headers.append("Cache-Control: no-store, no-cache\r\n"
+                                     "Pragma: no-cache\r\n");
+            }
+
+            request_stream << utility::conversions::to_utf8string(
+                ::web::http::details::flatten_http_headers(ctx->m_request.headers()));
+            request_stream << extra_headers;
+            // Enforce HTTP connection keep alive (even for the old HTTP/1.0 protocol).
+            request_stream << "Connection: Keep-Alive\r\n\r\n";
+
+            // Start connection timeout timer.
+            if (!ctx->m_timer.has_started())
+            {
+                ctx->m_timer.start();
+            }
+
+            if (ctx->m_connection->is_reused() || proxy_type == http_proxy_type::ssl_tunnel)
+            {
+                // If socket is a reused connection or we're connected via an ssl-tunneling proxy, try to write the
+                // request directly. In both cases we have already established a tcp connection.
+                ctx->write_request();
+            }
+            else
+            {
+                // If the connection is new (unresolved and unconnected socket), then start async
+                // call to resolve first, leading eventually to request write.
+
+                // For normal http proxies, we want to connect directly to the proxy server. It will relay our request.
+                auto tcp_host = proxy_type == http_proxy_type::http ? proxy_host : host;
+                auto tcp_port = proxy_type == http_proxy_type::http ? proxy_port : port;
+
+                tcp::resolver::query query(tcp_host, to_string(tcp_port));
+                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.
+            if (ctx->m_request._cancellation_token() != pplx::cancellation_token::none())
+            {
+                // weak_ptr prevents lambda from taking shared ownership of the context.
+                // Otherwise context replacement in the handle_status_line() would leak the objects.
+                std::weak_ptr<asio_context> ctx_weak(ctx);
+                ctx->m_cancellationRegistration = ctx->m_request._cancellation_token().register_callback([ctx_weak]() {
+                    if (auto ctx_lock = ctx_weak.lock())
+                    {
+                        // Shut down transmissions, close the socket and prevent connection from being pooled.
+                        ctx_lock->m_connection->close();
+                    }
+                });
+            }
+        };
+
+        // 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'
+            std::shared_ptr<ssl_proxy_tunnel> ssl_tunnel =
+                std::make_shared<ssl_proxy_tunnel>(shared_from_this(), start_http_request_flow);
+            ssl_tunnel->start_proxy_connect();
+        }
+        else
+        {
+            start_http_request_flow(shared_from_this());
+        }
+    }
+
+    template<typename _ExceptionType>
+    void report_exception(const _ExceptionType& e)
+    {
+        report_exception(std::make_exception_ptr(e));
+    }
+
+    void report_exception(std::exception_ptr exceptionPtr) override
+    {
+        // Don't recycle connections that had an error into the connection pool.
+        m_connection->close();
+        request_context::report_exception(exceptionPtr);
+    }
+
+private:
+    void upgrade_to_ssl()
+    {
+        auto& client = static_cast<asio_client&>(*m_http_client);
+        m_connection->upgrade_to_ssl(calc_cn_host(client.base_uri(), m_request.headers()),
+                                     client.client_config().get_ssl_context_callback());
+    }
+
+    std::string generate_basic_auth_header()
+    {
+        std::string header;
+        header.append("Authorization: Basic ");
+        header.append(generate_base64_userpass(m_http_client->client_config().credentials()));
+        header.append(CRLF);
+        return header;
+    }
+
+    std::string generate_basic_proxy_auth_header()
+    {
+        std::string header;
+        header.append("Proxy-Authorization: Basic ");
+        header.append(generate_base64_userpass(m_http_client->client_config().proxy().credentials()));
+        header.append(CRLF);
+        return header;
+    }
+
+    void report_error(const std::string& message,
+                      const boost::system::error_code& ec,
+                      httpclient_errorcode_context context = httpclient_errorcode_context::none)
+    {
+        // By default, errorcodeValue don't need to converted
+        long errorcodeValue = ec.value();
+
+        // map timer cancellation to time_out
+        if (m_timer.has_timedout())
+        {
+            errorcodeValue = make_error_code(std::errc::timed_out).value();
+        }
+        else
+        {
+            // We need to correct inaccurate ASIO error code base on context information
+            switch (context)
+            {
+                case httpclient_errorcode_context::writeheader:
+                    if (ec == boost::system::errc::broken_pipe)
+                    {
+                        errorcodeValue = make_error_code(std::errc::host_unreachable).value();
+                    }
+                    break;
+                case httpclient_errorcode_context::connect:
+                    if (ec == boost::system::errc::connection_refused)
+                    {
+                        errorcodeValue = make_error_code(std::errc::host_unreachable).value();
+                    }
+                    break;
+                case httpclient_errorcode_context::readheader:
+                    if (ec.default_error_condition().value() ==
+                        boost::system::errc::no_such_file_or_directory) // bug in boost error_code mapping
+                    {
+                        errorcodeValue = make_error_code(std::errc::connection_aborted).value();
+                    }
+                    break;
+                default: break;
+            }
+        }
+        request_context::report_error(errorcodeValue, message);
+    }
+
+    void handle_connect(const boost::system::error_code& ec, tcp::resolver::iterator endpoints)
+    {
+        m_timer.reset();
+        if (!ec)
+        {
+            m_connection->enable_no_delay();
+            write_request();
+        }
+        else if (ec.value() == boost::system::errc::operation_canceled ||
+                 ec.value() == boost::asio::error::operation_aborted)
+        {
+            report_error("Request canceled by user.", ec, httpclient_errorcode_context::connect);
+        }
+        else if (endpoints == tcp::resolver::iterator())
+        {
+            report_error("Failed to connect to any resolved endpoint", ec, httpclient_errorcode_context::connect);
+        }
+        else
+        {
+            // Replace the connection. This causes old connection object to go out of scope.
+            auto client = std::static_pointer_cast<asio_client>(m_http_client);
+            try
+            {
+                m_connection = client->obtain_connection(m_request);
+            }
+            catch (...)
+            {
+                request_context::report_exception(std::current_exception());
+                return;
+            }
+
+            auto endpoint = *endpoints;
+            m_connection->async_connect(
+                endpoint,
+                boost::bind(
+                    &asio_context::handle_connect, shared_from_this(), boost::asio::placeholders::error, ++endpoints));
+        }
+    }
+
+    void handle_resolve(const boost::system::error_code& ec, tcp::resolver::iterator endpoints)
+    {
+        if (ec)
+        {
+            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();
+            auto endpoint = *endpoints;
+            m_connection->async_connect(
+                endpoint,
+                boost::bind(
+                    &asio_context::handle_connect, shared_from_this(), boost::asio::placeholders::error, ++endpoints));
+        }
+    }
+
+    void write_request()
+    {
+        // Only perform handshake if a TLS connection and not being reused.
+        if (m_connection->is_ssl() && !m_connection->is_reused())
+        {
+            const auto weakCtx = std::weak_ptr<asio_context>(shared_from_this());
+            m_connection->async_handshake(
+                boost::asio::ssl::stream_base::client,
+                m_http_client->client_config(),
+                boost::bind(&asio_context::handle_handshake, shared_from_this(), boost::asio::placeholders::error),
+
+                // Use a weak_ptr since the verify_callback is stored until the connection is
+                // destroyed. This avoids creating a circular reference since we pool connection
+                // objects.
+                [weakCtx](bool preverified, boost::asio::ssl::verify_context& verify_context) {
+                    auto this_request = weakCtx.lock();
+                    if (this_request)
+                    {
+                        return this_request->handle_cert_verification(preverified, verify_context);
+                    }
+                    return false;
+                });
+        }
+        else
+        {
+            m_connection->async_write(
+                m_body_buf,
+                boost::bind(&asio_context::handle_write_headers, shared_from_this(), boost::asio::placeholders::error));
+        }
+    }
+
+    void handle_handshake(const boost::system::error_code& ec)
+    {
+        if (!ec)
+        {
+            m_connection->async_write(
+                m_body_buf,
+                boost::bind(&asio_context::handle_write_headers, shared_from_this(), boost::asio::placeholders::error));
+        }
+        else
+        {
+            report_error("Error in SSL handshake", ec, httpclient_errorcode_context::handshake);
+        }
+    }
+
+    bool handle_cert_verification(bool preverified, boost::asio::ssl::verify_context& verifyCtx)
+    {
+        // OpenSSL calls the verification callback once per certificate in the chain,
+        // starting with the root CA certificate. The 'leaf', non-Certificate Authority (CA)
+        // certificate, i.e. actual server certificate is at the '0' position in the
+        // certificate chain, the rest are optional intermediate certificates, followed
+        // finally by the root CA self signed certificate.
+
+#ifdef CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+        // If OpenSSL fails we will doing verification at the end using the whole certificate
+        // chain so wait until the 'leaf' cert. For now return true so OpenSSL continues down
+        // the certificate chain.
+        if (!preverified)
+        {
+            m_openssl_failed = true;
+        }
+
+        if (m_openssl_failed)
+        {
+            return verify_cert_chain_platform_specific(verifyCtx, m_connection->cn_hostname());
+        }
+#endif // CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+
+        boost::asio::ssl::rfc2818_verification rfc2818(m_connection->cn_hostname());
+        return rfc2818(preverified, verifyCtx);
+    }
+
+    void handle_write_headers(const boost::system::error_code& ec)
+    {
+        if (ec)
+        {
+            report_error("Failed to write request headers", ec, httpclient_errorcode_context::writeheader);
+        }
+        else
+        {
+            if (m_needChunked)
+            {
+                handle_write_chunked_body(ec);
+            }
+            else
+            {
+                handle_write_large_body(ec);
+            }
+        }
+    }
+
+    void handle_write_chunked_body(const boost::system::error_code& ec)
+    {
+        if (ec)
+        {
+            // Reuse error handling.
+            return handle_write_body(ec);
+        }
+
+        m_timer.reset();
+        const auto& progress = m_request._get_impl()->_progress_handler();
+        if (progress)
+        {
+            try
+            {
+                (*progress)(message_direction::upload, m_uploaded);
+            }
+            catch (...)
+            {
+                report_exception(std::current_exception());
+                return;
+            }
+        }
+
+        const auto& chunkSize = m_http_client->client_config().chunksize();
+        auto readbuf = _get_readbuffer();
+        uint8_t* buf = boost::asio::buffer_cast<uint8_t*>(
+            m_body_buf.prepare(chunkSize + http::details::chunked_encoding::additional_encoding_space));
+        const auto this_request = shared_from_this();
+        readbuf.getn(buf + http::details::chunked_encoding::data_offset, chunkSize)
+            .then([this_request, buf, chunkSize AND_CAPTURE_MEMBER_FUNCTION_POINTERS](pplx::task<size_t> op) {
+                size_t readSize = 0;
+                try
+                {
+                    readSize = op.get();
+                }
+                catch (...)
+                {
+                    this_request->report_exception(std::current_exception());
+                    return;
+                }
+
+                const size_t offset = http::details::chunked_encoding::add_chunked_delimiters(
+                    buf, chunkSize + http::details::chunked_encoding::additional_encoding_space, readSize);
+                this_request->m_body_buf.commit(readSize + http::details::chunked_encoding::additional_encoding_space);
+                this_request->m_body_buf.consume(offset);
+                this_request->m_uploaded += static_cast<uint64_t>(readSize);
+
+                if (readSize != 0)
+                {
+                    this_request->m_connection->async_write(this_request->m_body_buf,
+                                                            boost::bind(&asio_context::handle_write_chunked_body,
+                                                                        this_request,
+                                                                        boost::asio::placeholders::error));
+                }
+                else
+                {
+                    this_request->m_connection->async_write(
+                        this_request->m_body_buf,
+                        boost::bind(&asio_context::handle_write_body, this_request, boost::asio::placeholders::error));
+                }
+            });
+    }
+
+    void handle_write_large_body(const boost::system::error_code& ec)
+    {
+        if (ec || m_uploaded >= m_content_length)
+        {
+            // Reuse error handling.
+            return handle_write_body(ec);
+        }
+
+        m_timer.reset();
+        const auto& progress = m_request._get_impl()->_progress_handler();
+        if (progress)
+        {
+            try
+            {
+                (*progress)(message_direction::upload, m_uploaded);
+            }
+            catch (...)
+            {
+                report_exception(std::current_exception());
+                return;
+            }
+        }
+
+        const auto this_request = shared_from_this();
+        const auto readSize = static_cast<size_t>((std::min)(
+            static_cast<uint64_t>(m_http_client->client_config().chunksize()), m_content_length - m_uploaded));
+        auto readbuf = _get_readbuffer();
+        readbuf.getn(boost::asio::buffer_cast<uint8_t*>(m_body_buf.prepare(readSize)), readSize)
+            .then([this_request AND_CAPTURE_MEMBER_FUNCTION_POINTERS](pplx::task<size_t> op) {
+                try
+                {
+                    const auto actualReadSize = op.get();
+                    if (actualReadSize == 0)
+                    {
+                        this_request->report_exception(http_exception(
+                            "Unexpected end of request body stream encountered before Content-Length satisfied."));
+                        return;
+                    }
+                    this_request->m_uploaded += static_cast<uint64_t>(actualReadSize);
+                    this_request->m_body_buf.commit(actualReadSize);
+                    this_request->m_connection->async_write(this_request->m_body_buf,
+                                                            boost::bind(&asio_context::handle_write_large_body,
+                                                                        this_request,
+                                                                        boost::asio::placeholders::error));
+                }
+                catch (...)
+                {
+                    this_request->report_exception(std::current_exception());
+                    return;
+                }
+            });
+    }
+
+    void handle_write_body(const boost::system::error_code& ec)
+    {
+        if (!ec)
+        {
+            m_timer.reset();
+            const auto& progress = m_request._get_impl()->_progress_handler();
+            if (progress)
+            {
+                try
+                {
+                    (*progress)(message_direction::upload, m_uploaded);
+                }
+                catch (...)
+                {
+                    report_exception(std::current_exception());
+                    return;
+                }
+            }
+
+            // Read until the end of entire headers
+            m_connection->async_read_until(
+                m_body_buf,
+                CRLF + CRLF,
+                boost::bind(&asio_context::handle_status_line, shared_from_this(), boost::asio::placeholders::error));
+        }
+        else
+        {
+            report_error("Failed to write request body", ec, httpclient_errorcode_context::writebody);
+        }
+    }
+
+    void handle_status_line(const boost::system::error_code& ec)
+    {
+        if (!ec)
+        {
+            m_timer.reset();
+
+            std::istream response_stream(&m_body_buf);
+            response_stream.imbue(std::locale::classic());
+            std::string http_version;
+            response_stream >> http_version;
+            status_code status_code;
+            response_stream >> status_code;
+
+            std::string status_message;
+            std::getline(response_stream, status_message);
+
+            m_response.set_status_code(status_code);
+
+            ::web::http::details::trim_whitespace(status_message);
+            m_response.set_reason_phrase(utility::conversions::to_string_t(std::move(status_message)));
+
+            if (!response_stream || http_version.substr(0, 5) != "HTTP/")
+            {
+                report_error("Invalid HTTP status line", ec, httpclient_errorcode_context::readheader);
+                return;
+            }
+
+            web::http::http_version parsed_version = web::http::http_version::from_string(http_version);
+            m_response._get_impl()->_set_http_version(parsed_version);
+
+            // if HTTP version is 1.0 then disable 'Keep-Alive' by default
+            if (parsed_version == web::http::http_versions::HTTP_1_0)
+            {
+                m_connection->set_keep_alive(false);
+            }
+
+            read_headers();
+        }
+        else
+        {
+            handle_failed_read_status_line(ec, "Failed to read HTTP status line");
+        }
+    }
+
+    void handle_failed_read_status_line(const boost::system::error_code& ec, const char* generic_error_message)
+    {
+        if (m_connection->was_reused_and_closed_by_server(ec))
+        {
+            // Failed to write to socket because connection was already closed while it was in the pool.
+            // close() here ensures socket is closed in a robust way and prevents the connection from being put to the
+            // pool again.
+            m_connection->close();
+
+            // Create a new context and copy the request object, completion event and
+            // cancellation registration to maintain the old state.
+            // This also obtains a new connection from pool.
+            std::shared_ptr<request_context> new_ctx;
+            try
+            {
+                new_ctx = create_request_context(m_http_client, m_request);
+            }
+            catch (...)
+            {
+                report_exception(std::current_exception());
+                return;
+            }
+
+            // If the request contains a valid instream, we try to rewind it to
+            // replay the just-failed request. Otherwise we assume that no data
+            // was sent in the first place.
+            const auto& instream = new_ctx->m_request._get_impl()->instream();
+            if (instream)
+            {
+                // As stated in the commit message of f4f2348, we might encounter
+                // streams that are not capable of rewinding and hence resending the
+                // request is not possible. We cannot recover from this condition and
+                // need to escalate it to the using code.
+                if (!instream.can_seek())
+                {
+                    report_error("cannot rewind input stream for connection re-establishment",
+                                 ec,
+                                 httpclient_errorcode_context::readheader);
+                    return;
+                }
+
+                try
+                {
+                    // Rewinding the stream might throw, in which case we cannot do the
+                    // connection re-establishment transparently. I.e. report the exception
+                    // to the calling code.
+                    instream.seek(0);
+                }
+                catch (...)
+                {
+                    report_exception(std::current_exception());
+                    return;
+                }
+            }
+
+            new_ctx->m_request_completion = m_request_completion;
+            new_ctx->m_cancellationRegistration = m_cancellationRegistration;
+
+            auto client = std::static_pointer_cast<asio_client>(m_http_client);
+            // Resend the request using the new context.
+            client->send_request(new_ctx);
+        }
+        else
+        {
+            report_error(generic_error_message, ec, httpclient_errorcode_context::readheader);
+        }
+    }
+
+    void read_headers()
+    {
+        auto needChunked = false;
+        std::istream response_stream(&m_body_buf);
+        response_stream.imbue(std::locale::classic());
+        std::string header;
+        while (std::getline(response_stream, header) && header != "\r")
+        {
+            const auto colon = header.find(':');
+            if (colon != std::string::npos)
+            {
+                auto name = header.substr(0, colon);
+                auto value = header.substr(colon + 1, header.size() - colon - 2);
+                boost::algorithm::trim(name);
+                boost::algorithm::trim(value);
+
+                if (boost::iequals(name, header_names::transfer_encoding))
+                {
+                    needChunked = boost::icontains(value, U("chunked"));
+                }
+
+                if (boost::iequals(name, header_names::connection))
+                {
+                    // If the server uses HTTP/1.1, then 'Keep-Alive' is the default,
+                    // so connection is explicitly closed only if we get "Connection: close".
+                    // If the server uses HTTP/1.0, it would need to respond using
+                    // 'Connection: Keep-Alive' every time.
+                    if (m_response._get_impl()->http_version() != web::http::http_versions::HTTP_1_0)
+                        m_connection->set_keep_alive(!boost::iequals(value, U("close")));
+                    else
+                        m_connection->set_keep_alive(boost::iequals(value, U("Keep-Alive")));
+                }
+
+                m_response.headers().add(utility::conversions::to_string_t(std::move(name)),
+                                         utility::conversions::to_string_t(std::move(value)));
+            }
+        }
+
+        m_content_length = (std::numeric_limits<size_t>::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())
+        {
+            // false indicates report_exception was called
+            return;
+        }
+
+        complete_headers();
+
+        // Check for HEAD requests and status codes which cannot contain a
+        // message body in HTTP/1.1 (see 3.3.3/1 of the RFC 7230).
+        //
+        // note: need to check for 'chunked' here as well, azure storage sends both
+        // transfer-encoding:chunked and content-length:0 (although HTTP says not to)
+        const auto status = m_response.status_code();
+        if (m_request.method() == U("HEAD") || (status >= 100 && status < 200) || status == status_codes::NoContent ||
+            status == status_codes::NotModified || (!needChunked && m_content_length == 0))
+        {
+            // we can stop early - no body
+            const auto& progress = m_request._get_impl()->_progress_handler();
+            if (progress)
+            {
+                try
+                {
+                    (*progress)(message_direction::download, 0);
+                }
+                catch (...)
+                {
+                    report_exception(std::current_exception());
+                    return;
+                }
+            }
+
+            complete_request(0);
+        }
+        else
+        {
+            if (!needChunked)
+            {
+                async_read_until_buffersize(
+                    static_cast<size_t>((std::min)(m_content_length,
+                                                   static_cast<uint64_t>(m_http_client->client_config().chunksize()))),
+                    boost::bind(
+                        &asio_context::handle_read_content, shared_from_this(), boost::asio::placeholders::error));
+            }
+            else
+            {
+                m_connection->async_read_until(m_body_buf,
+                                               CRLF,
+                                               boost::bind(&asio_context::handle_chunk_header,
+                                                           shared_from_this(),
+                                                           boost::asio::placeholders::error));
+            }
+        }
+    }
+
+    template<typename ReadHandler>
+    void async_read_until_buffersize(size_t size, const ReadHandler& handler)
+    {
+        size_t size_to_read = 0;
+        if (m_body_buf.size() < size)
+        {
+            size_to_read = size - m_body_buf.size();
+        }
+
+        m_connection->async_read(m_body_buf, boost::asio::transfer_exactly(size_to_read), handler);
+    }
+
+    void handle_chunk_header(const boost::system::error_code& ec)
+    {
+        if (!ec)
+        {
+            m_timer.reset();
+
+            std::istream response_stream(&m_body_buf);
+            response_stream.imbue(std::locale::classic());
+            std::string line;
+            std::getline(response_stream, line);
+
+            std::istringstream octetLine(std::move(line));
+            octetLine.imbue(std::locale::classic());
+            int octets = 0;
+            octetLine >> std::hex >> octets;
+
+            if (octetLine.fail())
+            {
+                report_error("Invalid chunked response header",
+                             boost::system::error_code(),
+                             httpclient_errorcode_context::readbody);
+            }
+            else
+            {
+                async_read_until_buffersize(
+                    octets + CRLF.size(),
+                    boost::bind(
+                        &asio_context::handle_chunk, shared_from_this(), boost::asio::placeholders::error, octets));
+            }
+        }
+        else
+        {
+            report_error("Retrieving message chunk header", ec, httpclient_errorcode_context::readbody);
+        }
+    }
+
+    bool decompress(const uint8_t* input, size_t input_size, std::vector<uint8_t>& output)
+    {
+        // Need to guard against attempting to decompress when we're already finished or encountered an error!
+        if (input == nullptr || input_size == 0)
+        {
+            return false;
+        }
+
+        size_t processed;
+        size_t got;
+        size_t inbytes = 0;
+        size_t outbytes = 0;
+        bool done;
+
+        try
+        {
+            output.resize(input_size * 3);
+            do
+            {
+                if (inbytes)
+                {
+                    output.resize(output.size() + (std::max)(input_size, static_cast<size_t>(1024)));
+                }
+                got = m_decompressor->decompress(input + inbytes,
+                                                 input_size - inbytes,
+                                                 output.data() + outbytes,
+                                                 output.size() - outbytes,
+                                                 web::http::compression::operation_hint::has_more,
+                                                 processed,
+                                                 done);
+                inbytes += processed;
+                outbytes += got;
+            } while (got && !done);
+            output.resize(outbytes);
+        }
+        catch (...)
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    void handle_chunk(const boost::system::error_code& ec, int to_read)
+    {
+        if (!ec)
+        {
+            m_timer.reset();
+
+            m_downloaded += static_cast<uint64_t>(to_read);
+            const auto& progress = m_request._get_impl()->_progress_handler();
+            if (progress)
+            {
+                try
+                {
+                    (*progress)(message_direction::download, m_downloaded);
+                }
+                catch (...)
+                {
+                    report_exception(std::current_exception());
+                    return;
+                }
+            }
+
+            if (to_read == 0)
+            {
+                m_body_buf.consume(CRLF.size());
+                complete_request(m_downloaded);
+            }
+            else
+            {
+                auto writeBuffer = _get_writebuffer();
+                const auto this_request = shared_from_this();
+                if (m_decompressor)
+                {
+                    std::vector<uint8_t> decompressed;
+
+                    bool boo =
+                        decompress(boost::asio::buffer_cast<const uint8_t*>(m_body_buf.data()), to_read, decompressed);
+                    if (!boo)
+                    {
+                        report_exception(std::runtime_error("Failed to decompress the response body"));
+                        return;
+                    }
+
+                    // It is valid for the decompressor to sometimes return an empty output for a given chunk, the data
+                    // will be flushed when the next chunk is received
+                    if (decompressed.empty())
+                    {
+                        m_body_buf.consume(to_read + CRLF.size()); // consume crlf
+                        m_connection->async_read_until(m_body_buf,
+                                                       CRLF,
+                                                       boost::bind(&asio_context::handle_chunk_header,
+                                                                   this_request,
+                                                                   boost::asio::placeholders::error));
+                    }
+                    else
+                    {
+                        // Move the decompressed buffer into a shared_ptr to keep it alive until putn_nocopy completes.
+                        // When VS 2013 support is dropped, this should be changed to a unique_ptr plus a move capture.
+                        auto shared_decompressed = std::make_shared<std::vector<uint8_t>>(std::move(decompressed));
+
+                        writeBuffer.putn_nocopy(shared_decompressed->data(), shared_decompressed->size())
+                            .then([this_request, to_read, shared_decompressed AND_CAPTURE_MEMBER_FUNCTION_POINTERS](
+                                      pplx::task<size_t> op) {
+                                try
+                                {
+                                    op.get();
+                                    this_request->m_body_buf.consume(to_read + CRLF.size()); // consume crlf
+                                    this_request->m_connection->async_read_until(
+                                        this_request->m_body_buf,
+                                        CRLF,
+                                        boost::bind(&asio_context::handle_chunk_header,
+                                                    this_request,
+                                                    boost::asio::placeholders::error));
+                                }
+                                catch (...)
+                                {
+                                    this_request->report_exception(std::current_exception());
+                                    return;
+                                }
+                            });
+                    }
+                }
+                else
+                {
+                    writeBuffer.putn_nocopy(boost::asio::buffer_cast<const uint8_t*>(m_body_buf.data()), to_read)
+                        .then([this_request, to_read AND_CAPTURE_MEMBER_FUNCTION_POINTERS](pplx::task<size_t> op) {
+                            try
+                            {
+                                op.wait();
+                            }
+                            catch (...)
+                            {
+                                this_request->report_exception(std::current_exception());
+                                return;
+                            }
+                            this_request->m_body_buf.consume(to_read + CRLF.size()); // consume crlf
+                            this_request->m_connection->async_read_until(this_request->m_body_buf,
+                                                                         CRLF,
+                                                                         boost::bind(&asio_context::handle_chunk_header,
+                                                                                     this_request,
+                                                                                     boost::asio::placeholders::error));
+                        });
+                }
+            }
+        }
+        else
+        {
+            report_error("Failed to read chunked response part", ec, httpclient_errorcode_context::readbody);
+        }
+    }
+
+    void handle_read_content(const boost::system::error_code& ec)
+    {
+        auto writeBuffer = _get_writebuffer();
+
+        if (ec)
+        {
+            if (ec == boost::asio::error::eof && m_content_length == (std::numeric_limits<size_t>::max)())
+            {
+                m_content_length = m_downloaded + m_body_buf.size();
+            }
+            else
+            {
+                report_error("Failed to read response body", ec, httpclient_errorcode_context::readbody);
+                return;
+            }
+        }
+
+        m_timer.reset();
+        const auto& progress = m_request._get_impl()->_progress_handler();
+        if (progress)
+        {
+            try
+            {
+                (*progress)(message_direction::download, m_downloaded);
+            }
+            catch (...)
+            {
+                report_exception(std::current_exception());
+                return;
+            }
+        }
+
+        if (m_downloaded < m_content_length)
+        {
+            // more data need to be read
+            const auto this_request = shared_from_this();
+
+            auto read_size = static_cast<size_t>(
+                (std::min)(static_cast<uint64_t>(m_body_buf.size()), m_content_length - m_downloaded));
+
+            if (m_decompressor)
+            {
+                std::vector<uint8_t> decompressed;
+
+                bool boo =
+                    decompress(boost::asio::buffer_cast<const uint8_t*>(m_body_buf.data()), read_size, decompressed);
+                if (!boo)
+                {
+                    this_request->report_exception(std::runtime_error("Failed to decompress the response body"));
+                    return;
+                }
+
+                // It is valid for the decompressor to sometimes return an empty output for a given chunk, the data will
+                // be flushed when the next chunk is received
+                if (decompressed.empty())
+                {
+                    try
+                    {
+                        this_request->m_downloaded += static_cast<uint64_t>(read_size);
+
+                        this_request->async_read_until_buffersize(
+                            static_cast<size_t>((std::min)(
+                                static_cast<uint64_t>(this_request->m_http_client->client_config().chunksize()),
+                                this_request->m_content_length - this_request->m_downloaded)),
+                            boost::bind(
+                                &asio_context::handle_read_content, this_request, boost::asio::placeholders::error));
+                    }
+                    catch (...)
+                    {
+                        this_request->report_exception(std::current_exception());
+                        return;
+                    }
+                }
+                else
+                {
+                    // Move the decompressed buffer into a shared_ptr to keep it alive until putn_nocopy completes.
+                    // When VS 2013 support is dropped, this should be changed to a unique_ptr plus a move capture.
+                    auto shared_decompressed = std::make_shared<std::vector<uint8_t>>(std::move(decompressed));
+
+                    writeBuffer.putn_nocopy(shared_decompressed->data(), shared_decompressed->size())
+                        .then([this_request, read_size, shared_decompressed AND_CAPTURE_MEMBER_FUNCTION_POINTERS](
+                                  pplx::task<size_t> op) {
+                            size_t writtenSize = 0;
+                            (void)writtenSize;
+                            try
+                            {
+                                writtenSize = op.get();
+                                this_request->m_downloaded += static_cast<uint64_t>(read_size);
+                                this_request->m_body_buf.consume(read_size);
+                                this_request->async_read_until_buffersize(
+                                    static_cast<size_t>((std::min)(
+                                        static_cast<uint64_t>(this_request->m_http_client->client_config().chunksize()),
+                                        this_request->m_content_length - this_request->m_downloaded)),
+                                    boost::bind(&asio_context::handle_read_content,
+                                                this_request,
+                                                boost::asio::placeholders::error));
+                            }
+                            catch (...)
+                            {
+                                this_request->report_exception(std::current_exception());
+                                return;
+                            }
+                        });
+                }
+            }
+            else
+            {
+                writeBuffer.putn_nocopy(boost::asio::buffer_cast<const uint8_t*>(m_body_buf.data()), read_size)
+                    .then([this_request AND_CAPTURE_MEMBER_FUNCTION_POINTERS](pplx::task<size_t> op) {
+                        size_t writtenSize = 0;
+                        try
+                        {
+                            writtenSize = op.get();
+                            this_request->m_downloaded += static_cast<uint64_t>(writtenSize);
+                            this_request->m_body_buf.consume(writtenSize);
+                            this_request->async_read_until_buffersize(
+                                static_cast<size_t>((std::min)(
+                                    static_cast<uint64_t>(this_request->m_http_client->client_config().chunksize()),
+                                    this_request->m_content_length - this_request->m_downloaded)),
+                                boost::bind(&asio_context::handle_read_content,
+                                            this_request,
+                                            boost::asio::placeholders::error));
+                        }
+                        catch (...)
+                        {
+                            this_request->report_exception(std::current_exception());
+                            return;
+                        }
+                    });
+            }
+        }
+        else
+        {
+            // Request is complete no more data to read.
+            complete_request(m_downloaded);
+        }
+    }
+
+    // Simple timer class wrapping Boost deadline timer.
+    // Closes the connection when timer fires.
+    class timeout_timer
+    {
+    public:
+        timeout_timer(const std::chrono::microseconds& timeout)
+            : m_duration(timeout.count()), m_state(created), m_timer(crossplat::threadpool::shared_instance().service())
+        {
+        }
+
+        void set_ctx(const std::weak_ptr<asio_context>& ctx) { m_ctx = ctx; }
+
+        void start()
+        {
+            assert(m_state == created);
+            assert(!m_ctx.expired());
+            m_state = started;
+
+            m_timer.expires_from_now(m_duration);
+            auto ctx = m_ctx;
+            m_timer.async_wait([ctx AND_CAPTURE_MEMBER_FUNCTION_POINTERS](const boost::system::error_code& ec) {
+                handle_timeout(ec, ctx);
+            });
+        }
+
+        void reset()
+        {
+            assert(m_state == started || m_state == timedout);
+            assert(!m_ctx.expired());
+            if (m_timer.expires_from_now(m_duration) > 0)
+            {
+                // The existing handler was canceled so schedule a new one.
+                assert(m_state == started);
+                auto ctx = m_ctx;
+                m_timer.async_wait([ctx AND_CAPTURE_MEMBER_FUNCTION_POINTERS](const boost::system::error_code& ec) {
+                    handle_timeout(ec, ctx);
+                });
+            }
+        }
+
+        bool has_timedout() const { return m_state == timedout; }
+
+        bool has_started() const { return m_state == started; }
+
+        void stop()
+        {
+            m_state = stopped;
+            m_timer.cancel();
+        }
+
+        static void handle_timeout(const boost::system::error_code& ec, const std::weak_ptr<asio_context>& ctx)
+        {
+            if (!ec)
+            {
+                auto shared_ctx = ctx.lock();
+                if (shared_ctx)
+                {
+                    assert(shared_ctx->m_timer.m_state != timedout);
+                    shared_ctx->m_timer.m_state = timedout;
+                    shared_ctx->m_connection->close();
+                }
+            }
+        }
+
+    private:
+        enum timer_state
+        {
+            created,
+            started,
+            stopped,
+            timedout
+        };
+
+#if (defined(ANDROID) || defined(__ANDROID__)) && !defined(_LIBCPP_VERSION)
+        boost::chrono::microseconds m_duration;
+#else
+        std::chrono::microseconds m_duration;
+#endif
+        std::atomic<timer_state> m_state;
+        std::weak_ptr<asio_context> m_ctx;
+        boost::asio::steady_timer m_timer;
+    };
+
+    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<asio_connection> m_connection;
+
+#ifdef CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+    bool m_openssl_failed;
+#endif // CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+};
+
+std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri&& base_uri,
+                                                                                http_client_config&& client_config)
+{
+    return std::make_shared<asio_client>(std::move(base_uri), std::move(client_config));
+}
+
+void asio_client::send_request(const std::shared_ptr<request_context>& request_ctx)
+{
+    auto ctx = std::static_pointer_cast<asio_context>(request_ctx);
+
+    try
+    {
+        if (ctx->m_connection->is_ssl())
+        {
+            client_config().invoke_nativehandle_options(ctx->m_connection->m_ssl_stream.get());
+        }
+        else
+        {
+            client_config().invoke_nativehandle_options(&(ctx->m_connection->m_socket));
+        }
+    }
+    catch (...)
+    {
+        request_ctx->report_exception(std::current_exception());
+        return;
+    }
+
+    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<utility::string_t> 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<uri> 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<http_response> 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_response> 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<http_response> asio_client::propagate(http_request request)
+{
+    auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this());
+    std::shared_ptr<request_context> context;
+    try
+    {
+        context = details::asio_context::create_request_context(self, request);
+    }
+    catch (...)
+    {
+        return pplx::task_from_exception<http_response>(std::current_exception());
+    }
+
+    // Use a task to externally signal the final result and completion of the task.
+    auto result_task = pplx::create_task(context->m_request_completion);
+
+    // Asynchronously send the response with the HTTP client implementation.
+    this->async_send_request(context);
+
+    return client_config().max_redirects() > 0
+        ? result_task.then(http_redirect_follower(client_config(), request))
+        : result_task;
+}
+} // namespace details
+} // namespace client
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/client/http_client_impl.h b/Release/src/http/client/http_client_impl.h
new file mode 100644 (file)
index 0000000..d9e7d48
--- /dev/null
@@ -0,0 +1,163 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Client-side APIs.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#pragma once
+
+#include "cpprest/astreambuf.h"
+#include "cpprest/details/basic_types.h"
+#include "cpprest/http_client.h"
+#include "cpprest/http_msg.h"
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+namespace web
+{
+namespace http
+{
+namespace details
+{
+/// <summary>
+/// Serialize the http_headers into name:value pairs separated by a carriage return and line feed.
+/// </summary>
+utility::string_t flatten_http_headers(const http_headers& headers);
+/// <summary>
+/// Parses a string containing Http headers.
+/// </summary>
+void parse_headers_string(_Inout_z_ utility::char_t* headersStr, http_headers& headers);
+} // namespace details
+} // namespace http
+} // namespace web
+
+namespace web
+{
+namespace http
+{
+namespace client
+{
+namespace details
+{
+class _http_client_communicator;
+
+// Request context encapsulating everything necessary for creating and responding to a request.
+class request_context
+{
+public:
+    // Destructor to clean up any held resources.
+    virtual ~request_context() {}
+
+    virtual void report_exception(std::exception_ptr exceptionPtr);
+
+    virtual concurrency::streams::streambuf<uint8_t> _get_readbuffer();
+
+    void complete_headers();
+
+    /// <summary>
+    /// Completes this request, setting the underlying task completion event, and cleaning up the handles
+    /// </summary>
+    void complete_request(utility::size64_t body_size);
+
+    void report_error(unsigned long error_code, const std::string& errorMessage);
+
+#ifdef _WIN32
+    void report_error(unsigned long error_code, const std::wstring& errorMessage);
+#endif
+
+    template<typename _ExceptionType>
+    void report_exception(const _ExceptionType& e)
+    {
+        report_exception(std::make_exception_ptr(e));
+    }
+
+    /// <summary>Set m_decompressor based on the response headers, or call report_exception</summary>
+    /// <returns>false on failure</returns>
+    bool handle_compression();
+
+    /// <summary>Append an Accept-Encoding header if requested by the http_client settings</summary>
+    utility::string_t get_compression_header() const;
+
+    concurrency::streams::streambuf<uint8_t> _get_writebuffer();
+
+    // Reference to the http_client implementation.
+    std::shared_ptr<_http_client_communicator> m_http_client;
+
+    // request/response pair.
+    http_request m_request;
+    http_response m_response;
+
+    utility::size64_t m_uploaded;
+    utility::size64_t m_downloaded;
+
+    // task completion event to signal request is completed.
+    pplx::task_completion_event<http_response> m_request_completion;
+
+    // Registration for cancellation notification if enabled.
+    pplx::cancellation_token_registration m_cancellationRegistration;
+
+    std::unique_ptr<web::http::compression::decompress_provider> m_decompressor;
+
+protected:
+    request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request);
+
+    virtual void finish();
+};
+
+//
+// Interface used by client implementations. Concrete implementations are responsible for
+// sending HTTP requests and receiving the responses.
+//
+class _http_client_communicator : public http_pipeline_stage
+{
+public:
+    virtual ~_http_client_communicator() override = default;
+
+    // Asynchronously send a HTTP request and process the response.
+    void async_send_request(const std::shared_ptr<request_context>& request);
+
+    void finish_request();
+
+    const http_client_config& client_config() const;
+
+    const uri& base_uri() const;
+
+protected:
+    _http_client_communicator(http::uri&& address, http_client_config&& client_config);
+
+    // HTTP client implementations must implement send_request.
+    virtual void send_request(_In_ const std::shared_ptr<request_context>& request) = 0;
+
+    // URI to connect to.
+    const http::uri m_uri;
+
+    pplx::extensibility::critical_section_t m_client_lock;
+
+private:
+    http_client_config m_client_config;
+
+    // Wraps opening the client around sending a request.
+    void async_send_request_impl(const std::shared_ptr<request_context>& request);
+
+    // Queue used to guarantee ordering of requests, when applicable.
+    std::queue<std::shared_ptr<request_context>> m_requests_queue;
+    bool m_outstanding;
+};
+
+/// <summary>
+/// Factory function implemented by the separate platforms to construct their subclasses of _http_client_communicator
+/// </summary>
+std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri&& base_uri,
+                                                                                http_client_config&& client_config);
+
+} // namespace details
+} // namespace client
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/client/http_client_msg.cpp b/Release/src/http/client/http_client_msg.cpp
new file mode 100644 (file)
index 0000000..4ed3be8
--- /dev/null
@@ -0,0 +1,102 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Request and reply message definitions (client side).
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "../common/internal_http_helpers.h"
+#include "cpprest/asyncrt_utils.h"
+
+namespace web
+{
+namespace http
+{
+uri details::_http_request::relative_uri() const
+{
+    // If the listener path is empty, then just return the request URI.
+    if (m_listener_path.empty() || m_listener_path == _XPLATSTR("/"))
+    {
+        return m_uri.resource();
+    }
+
+    utility::string_t prefix = uri::decode(m_listener_path);
+    utility::string_t path = uri::decode(m_uri.resource().to_string());
+    if (path.empty())
+    {
+        path = _XPLATSTR("/");
+    }
+
+    auto pos = path.find(prefix);
+    if (pos == 0)
+    {
+        return uri(uri::encode_uri(path.erase(0, prefix.length())));
+    }
+    else
+    {
+        throw http_exception(_XPLATSTR("Error: request was not prefixed with listener uri"));
+    }
+}
+
+uri details::_http_request::absolute_uri() const
+{
+    if (m_base_uri.is_empty())
+    {
+        return m_uri;
+    }
+    else
+    {
+        return uri_builder(m_base_uri).append(m_uri).to_uri();
+    }
+}
+
+void details::_http_request::set_request_uri(const uri& relative) { m_uri = relative; }
+
+utility::string_t details::_http_request::to_string() const
+{
+    utility::string_t result(m_method);
+    result += _XPLATSTR(' ');
+    if (this->m_uri.is_empty())
+    {
+        result += _XPLATSTR('/');
+    }
+    else
+    {
+        result += this->m_uri.to_string();
+    }
+
+    result += _XPLATSTR(" HTTP/1.1\r\n");
+    result += http_msg_base::to_string();
+    return result;
+}
+
+utility::string_t details::_http_response::to_string() const
+{
+    utility::string_t result(_XPLATSTR("HTTP/1.1 "));
+    result += utility::conversions::details::to_string_t(m_status_code);
+    result += ' ';
+    // If the user didn't explicitly set a reason phrase then we should have it default
+    // if they used one of the standard known status codes.
+    if (m_reason_phrase.empty())
+    {
+        result += get_default_reason_phrase(status_code());
+    }
+    else
+    {
+        result += m_reason_phrase;
+    }
+
+    result += _XPLATSTR("\r\n");
+    result += http_msg_base::to_string();
+    return result;
+}
+
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/client/http_client_winhttp.cpp b/Release/src/http/client/http_client_winhttp.cpp
new file mode 100644 (file)
index 0000000..d6cdb53
--- /dev/null
@@ -0,0 +1,2604 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Client-side APIs.
+ *
+ * This file contains the implementation for Windows Desktop, based on WinHTTP.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#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 <Wincrypt.h>
+#endif
+#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
+#include "winhttppal.h"
+#endif
+#include <atomic>
+
+#if _WIN32_WINNT && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
+#include <VersionHelpers.h>
+#endif
+
+namespace
+{
+struct security_failure_message
+{
+    std::uint32_t flag;
+    const char* text;
+};
+
+CPPREST_CONSTEXPR security_failure_message g_security_failure_messages[] = {
+    {WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED,
+     "WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED failed to check revocation status."},
+    {WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT,
+     "WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT SSL certificate is invalid."},
+    {WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED,
+     "WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED SSL certificate was revoked."},
+    {WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA, "WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA SSL invalid CA."},
+    {WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID,
+     "WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID SSL common name does not match."},
+    {WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID,
+     "WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID SLL certificate is expired."},
+    {WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR,
+     "WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR internal error."},
+};
+
+std::string generate_security_failure_message(std::uint32_t flags)
+{
+    std::string result("SSL Error:");
+    for (const auto& message : g_security_failure_messages)
+    {
+        if (flags & message.flag)
+        {
+            result.push_back(' ');
+            result.append(message.text);
+        }
+    }
+
+    return result;
+}
+
+} // unnamed namespace
+namespace web
+{
+namespace http
+{
+namespace client
+{
+namespace details
+{
+// Helper function to query for the size of header values.
+static void query_header_length(HINTERNET request_handle, DWORD header, DWORD& length)
+{
+    WinHttpQueryHeaders(request_handle,
+                        header,
+                        WINHTTP_HEADER_NAME_BY_INDEX,
+                        WINHTTP_NO_OUTPUT_BUFFER,
+                        &length,
+                        WINHTTP_NO_HEADER_INDEX);
+}
+
+// Helper function to get the status code from a WinHTTP response.
+static http::status_code parse_status_code(HINTERNET request_handle)
+{
+    DWORD length = 0;
+    query_header_length(request_handle, WINHTTP_QUERY_STATUS_CODE, length);
+    utility::string_t buffer;
+    buffer.resize(length);
+    WinHttpQueryHeaders(request_handle,
+                        WINHTTP_QUERY_STATUS_CODE,
+                        WINHTTP_HEADER_NAME_BY_INDEX,
+                        &buffer[0],
+                        &length,
+                        WINHTTP_NO_HEADER_INDEX);
+    return (unsigned short)stoi(buffer);
+}
+
+// Helper function to get the reason phrase from a WinHTTP response.
+static utility::string_t parse_reason_phrase(HINTERNET request_handle)
+{
+    utility::string_t phrase;
+    DWORD length = 0;
+
+    query_header_length(request_handle, WINHTTP_QUERY_STATUS_TEXT, length);
+    phrase.resize(length);
+    WinHttpQueryHeaders(request_handle,
+                        WINHTTP_QUERY_STATUS_TEXT,
+                        WINHTTP_HEADER_NAME_BY_INDEX,
+                        &phrase[0],
+                        &length,
+                        WINHTTP_NO_HEADER_INDEX);
+    // WinHTTP reports back the wrong length, trim any null characters.
+    ::web::http::details::trim_nulls(phrase);
+    return phrase;
+}
+
+/// <summary>
+/// Parses a string containing HTTP headers.
+/// </summary>
+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();
+
+    // Status code and reason phrase.
+    response.set_status_code(parse_status_code(request_handle));
+    response.set_reason_phrase(parse_reason_phrase(request_handle));
+
+    web::http::details::parse_headers_string(headersStr, response.headers());
+}
+
+// Helper function to build error messages.
+static std::string build_error_msg(unsigned long code, const std::string& location)
+{
+    std::string msg(location);
+    msg.append(": ");
+    msg.append(std::to_string(code));
+    msg.append(": ");
+    msg.append(utility::details::platform_category().message(static_cast<int>(code)));
+    return msg;
+}
+
+// Helper function to build an error message from a WinHTTP async result.
+static std::string build_error_msg(_In_ WINHTTP_ASYNC_RESULT* error_result)
+{
+    switch (error_result->dwResult)
+    {
+        case API_RECEIVE_RESPONSE: return build_error_msg(error_result->dwError, "WinHttpReceiveResponse");
+        case API_QUERY_DATA_AVAILABLE: return build_error_msg(error_result->dwError, "WinHttpQueryDataAvaliable");
+        case API_READ_DATA: return build_error_msg(error_result->dwError, "WinHttpReadData");
+        case API_WRITE_DATA: return build_error_msg(error_result->dwError, "WinHttpWriteData");
+        case API_SEND_REQUEST: return build_error_msg(error_result->dwError, "WinHttpSendRequest");
+        default: return build_error_msg(error_result->dwError, "Unknown WinHTTP Function");
+    }
+}
+
+
+class memory_holder
+{
+    uint8_t* m_externalData;
+    std::vector<uint8_t> m_internalData;
+    size_t m_size;
+
+public:
+    memory_holder() : m_externalData(nullptr), m_size(0) {}
+
+    void allocate_space(size_t length)
+    {
+        if (length > m_internalData.size())
+        {
+            m_internalData.resize(length);
+        }
+        m_externalData = nullptr;
+    }
+
+    inline void reassign_to(_In_opt_ uint8_t* block, size_t length)
+    {
+        assert(block != nullptr);
+        m_externalData = block;
+        m_size = length;
+    }
+
+    inline bool is_internally_allocated() const { return m_externalData == nullptr; }
+
+    inline uint8_t* get() { return is_internally_allocated() ? &m_internalData[0] : m_externalData; }
+
+    inline size_t size() const { return is_internally_allocated() ? m_internalData.size() : m_size; }
+};
+
+// Possible ways a message body can be sent/received.
+enum msg_body_type
+{
+    no_body,
+    content_length_chunked,
+    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
+{
+public:
+    // Factory function to create requests on the heap.
+    static std::shared_ptr<request_context> create_request_context(
+        const std::shared_ptr<_http_client_communicator>& client, const http_request& request)
+    {
+        std::shared_ptr<winhttp_request_context> ret(new winhttp_request_context(client, request));
+        ret->m_self_reference = ret;
+        return std::move(ret);
+    }
+
+    ~winhttp_request_context() { cleanup(); }
+
+    void allocate_request_space(_In_opt_ uint8_t* block, size_t length)
+    {
+        if (block == nullptr)
+            m_body_data.allocate_space(length);
+        else
+            m_body_data.reassign_to(block, length);
+    }
+
+    void allocate_reply_space(_In_opt_ uint8_t* block, size_t length)
+    {
+        if (block == nullptr)
+            m_body_data.allocate_space(length);
+        else
+            m_body_data.reassign_to(block, length);
+    }
+
+    bool is_externally_allocated() const { return !m_body_data.is_internally_allocated(); }
+
+    HINTERNET m_request_handle;
+    std::weak_ptr<winhttp_request_context>*
+        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;
+
+    std::char_traits<uint8_t>::pos_type m_startingPosition;
+
+    // If the user specified that to guarantee data buffering of request data, in case of challenged authentication
+    // requests, etc... Then if the request stream buffer doesn't support seeking we need to copy the body chunks as it
+    // is sent.
+    concurrency::streams::istream m_readStream;
+    std::unique_ptr<concurrency::streams::container_buffer<std::vector<uint8_t>>> m_readBufferCopy;
+    virtual concurrency::streams::streambuf<uint8_t> _get_readbuffer() { return m_readStream.streambuf(); }
+
+    // This self reference will keep us alive until finish() is called.
+    std::shared_ptr<winhttp_request_context> m_self_reference;
+    memory_holder m_body_data;
+
+    // Compress/decompress-related processing state lives here
+    class compression_state
+    {
+    public:
+        compression_state()
+            : m_acquired(nullptr)
+            , m_bytes_read(0)
+            , m_bytes_processed(0)
+            , m_needs_flush(false)
+            , m_started(false)
+            , m_done(false)
+            , m_chunked(false)
+        {
+        }
+
+#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))
+            , m_acquired(other.m_acquired)
+            , m_bytes_read(other.m_bytes_read)
+            , m_bytes_processed(other.m_bytes_processed)
+            , m_needs_flush(other.m_needs_flush)
+            , m_started(other.m_started)
+            , m_done(other.m_done)
+            , m_chunked(other.m_chunked)
+            , m_chunk_bytes(other.m_chunk_bytes)
+            , m_chunk(std::move(other.m_chunk))
+        {
+        }
+        compression_state& operator=(const compression_state&) = delete;
+        compression_state& operator=(compression_state&& other)
+        {
+            m_buffer = std::move(other.m_buffer);
+            m_acquired = other.m_acquired;
+            m_bytes_read = other.m_bytes_read;
+            m_bytes_processed = other.m_bytes_processed;
+            m_needs_flush = other.m_needs_flush;
+            m_started = other.m_started;
+            m_done = other.m_done;
+            m_chunked = other.m_chunked;
+            m_chunk_bytes = other.m_chunk_bytes;
+            m_chunk = std::move(other.m_chunk);
+            return *this;
+        }
+#endif // defined(_MSC_VER) && _MSC_VER < 1900
+
+        // Minimal state for on-the-fly decoding of "chunked" encoded data
+        class _chunk_helper
+        {
+        public:
+            _chunk_helper()
+                : m_bytes_remaining(0)
+                , m_chunk_size(true)
+                , m_chunk_delim(false)
+                , m_expect_linefeed(false)
+                , m_ignore(false)
+                , m_trailer(false)
+            {
+            }
+
+            // Returns true if the end of chunked data has been reached, specifically whether the 0-length
+            // chunk and its trailing delimiter has been processed.  Otherwise, offset and length bound the
+            // portion of buffer that represents a contiguous (and possibly partial) chunk of consumable
+            // data; offset+length is the total number of bytes processed from the buffer on this pass.
+            bool process_buffer(uint8_t* buffer, size_t buffer_size, size_t& offset, size_t& length)
+            {
+                bool done = false;
+                size_t n = 0;
+                size_t l = 0;
+
+                while (n < buffer_size)
+                {
+                    if (m_ignore)
+                    {
+                        if (m_expect_linefeed)
+                        {
+                            _ASSERTE(m_chunk_delim && m_trailer);
+                            if (buffer[n] != '\n')
+                            {
+                                // The data stream does not conform to "chunked" encoding
+                                throw http_exception(status_codes::BadRequest, "Transfer-Encoding malformed trailer");
+                            }
+
+                            // Look for further trailer fields or the end of the stream
+                            m_expect_linefeed = false;
+                            m_trailer = false;
+                        }
+                        else if (buffer[n] == '\r')
+                        {
+                            if (!m_trailer)
+                            {
+                                // We're at the end of the data we need to ignore
+                                _ASSERTE(m_chunk_size || m_chunk_delim);
+                                m_ignore = false;
+                                m_chunk_delim = false; // this is only set if we're at the end of the message
+                            }                          // else we're at the end of a trailer field
+                            m_expect_linefeed = true;
+                        }
+                        else if (m_chunk_delim)
+                        {
+                            // We're processing (and ignoring) a trailer field
+                            m_trailer = true;
+                        }
+                    }
+                    else if (m_expect_linefeed)
+                    {
+                        // We've already seen a carriage return; confirm the linefeed
+                        if (buffer[n] != '\n')
+                        {
+                            // The data stream does not conform to "chunked" encoding
+                            throw http_exception(status_codes::BadRequest, "Transfer-Encoding malformed delimiter");
+                        }
+                        if (m_chunk_size)
+                        {
+                            if (!m_bytes_remaining)
+                            {
+                                // We're processing the terminating "empty" chunk; there's
+                                // no data, we just need to confirm the final chunk delimiter,
+                                // possibly ignoring a trailer part along the way
+                                m_ignore = true;
+                                m_chunk_delim = true;
+                            } // else we move on to the chunk data itself
+                            m_chunk_size = false;
+                        }
+                        else
+                        {
+                            // Now we move on to the next chunk size
+                            _ASSERTE(!m_bytes_remaining);
+                            if (m_chunk_delim)
+                            {
+                                // We expect a chunk size next
+                                m_chunk_size = true;
+                            }
+                            else
+                            {
+                                // We just processed the end-of-input delimiter
+                                done = true;
+                            }
+                            m_chunk_delim = false;
+                        }
+                        m_expect_linefeed = false;
+                    }
+                    else if (m_chunk_delim)
+                    {
+                        // We're processing a post-chunk delimiter
+                        if (buffer[n] != '\r')
+                        {
+                            // The data stream does not conform to "chunked" encoding
+                            throw http_exception(status_codes::BadRequest,
+                                                 "Transfer-Encoding malformed chunk delimiter");
+                        }
+
+                        // We found the carriage return; look for the linefeed
+                        m_expect_linefeed = true;
+                    }
+                    else if (m_chunk_size)
+                    {
+                        // We're processing an ASCII hexadecimal chunk size
+                        if (buffer[n] >= 'a' && buffer[n] <= 'f')
+                        {
+                            m_bytes_remaining *= 16;
+                            m_bytes_remaining += 10 + buffer[n] - 'a';
+                        }
+                        else if (buffer[n] >= 'A' && buffer[n] <= 'F')
+                        {
+                            m_bytes_remaining *= 16;
+                            m_bytes_remaining += 10 + buffer[n] - 'A';
+                        }
+                        else if (buffer[n] >= '0' && buffer[n] <= '9')
+                        {
+                            m_bytes_remaining *= 16;
+                            m_bytes_remaining += buffer[n] - '0';
+                        }
+                        else if (buffer[n] == '\r')
+                        {
+                            // We've reached the end of the size, and there's no chunk extension
+                            m_expect_linefeed = true;
+                        }
+                        else if (buffer[n] == ';')
+                        {
+                            // We've reached the end of the size, and there's a chunk extension;
+                            // we don't support extensions, so we ignore them per RFC
+                            m_ignore = true;
+                        }
+                        else
+                        {
+                            // The data stream does not conform to "chunked" encoding
+                            throw http_exception(status_codes::BadRequest,
+                                                 "Transfer-Encoding malformed chunk size or extension");
+                        }
+                    }
+                    else
+                    {
+                        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);
+                            m_bytes_remaining -= l;
+                            if (!m_bytes_remaining)
+                            {
+                                // We're moving on to the post-chunk delimiter
+                                m_chunk_delim = true;
+                            }
+                        }
+                        else
+                        {
+                            // We've previously processed the terminating empty chunk and its
+                            // trailing delimiter; skip the entire buffer, and inform the caller
+                            n = buffer_size;
+                            done = true;
+                        }
+
+                        // Let the caller process the result
+                        break;
+                    }
+
+                    // Move on to the next byte
+                    n++;
+                }
+
+                offset = n;
+                length = l;
+                return buffer_size ? done : (!m_bytes_remaining && !m_chunk_size && !m_chunk_delim);
+            }
+
+        private:
+            size_t m_bytes_remaining; // the number of bytes remaining in the chunk we're currently processing
+            bool m_chunk_size;        // if true, we're processing a chunk size or its trailing delimiter
+            bool m_chunk_delim;       // if true, we're processing a delimiter between a chunk and the next chunk's size
+            bool m_expect_linefeed; // if true, we're processing a delimiter, and we've already seen its carriage return
+            bool m_ignore;          // if true, we're processing a chunk extension or trailer, which we don't support
+            bool m_trailer;         // if true, we're processing (and ignoring) a trailer field; m_ignore is also true
+        };
+
+        std::vector<uint8_t> m_buffer; // we read data from the stream into this before compressing
+        uint8_t* m_acquired; // we use this in place of m_buffer if the stream has directly-accessible data available
+        size_t m_bytes_read; // we most recently read this many bytes, which may be less than m_buffer.size()
+        size_t m_bytes_processed; // we've compressed this many bytes of m_bytes_read so far
+        bool m_needs_flush;   // we've read and compressed all bytes, but the compressor still has compressed bytes to
+                              // give us
+        bool m_started;       // we've sent at least some number of bytes to m_decompressor
+        bool m_done;          // we've read, compressed, and consumed all bytes
+        bool m_chunked;       // if true, we need to decode and decompress a transfer-encoded message
+        size_t m_chunk_bytes; // un-decompressed bytes remaining in the most-recently-obtained data from m_chunk
+        std::unique_ptr<_chunk_helper> m_chunk;
+    } m_compression_state;
+
+    void cleanup()
+    {
+        if (m_compression_state.m_acquired != nullptr)
+        {
+            // We may still hold a piece of the buffer if we encountered an exception; release it here
+            if (m_decompressor)
+            {
+                _get_writebuffer().commit(0);
+            }
+            else
+            {
+                _get_readbuffer().release(m_compression_state.m_acquired, m_compression_state.m_bytes_processed);
+            }
+            m_compression_state.m_acquired = nullptr;
+        }
+
+        if (m_request_handle != nullptr)
+        {
+            WinHttpCloseHandle(m_request_handle);
+        }
+    }
+
+    void install_custom_cn_check(const utility::string_t& customHost)
+    {
+        m_customCnCheck = customHost;
+        utility::details::inplace_tolower(m_customCnCheck);
+    }
+
+    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
+            // certificate checking is completely disabled
+            return;
+        }
+
+        winhttp_cert_context certContext;
+        DWORD bufferSize = sizeof(certContext.raw);
+        if (!WinHttpQueryOption(m_request_handle, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &certContext.raw, &bufferSize))
+        {
+            auto errorCode = GetLastError();
+            if (errorCode == HRESULT_CODE(WININET_E_INCORRECT_HANDLE_STATE))
+            {
+                // typically happens when given a custom host with an initially HTTP connection
+                return;
+            }
+
+            report_error(errorCode,
+                         build_error_msg(errorCode, "WinHttpQueryOption WINHTTP_OPTION_SERVER_CERT_CONTEXT"));
+            cleanup();
+            return;
+        }
+
+        const auto encodedFirst = certContext.raw->pbCertEncoded;
+        const auto encodedLast = encodedFirst + certContext.raw->cbCertEncoded;
+        if (certContext.raw->cbCertEncoded == m_cachedEncodedCert.size() &&
+            std::equal(encodedFirst, encodedLast, m_cachedEncodedCert.begin()))
+        {
+            // already validated OK
+            return;
+        }
+
+        char oidPkixKpServerAuth[] = szOID_PKIX_KP_SERVER_AUTH;
+        char oidServerGatedCrypto[] = szOID_SERVER_GATED_CRYPTO;
+        char oidSgcNetscape[] = szOID_SGC_NETSCAPE;
+        char* chainUses[] = {
+            oidPkixKpServerAuth,
+            oidServerGatedCrypto,
+            oidSgcNetscape,
+        };
+
+        winhttp_cert_chain_context chainContext;
+        CERT_CHAIN_PARA chainPara = {sizeof(chainPara)};
+        chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+        chainPara.RequestedUsage.Usage.cUsageIdentifier = sizeof(chainUses) / sizeof(char*);
+        chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = chainUses;
+
+        // note that the following chain only checks the end certificate; we
+        // assume WinHTTP already validated everything but the common name.
+        if (!CertGetCertificateChain(NULL,
+                                     certContext.raw,
+                                     nullptr,
+                                     certContext.raw->hCertStore,
+                                     &chainPara,
+                                     CERT_CHAIN_CACHE_END_CERT,
+                                     NULL,
+                                     &chainContext.raw))
+        {
+            auto errorCode = GetLastError();
+            report_error(errorCode, build_error_msg(errorCode, "CertGetCertificateChain"));
+            cleanup();
+            return;
+        }
+
+        HTTPSPolicyCallbackData policyData = {
+            {sizeof(policyData)},
+            AUTHTYPE_SERVER,
+            // we assume WinHTTP already checked these:
+            0x00000080       /* SECURITY_FLAG_IGNORE_REVOCATION */
+                | 0x00000100 /* SECURITY_FLAG_IGNORE_UNKNOWN_CA */
+                | 0x00000200 /* SECURITY_FLAG_IGNORE_WRONG_USAGE */
+                | 0x00002000 /* SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */,
+            &m_customCnCheck[0],
+        };
+        CERT_CHAIN_POLICY_PARA policyPara = {sizeof(policyPara)};
+        policyPara.pvExtraPolicyPara = &policyData;
+
+        CERT_CHAIN_POLICY_STATUS policyStatus = {sizeof(policyStatus)};
+        if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext.raw, &policyPara, &policyStatus))
+        {
+            auto errorCode = GetLastError();
+            report_error(errorCode, build_error_msg(errorCode, "CertVerifyCertificateChainPolicy"));
+            cleanup();
+            return;
+        }
+
+        if (policyStatus.dwError)
+        {
+            report_error(policyStatus.dwError, build_error_msg(policyStatus.dwError, "Incorrect common name"));
+            cleanup();
+            return;
+        }
+
+        m_cachedEncodedCert.assign(encodedFirst, encodedLast);
+#endif
+    }
+
+protected:
+    virtual void finish() override
+    {
+        request_context::finish();
+        assert(m_self_reference != nullptr);
+        auto dereference_self = std::move(m_self_reference);
+        // As the stack frame cleans up, this will be deleted if no other references exist.
+    }
+
+private:
+    utility::string_t m_customCnCheck;
+    std::vector<unsigned char> m_cachedEncodedCert;
+
+    // Can only create on the heap using factory function.
+    winhttp_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request)
+        : request_context(client, request)
+        , m_request_handle(nullptr)
+        , 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<uint8_t>::eof())
+        , m_readStream(request.body())
+        , m_body_data()
+    {
+    }
+};
+
+static DWORD ChooseAuthScheme(DWORD dwSupportedSchemes)
+{
+    //  It is the server's responsibility only to accept
+    //  authentication schemes that provide a sufficient
+    //  level of security to protect the servers resources.
+    //
+    //  The client is also obligated only to use an authentication
+    //  scheme that adequately protects its username and password.
+    //
+    if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE)
+        return WINHTTP_AUTH_SCHEME_NEGOTIATE;
+    else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NTLM)
+        return WINHTTP_AUTH_SCHEME_NTLM;
+    else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT)
+        return WINHTTP_AUTH_SCHEME_PASSPORT;
+    else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST)
+        return WINHTTP_AUTH_SCHEME_DIGEST;
+    else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_BASIC)
+        return WINHTTP_AUTH_SCHEME_BASIC;
+    else
+        return 0;
+}
+
+// Small RAII helper to ensure that the fields of this struct are always
+// properly freed.
+struct proxy_info : WINHTTP_PROXY_INFO
+{
+    proxy_info() { memset(this, 0, sizeof(WINHTTP_PROXY_INFO)); }
+
+    ~proxy_info()
+    {
+        if (lpszProxy) ::GlobalFree(lpszProxy);
+        if (lpszProxyBypass) ::GlobalFree(lpszProxyBypass);
+    }
+};
+
+struct ie_proxy_config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
+{
+    ie_proxy_config() { memset(this, 0, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG)); }
+
+    ~ie_proxy_config()
+    {
+        if (lpszAutoConfigUrl) ::GlobalFree(lpszAutoConfigUrl);
+        if (lpszProxy) ::GlobalFree(lpszProxy);
+        if (lpszProxyBypass) ::GlobalFree(lpszProxyBypass);
+    }
+};
+
+// WinHTTP client.
+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_opened(false)
+        , m_hSession(nullptr)
+        , m_hConnection(nullptr)
+        , m_secure(m_uri.scheme() == _XPLATSTR("https"))
+    {
+    }
+
+    winhttp_client(const winhttp_client&) = delete;
+    winhttp_client& operator=(const winhttp_client&) = delete;
+
+    // Closes session.
+    ~winhttp_client()
+    {
+        if (m_hConnection != nullptr)
+        {
+            WinHttpCloseHandle(m_hConnection);
+        }
+
+        if (m_hSession != nullptr)
+        {
+            // Unregister the callback.
+            WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
+
+            WinHttpCloseHandle(m_hSession);
+        }
+    }
+
+    virtual pplx::task<http_response> propagate(http_request request) override
+    {
+        auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this());
+        auto context = details::winhttp_request_context::create_request_context(self, request);
+
+        // Use a task to externally signal the final result and completion of the task.
+        auto result_task = pplx::create_task(context->m_request_completion);
+
+        // Asynchronously send the response with the HTTP client implementation.
+        this->async_send_request(context);
+
+        return result_task;
+    }
+
+protected:
+    // Open session and connection with the server.
+    unsigned long open()
+    {
+        if (m_opened)
+        {
+            return 0;
+        }
+
+        pplx::extensibility::scoped_critical_section_t l(m_client_lock);
+        if (m_opened)
+        {
+            return 0;
+        }
+
+        // This object have lifetime greater than proxy_name and proxy_bypass
+        // which may point to its elements.
+        ie_proxy_config proxyIE;
+
+        DWORD access_type;
+        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();
+        const auto& proxy = config.proxy();
+        if (proxy.is_default())
+        {
+            access_type = WinHttpDefaultProxyConstant();
+        }
+        else if (proxy.is_disabled())
+        {
+            access_type = WINHTTP_ACCESS_TYPE_NO_PROXY;
+        }
+        else if (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;
+
+                proxy_info proxyDefault;
+                if (!WinHttpGetDefaultProxyConfiguration(&proxyDefault) ||
+                    proxyDefault.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY)
+                {
+                    // ... then try to fall back on the default WinINET proxy, as
+                    // recommended for the desktop applications (if we're not
+                    // running under a user account, the function below will just
+                    // fail, so there is no real need to check for this explicitly)
+                    if (WinHttpGetIEProxyConfigForCurrentUser(&proxyIE))
+                    {
+                        if (proxyIE.fAutoDetect)
+                        {
+                            m_proxy_auto_config = true;
+                        }
+                        else if (proxyIE.lpszAutoConfigUrl)
+                        {
+                            m_proxy_auto_config = true;
+                            m_proxy_auto_config_url = proxyIE.lpszAutoConfigUrl;
+                        }
+                        else if (proxyIE.lpszProxy)
+                        {
+                            access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+                            proxy_name = proxyIE.lpszProxy;
+
+                            if (proxyIE.lpszProxyBypass)
+                            {
+                                proxy_bypass = proxyIE.lpszProxyBypass;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            _ASSERTE(config.proxy().is_specified());
+            access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+            // WinHttpOpen cannot handle trailing slash in the name, so here is some string gymnastics to keep
+            // WinHttpOpen happy proxy_str is intentionally declared at the function level to avoid pointing to the
+            // string in the destructed object
+            uri = config.proxy().address();
+            if (uri.is_port_default())
+            {
+                proxy_name = uri.host().c_str();
+            }
+            else
+            {
+                proxy_str = uri.host();
+                if (uri.port() > 0)
+                {
+                    proxy_str.push_back(_XPLATSTR(':'));
+                    proxy_str.append(::utility::conversions::details::to_string_t(uri.port()));
+                }
+
+                proxy_name = proxy_str.c_str();
+            }
+        }
+
+        // Open session.
+        m_hSession = WinHttpOpen(NULL, access_type, proxy_name, proxy_bypass, WINHTTP_FLAG_ASYNC);
+        if (!m_hSession)
+        {
+            return GetLastError();
+        }
+
+        {
+            // Set timeouts.
+            const int milliseconds =
+                (std::max)(static_cast<int>(config.timeout<std::chrono::milliseconds>().count()), 1);
+            if (!WinHttpSetTimeouts(m_hSession, milliseconds, milliseconds, milliseconds, milliseconds))
+            {
+                return GetLastError();
+            }
+        }
+
+        if (config.guarantee_order())
+        {
+            // Set max connection to use per server to 1.
+            DWORD maxConnections = 1;
+            if (!WinHttpSetOption(
+                    m_hSession, WINHTTP_OPTION_MAX_CONNS_PER_SERVER, &maxConnections, sizeof(maxConnections)))
+            {
+                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);
+
+        // Register asynchronous callback.
+        if (WINHTTP_INVALID_STATUS_CALLBACK ==
+            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_STATUS_REDIRECT,
+                                     0))
+        {
+            return GetLastError();
+        }
+
+        // Open connection.
+        unsigned int port = m_uri.is_port_default()
+                                ? (m_secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT)
+                                : m_uri.port();
+        m_hConnection = WinHttpConnect(m_hSession, m_uri.host().c_str(), (INTERNET_PORT)port, 0);
+
+        if (m_hConnection == nullptr)
+        {
+            return GetLastError();
+        }
+
+        m_opened = true;
+        return S_OK;
+    }
+
+    // Start sending request.
+    void send_request(_In_ const std::shared_ptr<request_context>& request)
+    {
+        // First see if we need to be opened.
+        unsigned long error = open();
+        if (error != 0)
+        {
+            // DO NOT TOUCH the this pointer after completing the request
+            // This object could be freed along with the request as it could
+            // be the last reference to this object
+            request->report_error(error, _XPLATSTR("Open failed"));
+            return;
+        }
+
+        http_request& msg = request->m_request;
+        http_headers& headers = msg.headers();
+        std::shared_ptr<winhttp_request_context> winhttp_context =
+            std::static_pointer_cast<winhttp_request_context>(request);
+        std::weak_ptr<winhttp_request_context> weak_winhttp_context = winhttp_context;
+
+        proxy_info info;
+        bool proxy_info_required = false;
+
+        const auto& method = msg.method();
+
+        // stop injection of headers via method
+        // resource should be ok, since it's been encoded
+        // and host won't resolve
+        if (!::web::http::details::validate_method(method))
+        {
+            request->report_exception(http_exception("The method string is invalid."));
+            return;
+        }
+
+        if (m_proxy_auto_config)
+        {
+            WINHTTP_AUTOPROXY_OPTIONS autoproxy_options {};
+            if (m_proxy_auto_config_url.empty())
+            {
+                autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
+                autoproxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+            }
+            else
+            {
+                autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
+                autoproxy_options.lpszAutoConfigUrl = m_proxy_auto_config_url.c_str();
+            }
+
+            autoproxy_options.fAutoLogonIfChallenged = TRUE;
+
+            auto result = WinHttpGetProxyForUrl(m_hSession, m_uri.to_string().c_str(), &autoproxy_options, &info);
+            if (result)
+            {
+                proxy_info_required = true;
+            }
+            else
+            {
+                // Failure to download the auto-configuration script is not fatal. Fall back to the default proxy.
+            }
+        }
+
+        // Need to form uri path, query, and fragment for this request.
+        // Make sure to keep any path that was specified with the uri when the http_client was created.
+        const utility::string_t encoded_resource =
+            http::uri_builder(m_uri).append(msg.relative_uri()).to_uri().resource().to_string();
+
+        // Open the request.
+        winhttp_context->m_request_handle_context = new std::weak_ptr<winhttp_request_context>(winhttp_context);
+
+        winhttp_context->m_request_handle =
+            WinHttpOpenRequest(m_hConnection,
+                               msg.method().c_str(),
+                               encoded_resource.c_str(),
+                               nullptr,
+                               WINHTTP_NO_REFERER,
+                               WINHTTP_DEFAULT_ACCEPT_TYPES,
+                               WINHTTP_FLAG_ESCAPE_DISABLE | (m_secure ? WINHTTP_FLAG_SECURE : 0));
+        if (winhttp_context->m_request_handle == nullptr)
+        {
+            auto errorCode = GetLastError();
+            delete winhttp_context->m_request_handle_context;
+            winhttp_context->m_request_handle_context = 0;
+            request->report_error(errorCode, build_error_msg(errorCode, "WinHttpOpenRequest"));
+            return;
+        }
+
+        if (!WinHttpSetOption(winhttp_context->m_request_handle,
+                              WINHTTP_OPTION_CONTEXT_VALUE,
+                              &winhttp_context->m_request_handle_context,
+                              sizeof(void*)))
+        {
+            auto errorCode = GetLastError();
+            delete winhttp_context->m_request_handle_context;
+            winhttp_context->m_request_handle_context = 0;
+            request->report_error(errorCode, build_error_msg(errorCode, "WinHttpSetOption request context"));
+            return;
+        }
+
+        if (proxy_info_required)
+        {
+            auto result = WinHttpSetOption(
+                winhttp_context->m_request_handle, WINHTTP_OPTION_PROXY, &info, sizeof(WINHTTP_PROXY_INFO));
+            if (!result)
+            {
+                auto errorCode = GetLastError();
+                request->report_error(errorCode, build_error_msg(errorCode, "Setting proxy options"));
+                return;
+            }
+        }
+
+        // If credentials are specified, use autologon policy: WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH
+        //    => default credentials are not used.
+        // Else, the default autologon policy WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM will be used.
+        if (client_config().credentials().is_set())
+        {
+            DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
+
+            auto result = WinHttpSetOption(
+                winhttp_context->m_request_handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(data));
+            if (!result)
+            {
+                auto errorCode = GetLastError();
+                request->report_error(
+                    errorCode,
+                    build_error_msg(errorCode, "Setting autologon policy to WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH"));
+                return;
+            }
+        }
+
+        // Check to turn off server certificate verification.
+        DWORD ignoredCertificateValidationSteps = 0;
+        if (client_config().validate_certificates())
+        {
+            // if we are validating certificates, also turn on revocation checking
+            DWORD dwEnableSSLRevocationOpt = WINHTTP_ENABLE_SSL_REVOCATION;
+            if (!WinHttpSetOption(winhttp_context->m_request_handle,
+                                  WINHTTP_OPTION_ENABLE_FEATURE,
+                                  &dwEnableSSLRevocationOpt,
+                                  sizeof(dwEnableSSLRevocationOpt)))
+            {
+                auto errorCode = GetLastError();
+                request->report_error(errorCode, build_error_msg(errorCode, "Error enabling SSL revocation check"));
+                return;
+            }
+
+            // check if the user has overridden the desired Common Name with the host header
+            const auto hostHeader = headers.find(_XPLATSTR("Host"));
+            if (hostHeader != headers.end())
+            {
+                const auto& requestHost = hostHeader->second;
+                if (!utility::details::str_iequal(requestHost, m_uri.host()))
+                {
+                    winhttp_context->install_custom_cn_check(requestHost);
+                    ignoredCertificateValidationSteps = SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
+                }
+            }
+        }
+        else
+        {
+            ignoredCertificateValidationSteps =
+                SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
+                SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
+        }
+
+        if (ignoredCertificateValidationSteps && !WinHttpSetOption(winhttp_context->m_request_handle,
+                                                                   WINHTTP_OPTION_SECURITY_FLAGS,
+                                                                   &ignoredCertificateValidationSteps,
+                                                                   sizeof(ignoredCertificateValidationSteps)))
+        {
+            auto errorCode = GetLastError();
+            request->report_error(errorCode,
+                                  build_error_msg(errorCode, "Setting ignore server certificate verification"));
+            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<DWORD>(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
+        {
+            content_length = msg._get_impl()->_get_content_length_and_set_compression();
+        }
+        catch (...)
+        {
+            request->report_exception(std::current_exception());
+            return;
+        }
+        if (content_length > 0)
+        {
+            if (msg.method() == http::methods::GET || msg.method() == http::methods::HEAD)
+            {
+                request->report_exception(http_exception(get_with_body_err_msg));
+                return;
+            }
+
+            // There is a request body that needs to be transferred.
+            if (content_length == (std::numeric_limits<size_t>::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
+                // know that stream's effective length if possible for memory efficiency.
+                winhttp_context->m_bodyType = transfer_encoding_chunked;
+                winhttp_context->m_remaining_to_write = msg._get_impl()->_get_stream_length();
+            }
+            else
+            {
+                // While we won't be transfer-encoding the data, we will write it in portions.
+                winhttp_context->m_bodyType = content_length_chunked;
+                winhttp_context->m_remaining_to_write = content_length;
+            }
+        }
+
+        utility::string_t flattened_headers = web::http::details::flatten_http_headers(headers);
+        if (winhttp_context->m_request.method() == http::methods::GET)
+        {
+            // Prepare to request a compressed response from the server if necessary.
+            flattened_headers += winhttp_context->get_compression_header();
+        }
+
+        // Add headers.
+        if (!flattened_headers.empty())
+        {
+            if (!WinHttpAddRequestHeaders(winhttp_context->m_request_handle,
+                                          flattened_headers.c_str(),
+                                          static_cast<DWORD>(flattened_headers.length()),
+                                          WINHTTP_ADDREQ_FLAG_ADD))
+            {
+                auto errorCode = GetLastError();
+                request->report_error(errorCode, build_error_msg(errorCode, "WinHttpAddRequestHeaders"));
+                return;
+            }
+        }
+
+        // Register for notification on cancellation to abort this request.
+        if (msg._cancellation_token() != pplx::cancellation_token::none())
+        {
+            // cancellation callback is unregistered when request is completed.
+            winhttp_context->m_cancellationRegistration =
+                msg._cancellation_token().register_callback([weak_winhttp_context]() {
+                    // Call the WinHttpSendRequest API after WinHttpCloseHandle will give invalid handle error and we
+                    // throw this exception. Call the cleanup to make the m_request_handle as nullptr, otherwise,
+                    // Application Verifier will give AV exception on m_request_handle.
+                    auto lock = weak_winhttp_context.lock();
+                    if (!lock) return;
+                    lock->cleanup();
+                });
+        }
+
+        // Call the callback function of user customized options.
+        try
+        {
+            client_config().invoke_nativehandle_options(winhttp_context->m_request_handle);
+        }
+        catch (...)
+        {
+            request->report_exception(std::current_exception());
+            return;
+        }
+
+        // Only need to cache the request body if user specified and the request stream doesn't support seeking.
+        if (winhttp_context->m_bodyType != no_body && client_config().buffer_request() &&
+            !winhttp_context->_get_readbuffer().can_seek())
+        {
+            winhttp_context->m_readBufferCopy =
+                ::utility::details::make_unique<::concurrency::streams::container_buffer<std::vector<uint8_t>>>();
+        }
+
+        _start_request_send(winhttp_context, content_length);
+
+        return;
+    }
+
+private:
+    void _start_request_send(const std::shared_ptr<winhttp_request_context>& winhttp_context, size_t content_length)
+    {
+        DWORD totalLength;
+        if (winhttp_context->m_bodyType == no_body)
+        {
+            totalLength = 0;
+        }
+        else
+        {
+            // Capture the current read position of the stream.
+            auto rbuf = winhttp_context->_get_readbuffer();
+
+            // Record starting position in case request is challenged for authorization
+            // and needs to seek back to where reading is started from.
+            winhttp_context->m_startingPosition = rbuf.getpos(std::ios_base::in);
+
+            // If we find ourselves here, we either don't know how large the message
+            totalLength = winhttp_context->m_bodyType == content_length_chunked ? (DWORD)content_length
+                                                                                : WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH;
+        }
+
+        const auto requestSuccess = WinHttpSendRequest(winhttp_context->m_request_handle,
+                                                       WINHTTP_NO_ADDITIONAL_HEADERS,
+                                                       0,
+                                                       nullptr,
+                                                       0,
+                                                       totalLength,
+                                                       (DWORD_PTR)winhttp_context->m_request_handle_context);
+        if (!requestSuccess)
+        {
+            auto errorCode = GetLastError();
+            winhttp_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpSendRequest"));
+        }
+    }
+
+    // Helper function to query/read next part of response data from winhttp.
+    static void read_next_response_chunk(winhttp_request_context* pContext, DWORD bytesRead, bool firstRead = false)
+    {
+        const bool defaultChunkSize = pContext->m_http_client->client_config().is_default_chunksize();
+
+        // If user specified a chunk size then read in chunks instead of using query data available.
+        if (defaultChunkSize)
+        {
+            if (!WinHttpQueryDataAvailable(pContext->m_request_handle, nullptr))
+            {
+                auto errorCode = GetLastError();
+                pContext->report_error(errorCode, build_error_msg(errorCode, "WinHttpQueryDataAvaliable"));
+            }
+        }
+        else
+        {
+            // If bytes read is less than the chunk size this request is done.
+            // Is it really, though?  The WinHttpReadData docs suggest that less can be returned regardless...
+            const size_t chunkSize = pContext->m_http_client->client_config().chunksize();
+            std::unique_ptr<compression::decompress_provider>& decompressor = pContext->m_decompressor;
+            if (!decompressor && bytesRead < chunkSize && !firstRead)
+            {
+                pContext->complete_request(pContext->m_downloaded);
+            }
+            else
+            {
+                uint8_t* buffer;
+
+                if (decompressor)
+                {
+                    // m_buffer holds the compressed data; we'll decompress into the caller's buffer later
+                    if (pContext->m_compression_state.m_buffer.capacity() < chunkSize)
+                    {
+                        pContext->m_compression_state.m_buffer.reserve(chunkSize);
+                    }
+                    buffer = pContext->m_compression_state.m_buffer.data();
+                }
+                else
+                {
+                    auto writebuf = pContext->_get_writebuffer();
+                    pContext->allocate_reply_space(writebuf.alloc(chunkSize), chunkSize);
+                    buffer = pContext->m_body_data.get();
+                }
+
+                if (!WinHttpReadData(pContext->m_request_handle, buffer, static_cast<DWORD>(chunkSize), nullptr))
+                {
+                    auto errorCode = GetLastError();
+                    pContext->report_error(errorCode, build_error_msg(errorCode, "WinHttpReadData"));
+                }
+            }
+        }
+    }
+
+    static void _transfer_encoding_chunked_write_data(_In_ winhttp_request_context* p_request_context)
+    {
+        size_t chunk_size;
+        std::unique_ptr<compression::compress_provider>& compressor = p_request_context->m_request.compressor();
+
+        // Set the chunk size up front; we need it before the lambda functions come into scope
+        if (compressor)
+        {
+            // We could allocate less than a chunk for the compressed data here, though that
+            // would result in more trips through this path for not-so-compressible data...
+            if (p_request_context->m_body_data.size() > http::details::chunked_encoding::additional_encoding_space)
+            {
+                // If we've previously allocated space for the compressed data, don't reduce it
+                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<size_t>::max)())
+            {
+                // Choose a semi-intelligent size based on how much total data is left to compress
+                chunk_size = (std::min)(static_cast<size_t>(p_request_context->m_remaining_to_write) + 128,
+                                        p_request_context->m_http_client->client_config().chunksize());
+            }
+            else
+            {
+                // Just base our allocation on the chunk size, since we don't have any other data available
+                chunk_size = p_request_context->m_http_client->client_config().chunksize();
+            }
+        }
+        else
+        {
+            // 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<size_t>(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);
+
+        auto after_read = [p_request_context, chunk_size, &compressor](pplx::task<size_t> op) {
+            size_t bytes_read;
+            try
+            {
+                bytes_read = op.get();
+                // If the read buffer for copying exists then write to it.
+                if (p_request_context->m_readBufferCopy)
+                {
+                    // We have raw memory here writing to a memory stream so it is safe to wait
+                    // since it will always be non-blocking.
+                    if (!compressor)
+                    {
+                        p_request_context->m_readBufferCopy
+                            ->putn_nocopy(
+                                &p_request_context->m_body_data.get()[http::details::chunked_encoding::data_offset],
+                                bytes_read)
+                            .wait();
+                    }
+                }
+            }
+            catch (...)
+            {
+                p_request_context->report_exception(std::current_exception());
+                return;
+            }
+
+            _ASSERTE(bytes_read != static_cast<size_t>(-1));
+
+            size_t offset = http::details::chunked_encoding::add_chunked_delimiters(
+                p_request_context->m_body_data.get(),
+                chunk_size + http::details::chunked_encoding::additional_encoding_space,
+                bytes_read);
+
+            if (!compressor && p_request_context->m_remaining_to_write != (std::numeric_limits<size_t>::max)())
+            {
+                if (bytes_read == 0 && p_request_context->m_remaining_to_write)
+                {
+                    // The stream ended earlier than we detected it should
+                    http_exception ex(
+                        U("Unexpected end of request body stream encountered before expected length met."));
+                    p_request_context->report_exception(ex);
+                    return;
+                }
+                p_request_context->m_remaining_to_write -= bytes_read;
+            }
+
+            // Stop writing chunks if we reached the end of the stream.
+            // Note that we could detect end-of-stream based on !m_remaining_to_write, and insert
+            // the last (0) chunk if we have enough extra space... though we currently don't.
+            if (bytes_read == 0)
+            {
+                p_request_context->m_bodyType = no_body;
+                if (p_request_context->m_readBufferCopy)
+                {
+                    // Move the saved buffer into the read buffer, which now supports seeking.
+                    p_request_context->m_readStream =
+                        concurrency::streams::container_stream<std::vector<uint8_t>>::open_istream(
+                            std::move(p_request_context->m_readBufferCopy->collection()));
+                    p_request_context->m_readBufferCopy.reset();
+                }
+            }
+
+            const auto length = bytes_read + (http::details::chunked_encoding::additional_encoding_space - offset);
+
+            if (!WinHttpWriteData(p_request_context->m_request_handle,
+                                  &p_request_context->m_body_data.get()[offset],
+                                  static_cast<DWORD>(length),
+                                  nullptr))
+            {
+                auto errorCode = GetLastError();
+                p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData"));
+            }
+        };
+
+        if (compressor)
+        {
+            auto do_compress =
+                [p_request_context, chunk_size, &compressor](pplx::task<size_t> op) -> pplx::task<size_t> {
+                size_t bytes_read;
+
+                try
+                {
+                    bytes_read = op.get();
+                }
+                catch (...)
+                {
+                    return pplx::task_from_exception<size_t>(std::current_exception());
+                }
+
+                uint8_t* buffer = p_request_context->m_compression_state.m_acquired;
+                if (buffer == nullptr)
+                {
+                    buffer = p_request_context->m_compression_state.m_buffer.data();
+                }
+
+                web::http::compression::operation_hint hint = web::http::compression::operation_hint::has_more;
+
+                if (bytes_read)
+                {
+                    // An actual read always resets compression state for the next chunk
+                    _ASSERTE(p_request_context->m_compression_state.m_bytes_processed ==
+                             p_request_context->m_compression_state.m_bytes_read);
+                    _ASSERTE(!p_request_context->m_compression_state.m_needs_flush);
+                    p_request_context->m_compression_state.m_bytes_read = bytes_read;
+                    p_request_context->m_compression_state.m_bytes_processed = 0;
+                    if (p_request_context->m_readBufferCopy)
+                    {
+                        // If we've been asked to keep a copy of the raw data for restarts, do so here, pre-compression
+                        p_request_context->m_readBufferCopy->putn_nocopy(buffer, bytes_read).wait();
+                    }
+                    if (p_request_context->m_remaining_to_write == bytes_read)
+                    {
+                        // We've read to the end of the stream; finalize here if possible.  We'll
+                        // decrement the remaining count as we actually process the read buffer.
+                        hint = web::http::compression::operation_hint::is_last;
+                    }
+                }
+                else if (p_request_context->m_compression_state.m_needs_flush)
+                {
+                    // All input has been consumed, but we still need to collect additional compressed output;
+                    // this is done (in theory it can be multiple times) as a finalizing operation
+                    hint = web::http::compression::operation_hint::is_last;
+                }
+                else if (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 &&
+                        p_request_context->m_remaining_to_write != (std::numeric_limits<size_t>::max)())
+                    {
+                        // The stream ended earlier than we detected it should
+                        return pplx::task_from_exception<size_t>(http_exception(
+                            U("Unexpected end of request body stream encountered before expected length met.")));
+                    }
+
+                    // We think we're done; inform the compression library so it can finalize and/or give us any pending
+                    // compressed bytes. Note that we may end up here multiple times if m_needs_flush is set, until all
+                    // compressed bytes are drained.
+                    hint = web::http::compression::operation_hint::is_last;
+                }
+                // else we're still compressing bytes from the previous read
+
+                _ASSERTE(p_request_context->m_compression_state.m_bytes_processed <=
+                         p_request_context->m_compression_state.m_bytes_read);
+
+                uint8_t* in = buffer + p_request_context->m_compression_state.m_bytes_processed;
+                size_t inbytes = p_request_context->m_compression_state.m_bytes_read -
+                                 p_request_context->m_compression_state.m_bytes_processed;
+                return compressor
+                    ->compress(in,
+                               inbytes,
+                               &p_request_context->m_body_data.get()[http::details::chunked_encoding::data_offset],
+                               chunk_size,
+                               hint)
+                    .then([p_request_context, bytes_read, hint, chunk_size](
+                              pplx::task<http::compression::operation_result> op) -> pplx::task<size_t> {
+                        http::compression::operation_result r;
+
+                        try
+                        {
+                            r = op.get();
+                        }
+                        catch (...)
+                        {
+                            return pplx::task_from_exception<size_t>(std::current_exception());
+                        }
+
+                        if (hint == web::http::compression::operation_hint::is_last)
+                        {
+                            // We're done reading all chunks, but the compressor may still have compressed bytes to
+                            // drain from previous reads
+                            _ASSERTE(r.done || r.output_bytes_produced == chunk_size);
+                            p_request_context->m_compression_state.m_needs_flush = !r.done;
+                            p_request_context->m_compression_state.m_done = r.done;
+                        }
+
+                        // Update the number of bytes compressed in this read chunk; if it's been fully compressed,
+                        // we'll reset m_bytes_processed and m_bytes_read after reading the next chunk
+                        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<size_t>::max)())
+                        {
+                            _ASSERTE(p_request_context->m_remaining_to_write >= r.input_bytes_processed);
+                            p_request_context->m_remaining_to_write -= r.input_bytes_processed;
+                        }
+
+                        if (p_request_context->m_compression_state.m_acquired != nullptr &&
+                            p_request_context->m_compression_state.m_bytes_processed ==
+                                p_request_context->m_compression_state.m_bytes_read)
+                        {
+                            // Release the acquired buffer back to the streambuf at the earliest possible point
+                            p_request_context->_get_readbuffer().release(
+                                p_request_context->m_compression_state.m_acquired,
+                                p_request_context->m_compression_state.m_bytes_processed);
+                            p_request_context->m_compression_state.m_acquired = nullptr;
+                        }
+
+                        return pplx::task_from_result<size_t>(r.output_bytes_produced);
+                    });
+            };
+
+            if (p_request_context->m_compression_state.m_bytes_processed <
+                    p_request_context->m_compression_state.m_bytes_read ||
+                p_request_context->m_compression_state.m_needs_flush)
+            {
+                // We're still working on data from a previous read; continue compression without reading new data
+                do_compress(pplx::task_from_result<size_t>(0)).then(after_read);
+            }
+            else if (p_request_context->m_compression_state.m_done)
+            {
+                // We just need to send the last (zero-length) chunk; there's no sense in going through the compression
+                // path
+                after_read(pplx::task_from_result<size_t>(0));
+            }
+            else
+            {
+                size_t length;
+
+                // We need to read from the input stream, then compress before sending
+                if (p_request_context->_get_readbuffer().acquire(p_request_context->m_compression_state.m_acquired,
+                                                                 length))
+                {
+                    if (length == 0)
+                    {
+                        if (p_request_context->_get_readbuffer().exception())
+                        {
+                            p_request_context->report_exception(p_request_context->_get_readbuffer().exception());
+                            return;
+                        }
+                        else if (p_request_context->m_remaining_to_write &&
+                                 p_request_context->m_remaining_to_write != (std::numeric_limits<size_t>::max)())
+                        {
+                            // Unexpected end-of-stream.
+                            p_request_context->report_error(GetLastError(),
+                                                            _XPLATSTR("Outgoing HTTP body stream ended early."));
+                            return;
+                        }
+                    }
+                    else if (length > p_request_context->m_remaining_to_write)
+                    {
+                        // The stream grew, but we won't
+                        length = static_cast<size_t>(p_request_context->m_remaining_to_write);
+                    }
+
+                    do_compress(pplx::task_from_result<size_t>(length)).then(after_read);
+                }
+                else
+                {
+                    length = (std::min)(static_cast<size_t>(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);
+                    }
+                    p_request_context->_get_readbuffer()
+                        .getn(p_request_context->m_compression_state.m_buffer.data(), length)
+                        .then(do_compress)
+                        .then(after_read);
+                }
+            }
+        }
+        else
+        {
+            // We're not compressing; just read and chunk
+            p_request_context->_get_readbuffer()
+                .getn(&p_request_context->m_body_data.get()[http::details::chunked_encoding::data_offset], chunk_size)
+                .then(after_read);
+        }
+    }
+
+    static void _multiple_segment_write_data(_In_ winhttp_request_context* p_request_context)
+    {
+        auto rbuf = p_request_context->_get_readbuffer();
+        msl::safeint3::SafeInt<utility::size64_t> safeCount = p_request_context->m_remaining_to_write;
+        safeCount = safeCount.Min(p_request_context->m_http_client->client_config().chunksize());
+
+        uint8_t* block = nullptr;
+        size_t length = 0;
+        if (rbuf.acquire(block, length))
+        {
+            if (length == 0)
+            {
+                // Unexpected end-of-stream.
+                if (rbuf.exception() == nullptr)
+                {
+                    p_request_context->report_error(GetLastError(),
+                                                    _XPLATSTR("Error reading outgoing HTTP body from its stream."));
+                }
+                else
+                {
+                    p_request_context->report_exception(rbuf.exception());
+                }
+                return;
+            }
+
+            p_request_context->allocate_request_space(block, length);
+
+            const size_t to_write = safeCount.Min(length);
+
+            // Stop writing chunks after this one if no more data.
+            p_request_context->m_remaining_to_write -= to_write;
+            if (p_request_context->m_remaining_to_write == 0)
+            {
+                p_request_context->m_bodyType = no_body;
+            }
+
+            if (!WinHttpWriteData(p_request_context->m_request_handle,
+                                  p_request_context->m_body_data.get(),
+                                  static_cast<DWORD>(to_write),
+                                  nullptr))
+            {
+                auto errorCode = GetLastError();
+                p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData"));
+            }
+        }
+        else
+        {
+            p_request_context->allocate_request_space(nullptr, safeCount);
+
+            rbuf.getn(p_request_context->m_body_data.get(), safeCount)
+                .then([p_request_context, rbuf](pplx::task<size_t> op) {
+                    size_t read;
+                    try
+                    {
+                        read = op.get();
+                    }
+                    catch (...)
+                    {
+                        p_request_context->report_exception(std::current_exception());
+                        return;
+                    }
+                    _ASSERTE(read != static_cast<size_t>(-1));
+
+                    if (read == 0)
+                    {
+                        p_request_context->report_exception(http_exception(
+                            U("Unexpected end of request body stream encountered before Content-Length met.")));
+                        return;
+                    }
+
+                    p_request_context->m_remaining_to_write -= read;
+
+                    // Stop writing chunks after this one if no more data.
+                    if (p_request_context->m_remaining_to_write == 0)
+                    {
+                        p_request_context->m_bodyType = no_body;
+                    }
+
+                    if (!WinHttpWriteData(p_request_context->m_request_handle,
+                                          p_request_context->m_body_data.get(),
+                                          static_cast<DWORD>(read),
+                                          nullptr))
+                    {
+                        auto errorCode = GetLastError();
+                        p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData"));
+                    }
+                });
+        }
+    }
+
+    static utility::string_t get_request_url(HINTERNET hRequestHandle)
+    {
+        utility::string_t url;
+        auto urlSize = static_cast<unsigned long>(url.capacity()) * 2; // use initial small string optimization capacity
+        for (;;)
+        {
+            url.resize(urlSize / sizeof(utility::char_t));
+            if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], (LPDWORD)&urlSize))
+            {
+                url.resize(url.length());
+                return url;
+            }
+
+            const auto lastError = GetLastError();
+            if (lastError != ERROR_INSUFFICIENT_BUFFER || urlSize == 0)
+            {
+                url.clear();
+                url.shrink_to_fit();
+                return url;
+            }
+        }
+    }
+
+    // Returns true if we handle successfully and resending the request
+    // or false if we fail to handle.
+    static bool handle_authentication_failure(HINTERNET hRequestHandle,
+                                              const std::shared_ptr<winhttp_request_context>& p_request_context,
+                                              _In_ DWORD error = 0)
+    {
+        http_request& request = p_request_context->m_request;
+
+        _ASSERTE(p_request_context->m_response.status_code() == status_codes::Unauthorized ||
+                 p_request_context->m_response.status_code() == status_codes::ProxyAuthRequired ||
+                 error == ERROR_WINHTTP_RESEND_REQUEST);
+
+        // Check if the saved read position is valid
+        auto rdpos = p_request_context->m_startingPosition;
+        if (rdpos != static_cast<std::char_traits<uint8_t>::pos_type>(std::char_traits<uint8_t>::eof()))
+        {
+            // Try to seek back to the saved read position
+            auto rbuf = p_request_context->_get_readbuffer();
+            if (rbuf.seekpos(rdpos, std::ios::ios_base::in) != rdpos)
+            {
+                return false;
+            }
+
+            // We successfully seeked back; now reset the compression state, if any, to match
+            if (p_request_context->m_request.compressor())
+            {
+                try
+                {
+                    p_request_context->m_request.compressor()->reset();
+                }
+                catch (...)
+                {
+                    return false;
+                }
+            }
+        }
+        p_request_context->m_compression_state = winhttp_request_context::compression_state();
+
+        //  If we got ERROR_WINHTTP_RESEND_REQUEST, the response header is not available,
+        //  we cannot call WinHttpQueryAuthSchemes and WinHttpSetCredentials.
+        if (error != ERROR_WINHTTP_RESEND_REQUEST)
+        {
+            // Obtain the supported and preferred schemes.
+            DWORD dwSupportedSchemes;
+            DWORD dwFirstScheme;
+            DWORD dwAuthTarget;
+            if (!WinHttpQueryAuthSchemes(hRequestHandle, &dwSupportedSchemes, &dwFirstScheme, &dwAuthTarget))
+            {
+                // This will return the authentication failure to the user, without reporting fatal errors
+                return false;
+            }
+
+            DWORD dwSelectedScheme = ChooseAuthScheme(dwSupportedSchemes);
+            if (dwSelectedScheme == 0)
+            {
+                // This will return the authentication failure to the user, without reporting fatal errors
+                return false;
+            }
+
+            credentials cred;
+            if (dwAuthTarget == WINHTTP_AUTH_TARGET_SERVER && !p_request_context->m_server_authentication_tried)
+            {
+                cred = p_request_context->m_http_client->client_config().credentials();
+                p_request_context->m_server_authentication_tried = true;
+            }
+            else if (dwAuthTarget == WINHTTP_AUTH_TARGET_PROXY)
+            {
+                bool is_redirect = false;
+                try
+                {
+                    web::uri current_uri(get_request_url(hRequestHandle));
+                    is_redirect = p_request_context->m_request.absolute_uri().to_string() != current_uri.to_string();
+                }
+                catch (const std::exception&)
+                {
+                }
+
+                // If we have been redirected, then WinHttp needs the proxy credentials again to make the next request
+                // leg (which may be on a different server)
+                if (is_redirect || !p_request_context->m_proxy_authentication_tried)
+                {
+                    cred = p_request_context->m_http_client->client_config().proxy().credentials();
+                    p_request_context->m_proxy_authentication_tried = true;
+                }
+            }
+
+            // No credentials found so can't resend.
+            if (!cred.is_set())
+            {
+                return false;
+            }
+
+            // New scope to ensure plaintext password is cleared as soon as possible.
+            {
+                auto password = cred._internal_decrypt();
+                if (!WinHttpSetCredentials(hRequestHandle,
+                                           dwAuthTarget,
+                                           dwSelectedScheme,
+                                           cred.username().c_str(),
+                                           password->c_str(),
+                                           nullptr))
+                {
+                    return false;
+                }
+            }
+        }
+
+        // Reset the request body type since it might have already started sending.
+        size_t content_length;
+        try
+        {
+            content_length = request._get_impl()->_get_content_length_and_set_compression();
+        }
+        catch (...)
+        {
+            return false;
+        }
+
+        if (content_length > 0)
+        {
+            // There is a request body that needs to be transferred.
+            if (content_length == (std::numeric_limits<size_t>::max)())
+            {
+                // The content length is unknown and the application set a stream. This is an
+                // indication that we will need to chunk the data.
+                p_request_context->m_bodyType = transfer_encoding_chunked;
+                p_request_context->m_remaining_to_write = request._get_impl()->_get_stream_length();
+            }
+            else
+            {
+                // While we won't be transfer-encoding the data, we will write it in portions.
+                p_request_context->m_bodyType = content_length_chunked;
+                p_request_context->m_remaining_to_write = content_length;
+            }
+        }
+        else
+        {
+            p_request_context->m_bodyType = no_body;
+        }
+
+        // We're good.
+        winhttp_client* winclnt = reinterpret_cast<winhttp_client*>(p_request_context->m_http_client.get());
+        winclnt->_start_request_send(p_request_context, content_length);
+
+        // We will not complete the request. Instead wait for the response to the request that was resent
+        return true;
+    }
+
+    // Callback used with WinHTTP to listen for async completions.
+    static void CALLBACK completion_callback(
+        HINTERNET hRequestHandle, DWORD_PTR context, DWORD statusCode, _In_ void* statusInfo, DWORD statusInfoLength)
+    {
+        (void)statusInfoLength;
+
+        std::weak_ptr<winhttp_request_context>* p_weak_request_context =
+            reinterpret_cast<std::weak_ptr<winhttp_request_context>*>(context);
+
+        if (p_weak_request_context == nullptr)
+        {
+            return;
+        }
+
+        if (statusCode == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING)
+        {
+            // This callback is responsible for freeing the type-erased context.
+            // This particular status code indicates that this is the final callback call, suitable for context
+            // destruction.
+            delete p_weak_request_context;
+            return;
+        }
+
+        auto p_request_context = p_weak_request_context->lock();
+        if (!p_request_context)
+        {
+            // The request context was already released, probably due to cancellation
+            return;
+        }
+
+        switch (statusCode)
+        {
+            case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
+            {
+                WINHTTP_ASYNC_RESULT* error_result = reinterpret_cast<WINHTTP_ASYNC_RESULT*>(statusInfo);
+                const DWORD errorCode = error_result->dwError;
+
+                //  Some authentication schemes require multiple transactions.
+                //  When ERROR_WINHTTP_RESEND_REQUEST is encountered,
+                //  we should continue to resend the request until a response is received that does not contain a 401 or
+                //  407 status code.
+                if (errorCode == ERROR_WINHTTP_RESEND_REQUEST)
+                {
+                    bool resending = handle_authentication_failure(hRequestHandle, p_request_context, errorCode);
+                    if (resending)
+                    {
+                        // The request is resending. Wait until we get a new response.
+                        return;
+                    }
+                }
+
+                p_request_context->report_error(errorCode, build_error_msg(error_result));
+                return;
+            }
+            case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
+            {
+                if (!p_request_context->m_request.body())
+                {
+                    // Report progress finished uploading with no message body.
+                    auto progress = p_request_context->m_request._get_impl()->_progress_handler();
+                    if (progress)
+                    {
+                        try
+                        {
+                            (*progress)(message_direction::upload, 0);
+                        }
+                        catch (...)
+                        {
+                            p_request_context->report_exception(std::current_exception());
+                            return;
+                        }
+                    }
+                }
+
+                if (p_request_context->m_bodyType == transfer_encoding_chunked)
+                {
+                    _transfer_encoding_chunked_write_data(p_request_context.get());
+                }
+                else if (p_request_context->m_bodyType == content_length_chunked)
+                {
+                    _multiple_segment_write_data(p_request_context.get());
+                }
+                else
+                {
+                    if (!WinHttpReceiveResponse(hRequestHandle, nullptr))
+                    {
+                        auto errorCode = GetLastError();
+                        p_request_context->report_error(errorCode,
+                                                        build_error_msg(errorCode, "WinHttpReceiveResponse"));
+                    }
+                }
+                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<std::uint32_t*>(statusInfo))));
+                return;
+            }
+            case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
+            {
+                DWORD bytesWritten = *((DWORD*)statusInfo);
+                _ASSERTE(statusInfoLength == sizeof(DWORD));
+
+                if (bytesWritten > 0)
+                {
+                    auto progress = p_request_context->m_request._get_impl()->_progress_handler();
+                    if (progress)
+                    {
+                        p_request_context->m_uploaded += bytesWritten;
+                        try
+                        {
+                            (*progress)(message_direction::upload, p_request_context->m_uploaded);
+                        }
+                        catch (...)
+                        {
+                            p_request_context->report_exception(std::current_exception());
+                            return;
+                        }
+                    }
+                }
+
+                if (p_request_context->is_externally_allocated())
+                {
+                    p_request_context->_get_readbuffer().release(p_request_context->m_body_data.get(), bytesWritten);
+                }
+
+                if (p_request_context->m_bodyType == transfer_encoding_chunked)
+                {
+                    _transfer_encoding_chunked_write_data(p_request_context.get());
+                }
+                else if (p_request_context->m_bodyType == content_length_chunked)
+                {
+                    _multiple_segment_write_data(p_request_context.get());
+                }
+                else
+                {
+                    if (!WinHttpReceiveResponse(hRequestHandle, nullptr))
+                    {
+                        auto errorCode = GetLastError();
+                        p_request_context->report_error(errorCode,
+                                                        build_error_msg(errorCode, "WinHttpReceiveResponse"));
+                    }
+                }
+                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<unsigned char> header_raw_buffer;
+                header_raw_buffer.resize(headerBufferLength);
+                utility::char_t* header_buffer = reinterpret_cast<utility::char_t*>(&header_raw_buffer[0]);
+                if (!WinHttpQueryHeaders(hRequestHandle,
+                                         WINHTTP_QUERY_RAW_HEADERS_CRLF,
+                                         WINHTTP_HEADER_NAME_BY_INDEX,
+                                         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.
+                DWORD headerBufferLength = 0;
+                query_header_length(hRequestHandle, WINHTTP_QUERY_RAW_HEADERS_CRLF, headerBufferLength);
+
+                // Now allocate buffer for headers and query for them.
+                std::vector<unsigned char> header_raw_buffer;
+                header_raw_buffer.resize(headerBufferLength);
+                utility::char_t* header_buffer = reinterpret_cast<utility::char_t*>(&header_raw_buffer[0]);
+                if (!WinHttpQueryHeaders(hRequestHandle,
+                                         WINHTTP_QUERY_RAW_HEADERS_CRLF,
+                                         WINHTTP_HEADER_NAME_BY_INDEX,
+                                         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);
+
+                if (response.status_code() == status_codes::Unauthorized /*401*/ ||
+                    response.status_code() == status_codes::ProxyAuthRequired /*407*/)
+                {
+                    bool resending = handle_authentication_failure(hRequestHandle, p_request_context);
+                    if (resending)
+                    {
+                        // The request was not completed but resent with credentials. Wait until we get a new response
+                        return;
+                    }
+                }
+
+                // Check whether the request is compressed, and if so, whether we're handling it.
+                if (!p_request_context->handle_compression())
+                {
+                    // false indicates report_exception was called
+                    return;
+                }
+                if (p_request_context->m_decompressor &&
+                    !p_request_context->m_http_client->client_config().request_compressed_response())
+                {
+                    p_request_context->m_compression_state.m_chunk =
+                        ::utility::details::make_unique<winhttp_request_context::compression_state::_chunk_helper>();
+                    p_request_context->m_compression_state.m_chunked = true;
+                }
+
+                // Signal that the headers are available.
+                p_request_context->complete_headers();
+
+                // If the method was 'HEAD,' the body of the message is by definition empty. No need to
+                // read it. Any headers that suggest the presence of a body can safely be ignored.
+                if (p_request_context->m_request.method() == methods::HEAD)
+                {
+                    p_request_context->allocate_request_space(nullptr, 0);
+                    p_request_context->complete_request(0);
+                    return;
+                }
+
+                // HTTP Specification states:
+                // If a message is received with both a Transfer-Encoding header field
+                // and a Content-Length header field, the latter MUST be ignored.
+                // If none of them is specified, the message length should be determined by the server closing the
+                // connection. http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4
+
+                read_next_response_chunk(p_request_context.get(), 0, true);
+                return;
+            }
+            case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
+            {
+                // Status information contains pointer to DWORD containing number of bytes available.
+                const DWORD num_bytes = *(PDWORD)statusInfo;
+                uint8_t* buffer;
+
+                if (num_bytes > 0)
+                {
+                    if (p_request_context->m_decompressor)
+                    {
+                        // Allocate space for the compressed data; we'll decompress it into the caller stream once it's
+                        // been filled in
+                        if (p_request_context->m_compression_state.m_buffer.capacity() < num_bytes)
+                        {
+                            p_request_context->m_compression_state.m_buffer.reserve(num_bytes);
+                        }
+                        buffer = p_request_context->m_compression_state.m_buffer.data();
+                    }
+                    else
+                    {
+                        auto writebuf = p_request_context->_get_writebuffer();
+                        p_request_context->allocate_reply_space(writebuf.alloc(num_bytes), num_bytes);
+                        buffer = p_request_context->m_body_data.get();
+                    }
+
+                    // Read in available body data all at once.
+                    if (!WinHttpReadData(hRequestHandle, buffer, num_bytes, nullptr))
+                    {
+                        auto errorCode = GetLastError();
+                        p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpReadData"));
+                    }
+                }
+                else
+                {
+                    if (p_request_context->m_decompressor)
+                    {
+                        if (p_request_context->m_compression_state.m_chunked)
+                        {
+                            // We haven't seen the 0-length chunk and/or trailing delimiter that indicate the end of
+                            // chunked input
+                            p_request_context->report_exception(
+                                http_exception("Chunked response stream ended unexpectedly"));
+                            return;
+                        }
+                        if (p_request_context->m_compression_state.m_started &&
+                            !p_request_context->m_compression_state.m_done)
+                        {
+                            p_request_context->report_exception(
+                                http_exception("Received incomplete compressed stream"));
+                            return;
+                        }
+                    }
+
+                    // No more data available, complete the request.
+                    auto progress = p_request_context->m_request._get_impl()->_progress_handler();
+                    if (progress)
+                    {
+                        try
+                        {
+                            (*progress)(message_direction::download, p_request_context->m_downloaded);
+                        }
+                        catch (...)
+                        {
+                            p_request_context->report_exception(std::current_exception());
+                            return;
+                        }
+                    }
+
+                    p_request_context->complete_request(p_request_context->m_downloaded);
+                }
+                return;
+            }
+            case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
+            {
+                // Status information length contains the number of bytes read.
+                DWORD bytesRead = statusInfoLength;
+
+                // Report progress about downloaded bytes.
+                auto progress = p_request_context->m_request._get_impl()->_progress_handler();
+                p_request_context->m_downloaded += statusInfoLength;
+                if (progress)
+                {
+                    try
+                    {
+                        (*progress)(message_direction::download, p_request_context->m_downloaded);
+                    }
+                    catch (...)
+                    {
+                        p_request_context->report_exception(std::current_exception());
+                        return;
+                    }
+                }
+
+                // If no bytes have been read, then this is the end of the response.
+                if (bytesRead == 0)
+                {
+                    if (p_request_context->m_decompressor)
+                    {
+                        if (p_request_context->m_compression_state.m_chunked)
+                        {
+                            // We haven't seen the 0-length chunk and/or trailing delimiter that indicate the end of
+                            // chunked input
+                            p_request_context->report_exception(
+                                http_exception("Chunked response stream ended unexpectedly"));
+                            return;
+                        }
+                        if (p_request_context->m_compression_state.m_started &&
+                            !p_request_context->m_compression_state.m_done)
+                        {
+                            p_request_context->report_exception(
+                                http_exception("Received incomplete compressed stream"));
+                            return;
+                        }
+                    }
+                    p_request_context->complete_request(p_request_context->m_downloaded);
+                    return;
+                }
+
+                auto writebuf = p_request_context->_get_writebuffer();
+
+                if (p_request_context->m_decompressor)
+                {
+                    size_t chunk_size = (std::max)(static_cast<size_t>(bytesRead),
+                                                   p_request_context->m_http_client->client_config().chunksize());
+                    p_request_context->m_compression_state.m_bytes_read = static_cast<size_t>(bytesRead);
+                    p_request_context->m_compression_state.m_chunk_bytes = 0;
+
+                    // Note, some servers seem to send a first chunk of body data that decompresses to nothing, but
+                    // initializes the decompression state; this produces no decompressed output.  Subsequent chunks
+                    // will then begin emitting decompressed body data.
+
+                    // Oddly enough, WinHttp doesn't de-chunk for us if "chunked" isn't the only
+                    // encoding, so we need to do so on the fly as we process the received data
+                    auto process_buffer =
+                        [chunk_size](winhttp_request_context* c, size_t bytes_produced, bool outer) -> bool {
+                        if (!c->m_compression_state.m_chunk_bytes)
+                        {
+                            if (c->m_compression_state.m_chunked)
+                            {
+                                size_t offset;
+                                bool done;
+
+                                // Process the next portion of this piece of the transfer-encoded message
+                                done = c->m_compression_state.m_chunk->process_buffer(
+                                    c->m_compression_state.m_buffer.data() + c->m_compression_state.m_bytes_processed,
+                                    c->m_compression_state.m_bytes_read - c->m_compression_state.m_bytes_processed,
+                                    offset,
+                                    c->m_compression_state.m_chunk_bytes);
+
+                                // Skip chunk-related metadata; it isn't relevant to decompression
+                                _ASSERTE(c->m_compression_state.m_bytes_processed + offset <=
+                                         c->m_compression_state.m_bytes_read);
+                                c->m_compression_state.m_bytes_processed += offset;
+
+                                if (!c->m_compression_state.m_chunk_bytes)
+                                {
+                                    if (done)
+                                    {
+                                        // We've processed/validated all bytes in this transfer-encoded message.
+                                        // Note that we currently ignore "extra" trailing bytes, i.e.
+                                        // c->m_compression_state.m_bytes_processed <
+                                        // c->m_compression_state.m_bytes_read
+                                        if (c->m_compression_state.m_done)
+                                        {
+                                            c->complete_request(c->m_downloaded);
+                                            return false;
+                                        }
+                                        else if (!outer && bytes_produced != chunk_size)
+                                        {
+                                            throw http_exception("Transfer ended before decompression completed");
+                                        }
+                                    }
+                                    else if (!outer && bytes_produced != chunk_size)
+                                    {
+                                        // There should be more data to receive; look for it
+                                        c->m_compression_state.m_bytes_processed = 0;
+                                        read_next_response_chunk(
+                                            c, static_cast<DWORD>(c->m_compression_state.m_bytes_read));
+                                        return false;
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                _ASSERTE(!c->m_compression_state.m_bytes_processed ||
+                                         c->m_compression_state.m_bytes_processed ==
+                                             c->m_compression_state.m_bytes_read);
+                                if (c->m_compression_state.m_done)
+                                {
+                                    // Decompression is done; complete the request
+                                    c->complete_request(c->m_downloaded);
+                                    return false;
+                                }
+                                else if (c->m_compression_state.m_bytes_processed !=
+                                         c->m_compression_state.m_bytes_read)
+                                {
+                                    // We still have more data to process in the current buffer
+                                    c->m_compression_state.m_chunk_bytes =
+                                        c->m_compression_state.m_bytes_read - c->m_compression_state.m_bytes_processed;
+                                }
+                                else if (!outer && bytes_produced != chunk_size)
+                                {
+                                    // There should be more data to receive; look for it
+                                    c->m_compression_state.m_bytes_processed = 0;
+                                    read_next_response_chunk(c,
+                                                             static_cast<DWORD>(c->m_compression_state.m_bytes_read));
+                                    return false;
+                                }
+                                // Otherwise, we've processed all bytes in the input buffer, but there's a good chance
+                                // that there are still decompressed bytes to emit; we'll do so before reading the next
+                                // chunk
+                            }
+                        }
+
+                        // We're still processing the current message chunk
+                        return true;
+                    };
+
+                    pplx::details::_do_while([p_request_context, chunk_size, process_buffer]() -> pplx::task<bool> {
+                        uint8_t* buffer;
+
+                        try
+                        {
+                            if (!process_buffer(p_request_context.get(), 0, true))
+                            {
+                                // The chunked request has been completely processed (or contains no data in the first
+                                // place)
+                                return pplx::task_from_result<bool>(false);
+                            }
+                        }
+                        catch (...)
+                        {
+                            // The outer do-while requires an explicit task return to activate the then() clause
+                            return pplx::task_from_exception<bool>(std::current_exception());
+                        }
+
+                        // If it's possible to know how much post-compression data we're expecting (for instance if we
+                        // can discern how much total data the ostream can support, we could allocate (or at least
+                        // attempt to acquire) based on that
+                        p_request_context->m_compression_state.m_acquired =
+                            p_request_context->_get_writebuffer().alloc(chunk_size);
+                        if (p_request_context->m_compression_state.m_acquired)
+                        {
+                            buffer = p_request_context->m_compression_state.m_acquired;
+                        }
+                        else
+                        {
+                            // The streambuf couldn't accommodate our request; we'll use m_body_data's
+                            // internal vector as temporary storage, then putn() to the caller's stream
+                            p_request_context->allocate_reply_space(nullptr, chunk_size);
+                            buffer = p_request_context->m_body_data.get();
+                        }
+
+                        uint8_t* in = p_request_context->m_compression_state.m_buffer.data() +
+                                      p_request_context->m_compression_state.m_bytes_processed;
+                        size_t inbytes = p_request_context->m_compression_state.m_chunk_bytes;
+                        if (inbytes)
+                        {
+                            p_request_context->m_compression_state.m_started = true;
+                        }
+                        return p_request_context->m_decompressor
+                            ->decompress(
+                                in, inbytes, buffer, chunk_size, web::http::compression::operation_hint::has_more)
+                            .then([p_request_context, buffer, chunk_size, process_buffer](
+                                      pplx::task<web::http::compression::operation_result> op) {
+                                auto r = op.get();
+                                auto keep_going = [&r, process_buffer](winhttp_request_context* c) -> pplx::task<bool> {
+                                    _ASSERTE(r.input_bytes_processed <= c->m_compression_state.m_chunk_bytes);
+                                    c->m_compression_state.m_chunk_bytes -= r.input_bytes_processed;
+                                    c->m_compression_state.m_bytes_processed += r.input_bytes_processed;
+                                    c->m_compression_state.m_done = r.done;
+
+                                    try
+                                    {
+                                        // See if we still have more work to do for this section and/or for the response
+                                        // in general
+                                        return pplx::task_from_result<bool>(
+                                            process_buffer(c, r.output_bytes_produced, false));
+                                    }
+                                    catch (...)
+                                    {
+                                        return pplx::task_from_exception<bool>(std::current_exception());
+                                    }
+                                };
+
+                                _ASSERTE(p_request_context->m_compression_state.m_bytes_processed +
+                                             r.input_bytes_processed <=
+                                         p_request_context->m_compression_state.m_bytes_read);
+
+                                if (p_request_context->m_compression_state.m_acquired != nullptr)
+                                {
+                                    // We decompressed directly into the output stream
+                                    p_request_context->m_compression_state.m_acquired = nullptr;
+                                    p_request_context->_get_writebuffer().commit(r.output_bytes_produced);
+                                    return keep_going(p_request_context.get());
+                                }
+
+                                // We decompressed into our own buffer; let the stream copy the data
+                                return p_request_context->_get_writebuffer()
+                                    .putn_nocopy(buffer, r.output_bytes_produced)
+                                    .then([p_request_context, r, keep_going](pplx::task<size_t> op) {
+                                        if (op.get() != r.output_bytes_produced)
+                                        {
+                                            return pplx::task_from_exception<bool>(
+                                                std::runtime_error("Response stream unexpectedly failed to write the "
+                                                                   "requested number of bytes"));
+                                        }
+                                        return keep_going(p_request_context.get());
+                                    });
+                            });
+                    }).then([p_request_context](pplx::task<bool> 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)
+                            {
+                                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
+                {
+                    // If the data was allocated directly from the buffer then commit, otherwise we still
+                    // need to write to the response stream buffer.
+                    if (p_request_context->is_externally_allocated())
+                    {
+                        writebuf.commit(bytesRead);
+                        read_next_response_chunk(p_request_context.get(), bytesRead);
+                    }
+                    else
+                    {
+                        writebuf.putn_nocopy(p_request_context->m_body_data.get(), bytesRead)
+                            .then([hRequestHandle, p_request_context, bytesRead](pplx::task<size_t> op) {
+                                size_t written = 0;
+                                try
+                                {
+                                    written = op.get();
+                                }
+                                catch (...)
+                                {
+                                    p_request_context->report_exception(std::current_exception());
+                                    return;
+                                }
+
+                                // If we couldn't write everything, it's time to exit.
+                                if (written != bytesRead)
+                                {
+                                    p_request_context->report_exception(std::runtime_error(
+                                        "response stream unexpectedly failed to write the requested number of bytes"));
+                                    return;
+                                }
+
+                                read_next_response_chunk(p_request_context.get(), bytesRead);
+                            });
+                    }
+                }
+                return;
+            }
+        }
+    }
+
+    std::atomic<bool> m_opened;
+
+    // WinHTTP session and connection
+    HINTERNET m_hSession;
+    HINTERNET m_hConnection;
+    bool m_secure;
+
+    // If auto config is true, dynamically find the proxy for each URL using
+    // the proxy configuration script at the given URL if it's not empty or
+    // using WPAD otherwise.
+    bool m_proxy_auto_config {false};
+    utility::string_t m_proxy_auto_config_url;
+};
+
+std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri&& base_uri,
+                                                                                http_client_config&& client_config)
+{
+    return std::make_shared<details::winhttp_client>(std::move(base_uri), std::move(client_config));
+}
+
+
+} // namespace details
+} // namespace client
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/client/http_client_winrt.cpp b/Release/src/http/client/http_client_winrt.cpp
new file mode 100644 (file)
index 0000000..a95d9b3
--- /dev/null
@@ -0,0 +1,568 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Client-side APIs.
+ *
+ * This file contains the implementation for the Windows Runtime, using XML HTTP Extended Request.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "../common/internal_http_helpers.h"
+#include "http_client_impl.h"
+#include <Strsafe.h>
+// Important for WP8
+#if !defined(__WRL_NO_DEFAULT_LIB__)
+#define __WRL_NO_DEFAULT_LIB__
+#endif
+#include <msxml6.h>
+#include <wrl.h>
+using namespace std;
+using namespace Platform;
+using namespace Microsoft::WRL;
+
+namespace web
+{
+namespace http
+{
+namespace client
+{
+namespace details
+{
+// Additional information necessary to track a WinRT request.
+class winrt_request_context final : public request_context
+{
+public:
+    // Factory function to create requests on the heap.
+    static std::shared_ptr<request_context> create_request_context(
+        const std::shared_ptr<_http_client_communicator>& client, http_request& request)
+    {
+        return std::make_shared<winrt_request_context>(client, request);
+    }
+
+    Microsoft::WRL::ComPtr<IXMLHTTPRequest2> m_hRequest;
+    std::exception_ptr m_exceptionPtr;
+
+    // Request contexts must be created through factory function.
+    // But constructor needs to be public for make_shared to access.
+    winrt_request_context(const std::shared_ptr<_http_client_communicator>& client, http_request& request)
+        : request_context(client, request), m_hRequest(nullptr), m_exceptionPtr()
+    {
+    }
+};
+
+// Implementation of IXMLHTTPRequest2Callback.
+class HttpRequestCallback final : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IXMLHTTPRequest2Callback, FtmBase>
+{
+public:
+    HttpRequestCallback(const std::shared_ptr<winrt_request_context>& request) : m_request(request) {}
+
+    // Called when the HTTP request is being redirected to a new URL.
+    HRESULT STDMETHODCALLTYPE OnRedirect(_In_opt_ IXMLHTTPRequest2*, __RPC__in_string const WCHAR*) { return S_OK; }
+
+    // Called when HTTP headers have been received and processed.
+    HRESULT STDMETHODCALLTYPE OnHeadersAvailable(_In_ IXMLHTTPRequest2* xmlReq,
+                                                 DWORD dw,
+                                                 __RPC__in_string const WCHAR* phrase)
+    {
+        http_response& response = m_request->m_response;
+        response.set_status_code((http::status_code)dw);
+        response.set_reason_phrase(phrase);
+
+        utf16char* hdrStr = nullptr;
+        HRESULT hr = xmlReq->GetAllResponseHeaders(&hdrStr);
+        if (SUCCEEDED(hr))
+        {
+            try
+            {
+                auto progress = m_request->m_request._get_impl()->_progress_handler();
+                if (progress && m_request->m_uploaded == 0)
+                {
+                    (*progress)(message_direction::upload, 0);
+                }
+
+                web::http::details::parse_headers_string(hdrStr, response.headers());
+                m_request->complete_headers();
+            }
+            catch (...)
+            {
+                m_request->m_exceptionPtr = std::current_exception();
+                hr = ERROR_UNHANDLED_EXCEPTION;
+            }
+        }
+
+        if (hdrStr != nullptr)
+        {
+            ::CoTaskMemFree(hdrStr);
+            hdrStr = nullptr;
+        }
+
+        return hr;
+    }
+
+    // Called when a portion of the entity body has been received.
+    HRESULT STDMETHODCALLTYPE OnDataAvailable(_In_opt_ IXMLHTTPRequest2*, _In_opt_ ISequentialStream*) { return S_OK; }
+
+    // Called when the entire entity response has been received.
+    HRESULT STDMETHODCALLTYPE OnResponseReceived(_In_opt_ IXMLHTTPRequest2*, _In_opt_ ISequentialStream*)
+    {
+        auto progress = m_request->m_request._get_impl()->_progress_handler();
+        if (progress && m_request->m_downloaded == 0)
+        {
+            try
+            {
+                (*progress)(message_direction::download, 0);
+            }
+            catch (...)
+            {
+                m_request->m_exceptionPtr = std::current_exception();
+            }
+        }
+
+        if (m_request->m_exceptionPtr != nullptr)
+            m_request->report_exception(m_request->m_exceptionPtr);
+        else
+            m_request->complete_request(m_request->m_downloaded);
+
+        // Break the circular reference loop.
+        //     - winrt_request_context holds a reference to IXmlHttpRequest2
+        //     - IXmlHttpRequest2 holds a reference to HttpRequestCallback
+        //     - HttpRequestCallback holds a reference to winrt_request_context
+        //
+        // Not releasing the winrt_request_context below previously worked due to the
+        // implementation of IXmlHttpRequest2, after calling OnError/OnResponseReceived
+        // it would immediately release its reference to HttpRequestCallback. However
+        // it since has been discovered on Xbox that the implementation is different,
+        // the reference to HttpRequestCallback is NOT immediately released and is only
+        // done at destruction of IXmlHttpRequest2.
+        //
+        // To be safe we now will break the circular reference.
+        m_request.reset();
+
+        return S_OK;
+    }
+
+    // Called when an error occurs during the HTTP request.
+    HRESULT STDMETHODCALLTYPE OnError(_In_opt_ IXMLHTTPRequest2*, HRESULT hrError)
+    {
+        if (m_request->m_exceptionPtr == nullptr)
+        {
+            std::wstring msg(L"IXMLHttpRequest2Callback::OnError: ");
+            msg.append(std::to_wstring(hrError));
+            msg.append(L": ");
+            msg.append(utility::conversions::to_string_t(utility::details::windows_category().message(hrError)));
+            m_request->report_error(hrError, msg);
+        }
+        else
+        {
+            m_request->report_exception(m_request->m_exceptionPtr);
+        }
+
+        // Break the circular reference loop.
+        // See full explanation in OnResponseReceived
+        m_request.reset();
+
+        return S_OK;
+    }
+
+private:
+    std::shared_ptr<winrt_request_context> m_request;
+};
+
+/// <summary>
+/// This class acts as a bridge for the underlying request stream.
+/// </summary>
+/// <remarks>
+/// These operations are completely synchronous, so it's important to block on both
+/// read and write operations. The I/O will be done off the UI thread, so there is no risk
+/// of causing the UI to become unresponsive.
+/// </remarks>
+class IRequestStream final
+    : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<ClassicCom>, ISequentialStream>
+{
+public:
+    IRequestStream(const std::weak_ptr<winrt_request_context>& context,
+                   size_t read_length = (std::numeric_limits<size_t>::max)())
+        : m_context(context), m_read_length(read_length)
+    {
+        // read_length is the initial length of the ISequentialStream that is available for read
+        // This is required because IXHR2 attempts to read more data that what is specified by
+        // the content_length. (Specifically, it appears to be reading 128K chunks regardless of
+        // the content_length specified).
+    }
+
+    virtual HRESULT STDMETHODCALLTYPE Read(_Out_writes_(cb) void* pv, _In_ ULONG cb, _Out_ ULONG* pcbRead)
+    {
+        auto context = m_context.lock();
+        if (context == nullptr)
+        {
+            // OnError has already been called so just error out
+            return STG_E_READFAULT;
+        }
+
+        try
+        {
+            auto buffer = context->_get_readbuffer();
+
+            // Do not read more than the specified read_length
+            msl::safeint3::SafeInt<size_t> safe_count = static_cast<size_t>(cb);
+            size_t size_to_read = safe_count.Min(m_read_length);
+
+            const size_t count = buffer.getn((uint8_t*)pv, size_to_read).get();
+            *pcbRead = (ULONG)count;
+            if (count == 0 && size_to_read != 0)
+            {
+                return STG_E_READFAULT;
+            }
+
+            _ASSERTE(count != static_cast<size_t>(-1));
+            _ASSERTE(m_read_length >= count);
+            m_read_length -= count;
+
+            auto progress = context->m_request._get_impl()->_progress_handler();
+            if (progress && count > 0)
+            {
+                context->m_uploaded += count;
+                try
+                {
+                    (*progress)(message_direction::upload, context->m_uploaded);
+                }
+                catch (...)
+                {
+                    context->m_exceptionPtr = std::current_exception();
+                    return STG_E_READFAULT;
+                }
+            }
+
+            return S_OK;
+        }
+        catch (...)
+        {
+            context->m_exceptionPtr = std::current_exception();
+            return STG_E_READFAULT;
+        }
+    }
+
+    virtual HRESULT STDMETHODCALLTYPE Write(_In_reads_bytes_(cb) const void* pv,
+                                            _In_ ULONG cb,
+                                            _Out_opt_ ULONG* pcbWritten)
+    {
+        (void)pv;
+        (void)cb;
+        (void)pcbWritten;
+        return E_NOTIMPL;
+    }
+
+private:
+    // The request context controls the lifetime of this class so we only hold a weak_ptr.
+    std::weak_ptr<winrt_request_context> m_context;
+
+    // Length of the ISequentialStream for reads. This is equivalent
+    // to the amount of data that the ISequentialStream is allowed
+    // to read from the underlying stream buffer.
+    size_t m_read_length;
+};
+
+/// <summary>
+/// This class acts as a bridge for the underlying response stream.
+/// </summary>
+/// <remarks>
+/// These operations are completely synchronous, so it's important to block on both
+/// read and write operations. The I/O will be done off the UI thread, so there is no risk
+/// of causing the UI to become unresponsive.
+/// </remarks>
+class IResponseStream final
+    : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<ClassicCom>, ISequentialStream>
+{
+public:
+    IResponseStream(const std::weak_ptr<winrt_request_context>& context) : m_context(context) {}
+
+    virtual HRESULT STDMETHODCALLTYPE Write(_In_reads_bytes_(cb) const void* pv,
+                                            _In_ ULONG cb,
+                                            _Out_opt_ ULONG* pcbWritten)
+    {
+        auto context = m_context.lock();
+        if (context == nullptr)
+        {
+            // OnError has already been called so just error out
+            return STG_E_CANTSAVE;
+        }
+
+        if (pcbWritten != nullptr)
+        {
+            *pcbWritten = 0;
+        }
+
+        if (cb == 0)
+        {
+            return S_OK;
+        }
+
+        try
+        {
+            auto buffer = context->_get_writebuffer();
+            const size_t count =
+                buffer.putn_nocopy(reinterpret_cast<const uint8_t*>(pv), static_cast<size_t>(cb)).get();
+
+            _ASSERTE(count != static_cast<size_t>(-1));
+            _ASSERTE(count <= static_cast<size_t>(ULONG_MAX));
+            if (pcbWritten != nullptr)
+            {
+                *pcbWritten = (ULONG)count;
+            }
+            context->m_downloaded += count;
+
+            auto progress = context->m_request._get_impl()->_progress_handler();
+            if (progress && count > 0)
+            {
+                try
+                {
+                    (*progress)(message_direction::download, context->m_downloaded);
+                }
+                catch (...)
+                {
+                    context->m_exceptionPtr = std::current_exception();
+                    return STG_E_CANTSAVE;
+                }
+            }
+
+            return S_OK;
+        }
+        catch (...)
+        {
+            context->m_exceptionPtr = std::current_exception();
+            return STG_E_CANTSAVE;
+        }
+    }
+
+    virtual HRESULT STDMETHODCALLTYPE Read(_Out_writes_bytes_to_(cb, *pcbRead) void* pv,
+                                           _In_ ULONG cb,
+                                           _Out_ ULONG* pcbRead)
+    {
+        (void)pv;
+        (void)cb;
+        (void)pcbRead;
+        return E_NOTIMPL;
+    }
+
+private:
+    // The request context controls the lifetime of this class so we only hold a weak_ptr.
+    std::weak_ptr<winrt_request_context> m_context;
+};
+
+// WinRT client.
+class winrt_client final : public _http_client_communicator
+{
+public:
+    winrt_client(http::uri&& address, http_client_config&& client_config)
+        : _http_client_communicator(std::move(address), std::move(client_config))
+    {
+    }
+
+    winrt_client(const winrt_client&) = delete;
+    winrt_client& operator=(const winrt_client&) = delete;
+
+    virtual pplx::task<http_response> propagate(http_request request) override
+    {
+        auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this());
+        auto context = details::winrt_request_context::create_request_context(self, request);
+
+        // Use a task to externally signal the final result and completion of the task.
+        auto result_task = pplx::create_task(context->m_request_completion);
+
+        // Asynchronously send the response with the HTTP client implementation.
+        this->async_send_request(context);
+
+        return result_task;
+    }
+
+protected:
+    // Start sending request.
+    virtual void send_request(_In_ const std::shared_ptr<request_context>& request) override
+    {
+        http_request& msg = request->m_request;
+        auto winrt_context = std::static_pointer_cast<winrt_request_context>(request);
+
+        if (!web::http::details::validate_method(msg.method()))
+        {
+            request->report_exception(http_exception(L"The method string is invalid."));
+            return;
+        }
+
+        if (msg.method() == http::methods::TRCE)
+        {
+            // Not supported by WinInet. Generate a more specific exception than what WinInet does.
+            request->report_exception(http_exception(L"TRACE is not supported"));
+            return;
+        }
+
+        const size_t content_length = msg._get_impl()->_get_content_length();
+        if (content_length == (std::numeric_limits<size_t>::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"));
+            return;
+        }
+
+        // Start sending HTTP request.
+        HRESULT hr = CoCreateInstance(__uuidof(FreeThreadedXMLHTTP60),
+                                      nullptr,
+                                      CLSCTX_INPROC,
+                                      __uuidof(IXMLHTTPRequest2),
+                                      reinterpret_cast<void**>(winrt_context->m_hRequest.GetAddressOf()));
+        if (FAILED(hr))
+        {
+            request->report_error(hr, L"Failure to create IXMLHTTPRequest2 instance");
+            return;
+        }
+
+        utility::string_t encoded_resource = http::uri_builder(m_uri).append(msg.relative_uri()).to_string();
+
+        const auto& config = client_config();
+        const auto& client_cred = config.credentials();
+        const auto& proxy = config.proxy();
+        const auto& proxy_cred = proxy.credentials();
+        if (!proxy.is_default())
+        {
+            request->report_exception(http_exception(L"Only a default proxy server is supported"));
+            return;
+        }
+
+        // New scope to ensure plain text password is cleared as soon as possible.
+        {
+            utility::string_t username, proxy_username;
+            const utility::char_t* password = nullptr;
+            const utility::char_t* proxy_password = nullptr;
+            ::web::details::plaintext_string password_plaintext, proxy_password_plaintext;
+
+            if (client_cred.is_set())
+            {
+                username = client_cred.username();
+                password_plaintext = client_cred._internal_decrypt();
+                password = password_plaintext->c_str();
+            }
+            if (proxy_cred.is_set())
+            {
+                proxy_username = proxy_cred.username();
+                proxy_password_plaintext = proxy_cred._internal_decrypt();
+                proxy_password = proxy_password_plaintext->c_str();
+            }
+
+            hr = winrt_context->m_hRequest->Open(msg.method().c_str(),
+                                                 encoded_resource.c_str(),
+                                                 Make<HttpRequestCallback>(winrt_context).Get(),
+                                                 username.c_str(),
+                                                 password,
+                                                 proxy_username.c_str(),
+                                                 proxy_password);
+        }
+        if (FAILED(hr))
+        {
+            request->report_error(hr, L"Failure to open HTTP request");
+            return;
+        }
+
+        // Suppress automatic prompts for user credentials, since they are already provided.
+        hr = winrt_context->m_hRequest->SetProperty(XHR_PROP_NO_CRED_PROMPT, TRUE);
+        if (FAILED(hr))
+        {
+            request->report_error(hr, L"Failure to set no credentials prompt property");
+            return;
+        }
+
+        // Set timeout.
+        ULONGLONG timeout = static_cast<ULONGLONG>(config.timeout<std::chrono::milliseconds>().count());
+        timeout = (std::max<decltype(timeout)>)(timeout, (std::numeric_limits<decltype(timeout)>::min)() + 1);
+        hr = winrt_context->m_hRequest->SetProperty(XHR_PROP_TIMEOUT, timeout);
+        if (FAILED(hr))
+        {
+            request->report_error(hr, L"Failure to set HTTP request properties");
+            return;
+        }
+
+        // If XHR_PROP_ONDATA_NEVER is defined in ixmlhttprequest.h or msxml6.h utilize it.
+        // Specifies never to call OnDataAvailable improving performance and we
+        // already don't use OnDataAvaliable anyway.
+#ifdef XHR_PROP_ONDATA_NEVER
+        hr = winrt_context->m_hRequest->SetProperty(XHR_PROP_ONDATA_THRESHOLD, XHR_PROP_ONDATA_NEVER);
+        if (FAILED(hr))
+        {
+            request->report_error(hr, L"Failure to turn off on data threshold");
+        }
+#endif
+
+        // Add headers.
+        for (const auto& hdr : msg.headers())
+        {
+            winrt_context->m_hRequest->SetRequestHeader(hdr.first.c_str(), hdr.second.c_str());
+        }
+
+        // Set response stream.
+        hr = winrt_context->m_hRequest->SetCustomResponseStream(Make<IResponseStream>(winrt_context).Get());
+        if (FAILED(hr))
+        {
+            request->report_error(hr, L"Failure to set HTTP response stream");
+            return;
+        }
+
+        // Call the callback function of user customized options
+        try
+        {
+            config.invoke_nativehandle_options(winrt_context->m_hRequest.Get());
+        }
+        catch (...)
+        {
+            request->report_exception(std::current_exception());
+            return;
+        }
+
+        if (content_length == 0)
+        {
+            hr = winrt_context->m_hRequest->Send(nullptr, 0);
+        }
+        else
+        {
+            if (msg.method() == http::methods::GET || msg.method() == http::methods::HEAD)
+            {
+                request->report_exception(http_exception(get_with_body_err_msg));
+                return;
+            }
+
+            hr = winrt_context->m_hRequest->Send(Make<IRequestStream>(winrt_context, content_length).Get(),
+                                                 content_length);
+        }
+
+        if (FAILED(hr))
+        {
+            request->report_error(hr, L"Failure to send HTTP request");
+            return;
+        }
+
+        // Register for notification on cancellation to abort this request.
+        if (msg._cancellation_token() != pplx::cancellation_token::none())
+        {
+            auto requestHandle = winrt_context->m_hRequest;
+
+            // cancellation callback is unregistered when request is completed.
+            winrt_context->m_cancellationRegistration =
+                msg._cancellation_token().register_callback([requestHandle]() { requestHandle->Abort(); });
+        }
+    }
+};
+
+std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri&& base_uri,
+                                                                                http_client_config&& client_config)
+{
+    return std::make_shared<details::winrt_client>(std::move(base_uri), std::move(client_config));
+}
+
+} // namespace details
+} // namespace client
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/client/x509_cert_utilities.cpp b/Release/src/http/client/x509_cert_utilities.cpp
new file mode 100644 (file)
index 0000000..67fc5ac
--- /dev/null
@@ -0,0 +1,452 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Contains utility functions for helping to verify server certificates in OS X/iOS.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "../common/x509_cert_utilities.h"
+
+#ifdef CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+
+#include <type_traits>
+#include <vector>
+
+#if defined(ANDROID) || defined(__ANDROID__)
+#include "pplx/threadpool.h"
+#include <jni.h>
+#endif
+
+#if defined(__APPLE__)
+#include <CoreFoundation/CFData.h>
+#include <Security/SecBase.h>
+#include <Security/SecCertificate.h>
+#include <Security/SecPolicy.h>
+#include <Security/SecTrust.h>
+#endif
+
+namespace web
+{
+namespace http
+{
+namespace client
+{
+namespace details
+{
+static bool verify_X509_cert_chain(const std::vector<std::string>& certChain, const std::string& hostName);
+
+bool verify_cert_chain_platform_specific(boost::asio::ssl::verify_context& verifyCtx, const std::string& hostName)
+{
+    X509_STORE_CTX* storeContext = verifyCtx.native_handle();
+    int currentDepth = X509_STORE_CTX_get_error_depth(storeContext);
+    if (currentDepth != 0)
+    {
+        return true;
+    }
+
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
+    STACK_OF(X509)* certStack = X509_STORE_CTX_get_chain(storeContext);
+#else
+    STACK_OF(X509)* certStack = X509_STORE_CTX_get0_chain(storeContext);
+#endif
+
+    const int numCerts = sk_X509_num(certStack);
+    if (numCerts < 0)
+    {
+        return false;
+    }
+
+    std::vector<std::string> certChain;
+    certChain.reserve(numCerts);
+    for (int i = 0; i < numCerts; ++i)
+    {
+        X509* cert = sk_X509_value(certStack, i);
+
+        // Encode into DER format into raw memory.
+        int len = i2d_X509(cert, nullptr);
+        if (len < 0)
+        {
+            return false;
+        }
+
+        std::string certData;
+        certData.resize(len);
+        unsigned char* buffer = reinterpret_cast<unsigned char*>(&certData[0]);
+        len = i2d_X509(cert, &buffer);
+        if (len < 0)
+        {
+            return false;
+        }
+
+        certChain.push_back(std::move(certData));
+    }
+
+    auto verify_result = verify_X509_cert_chain(certChain, hostName);
+
+    // The Windows Crypto APIs don't do host name checks, use Boost's implementation.
+#if defined(_WIN32)
+    if (verify_result)
+    {
+        boost::asio::ssl::rfc2818_verification rfc2818(hostName);
+        verify_result = rfc2818(verify_result, verifyCtx);
+    }
+#endif
+    return verify_result;
+}
+
+#if defined(ANDROID) || defined(__ANDROID__)
+using namespace crossplat;
+
+/// <summary>
+/// Helper function to check return value and see if any exceptions
+/// occurred when calling a JNI function.
+/// <summary>
+/// <returns><c>true</c> if JNI call failed, <c>false</c> otherwise.</returns>
+static bool jni_failed(JNIEnv* env)
+{
+    if (env->ExceptionOccurred())
+    {
+        // Clear exception otherwise no other JNI functions can be called.
+        // In the future if we improve error reporting the exception message
+        // can be retrieved from here.
+        env->ExceptionClear();
+        return true;
+    }
+    return false;
+}
+template<typename T>
+static bool jni_failed(JNIEnv* env, const java_local_ref<T>& result)
+{
+    if (jni_failed(env) || !result)
+    {
+        return true;
+    }
+    return false;
+}
+static bool jni_failed(JNIEnv* env, const jmethodID& result)
+{
+    if (jni_failed(env) || result == nullptr)
+    {
+        return true;
+    }
+    return false;
+}
+#define CHECK_JREF(env, obj)                                                                                           \
+    if (jni_failed<decltype(obj)::element_type>(env, obj)) return false;
+#define CHECK_JMID(env, mid)                                                                                           \
+    if (jni_failed(env, mid)) return false;
+#define CHECK_JNI(env)                                                                                                 \
+    if (jni_failed(env)) return false;
+
+bool verify_X509_cert_chain(const std::vector<std::string>& certChain, const std::string& hostName)
+{
+    JNIEnv* env = get_jvm_env();
+
+    // Possible performance improvement:
+    // In the future we could gain performance by turning all the jclass local
+    // references into global references. Then we could lazy initialize and
+    // save them globally. If this is done I'm not exactly sure where the release
+    // should be.
+
+    // ByteArrayInputStream
+    java_local_ref<jclass> byteArrayInputStreamClass(env->FindClass("java/io/ByteArrayInputStream"));
+    CHECK_JREF(env, byteArrayInputStreamClass);
+    jmethodID byteArrayInputStreamConstructorMethod =
+        env->GetMethodID(byteArrayInputStreamClass.get(), "<init>", "([B)V");
+    CHECK_JMID(env, byteArrayInputStreamConstructorMethod);
+
+    // CertificateFactory
+    java_local_ref<jclass> certificateFactoryClass(env->FindClass("java/security/cert/CertificateFactory"));
+    CHECK_JREF(env, certificateFactoryClass);
+    jmethodID certificateFactoryGetInstanceMethod = env->GetStaticMethodID(
+        certificateFactoryClass.get(), "getInstance", "(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");
+    CHECK_JMID(env, certificateFactoryGetInstanceMethod);
+    jmethodID generateCertificateMethod = env->GetMethodID(certificateFactoryClass.get(),
+                                                           "generateCertificate",
+                                                           "(Ljava/io/InputStream;)Ljava/security/cert/Certificate;");
+    CHECK_JMID(env, generateCertificateMethod);
+
+    // X509Certificate
+    java_local_ref<jclass> X509CertificateClass(env->FindClass("java/security/cert/X509Certificate"));
+    CHECK_JREF(env, X509CertificateClass);
+
+    // TrustManagerFactory
+    java_local_ref<jclass> trustManagerFactoryClass(env->FindClass("javax/net/ssl/TrustManagerFactory"));
+    CHECK_JREF(env, trustManagerFactoryClass);
+    jmethodID trustManagerFactoryGetInstanceMethod = env->GetStaticMethodID(
+        trustManagerFactoryClass.get(), "getInstance", "(Ljava/lang/String;)Ljavax/net/ssl/TrustManagerFactory;");
+    CHECK_JMID(env, trustManagerFactoryGetInstanceMethod);
+    jmethodID trustManagerFactoryInitMethod =
+        env->GetMethodID(trustManagerFactoryClass.get(), "init", "(Ljava/security/KeyStore;)V");
+    CHECK_JMID(env, trustManagerFactoryInitMethod);
+    jmethodID trustManagerFactoryGetTrustManagersMethod =
+        env->GetMethodID(trustManagerFactoryClass.get(), "getTrustManagers", "()[Ljavax/net/ssl/TrustManager;");
+    CHECK_JMID(env, trustManagerFactoryGetTrustManagersMethod);
+
+    // X509TrustManager
+    java_local_ref<jclass> X509TrustManagerClass(env->FindClass("javax/net/ssl/X509TrustManager"));
+    CHECK_JREF(env, X509TrustManagerClass);
+    jmethodID X509TrustManagerCheckServerTrustedMethod =
+        env->GetMethodID(X509TrustManagerClass.get(),
+                         "checkServerTrusted",
+                         "([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V");
+    CHECK_JMID(env, X509TrustManagerCheckServerTrustedMethod);
+
+    // StrictHostnameVerifier
+    java_local_ref<jclass> strictHostnameVerifierClass(
+        env->FindClass("org/apache/http/conn/ssl/StrictHostnameVerifier"));
+    CHECK_JREF(env, strictHostnameVerifierClass);
+    jmethodID strictHostnameVerifierConstructorMethod =
+        env->GetMethodID(strictHostnameVerifierClass.get(), "<init>", "()V");
+    CHECK_JMID(env, strictHostnameVerifierConstructorMethod);
+    jmethodID strictHostnameVerifierVerifyMethod = env->GetMethodID(
+        strictHostnameVerifierClass.get(), "verify", "(Ljava/lang/String;Ljava/security/cert/X509Certificate;)V");
+    CHECK_JMID(env, strictHostnameVerifierVerifyMethod);
+
+    // Create CertificateFactory
+    java_local_ref<jstring> XDot509String(env->NewStringUTF("X.509"));
+    CHECK_JREF(env, XDot509String);
+    java_local_ref<jobject> certificateFactory(env->CallStaticObjectMethod(
+        certificateFactoryClass.get(), certificateFactoryGetInstanceMethod, XDot509String.get()));
+    CHECK_JREF(env, certificateFactory);
+
+    // Create Java array to store all the certs in.
+    java_local_ref<jobjectArray> certsArray(env->NewObjectArray(certChain.size(), X509CertificateClass.get(), nullptr));
+    CHECK_JREF(env, certsArray);
+
+    // For each certificate perform the following steps:
+    //   1. Create ByteArrayInputStream backed by DER certificate bytes
+    //   2. Create Certificate using CertificateFactory.generateCertificate
+    //   3. Add Certificate to array
+    int i = 0;
+    for (const auto& certData : certChain)
+    {
+        java_local_ref<jbyteArray> byteArray(env->NewByteArray(certData.size()));
+        CHECK_JREF(env, byteArray);
+        env->SetByteArrayRegion(byteArray.get(), 0, certData.size(), reinterpret_cast<const jbyte*>(certData.c_str()));
+        CHECK_JNI(env);
+        java_local_ref<jobject> byteArrayInputStream(
+            env->NewObject(byteArrayInputStreamClass.get(), byteArrayInputStreamConstructorMethod, byteArray.get()));
+        CHECK_JREF(env, byteArrayInputStream);
+
+        java_local_ref<jobject> cert(
+            env->CallObjectMethod(certificateFactory.get(), generateCertificateMethod, byteArrayInputStream.get()));
+        CHECK_JREF(env, cert);
+
+        env->SetObjectArrayElement(certsArray.get(), i, cert.get());
+        CHECK_JNI(env);
+        ++i;
+    }
+
+    // Create TrustManagerFactory, init with Android system certs
+    java_local_ref<jstring> X509String(env->NewStringUTF("X509"));
+    CHECK_JREF(env, X509String);
+    java_local_ref<jobject> trustFactoryManager(env->CallStaticObjectMethod(
+        trustManagerFactoryClass.get(), trustManagerFactoryGetInstanceMethod, X509String.get()));
+    CHECK_JREF(env, trustFactoryManager);
+    env->CallVoidMethod(trustFactoryManager.get(), trustManagerFactoryInitMethod, nullptr);
+    CHECK_JNI(env);
+
+    // Get TrustManager
+    java_local_ref<jobjectArray> trustManagerArray(static_cast<jobjectArray>(
+        env->CallObjectMethod(trustFactoryManager.get(), trustManagerFactoryGetTrustManagersMethod)));
+    CHECK_JREF(env, trustManagerArray);
+    java_local_ref<jobject> trustManager(env->GetObjectArrayElement(trustManagerArray.get(), 0));
+    CHECK_JREF(env, trustManager);
+
+    // Validate certificate chain.
+    java_local_ref<jstring> RSAString(env->NewStringUTF("RSA"));
+    CHECK_JREF(env, RSAString);
+    env->CallVoidMethod(
+        trustManager.get(), X509TrustManagerCheckServerTrustedMethod, certsArray.get(), RSAString.get());
+    CHECK_JNI(env);
+
+    // Verify hostname on certificate according to RFC 2818.
+    java_local_ref<jobject> hostnameVerifier(
+        env->NewObject(strictHostnameVerifierClass.get(), strictHostnameVerifierConstructorMethod));
+    CHECK_JREF(env, hostnameVerifier);
+    java_local_ref<jstring> hostNameString(env->NewStringUTF(hostName.c_str()));
+    CHECK_JREF(env, hostNameString);
+    java_local_ref<jobject> cert(env->GetObjectArrayElement(certsArray.get(), 0));
+    CHECK_JREF(env, cert);
+    env->CallVoidMethod(hostnameVerifier.get(), strictHostnameVerifierVerifyMethod, hostNameString.get(), cert.get());
+    CHECK_JNI(env);
+
+    return true;
+}
+#endif
+
+#if defined(__APPLE__)
+namespace
+{
+// Simple RAII pattern wrapper to perform CFRelease on objects.
+template<typename T>
+class cf_ref
+{
+public:
+    cf_ref(T v) : value(v)
+    {
+        static_assert(sizeof(cf_ref<T>) == sizeof(T), "Code assumes just a wrapper, see usage in CFArrayCreate below.");
+    }
+    cf_ref() : value(nullptr) {}
+    cf_ref(cf_ref&& other) : value(other.value) { other.value = nullptr; }
+
+    ~cf_ref()
+    {
+        if (value != nullptr)
+        {
+            CFRelease(value);
+        }
+    }
+
+    T& get() { return value; }
+
+private:
+    cf_ref(const cf_ref&);
+    cf_ref& operator=(const cf_ref&);
+    T value;
+};
+} // namespace
+
+bool verify_X509_cert_chain(const std::vector<std::string>& certChain, const std::string& hostName)
+{
+    // Build up CFArrayRef with all the certificates.
+    // All this code is basically just to get into the correct structures for the Apple APIs.
+    // Copies are avoided whenever possible.
+    std::vector<cf_ref<SecCertificateRef>> certs;
+    for (const auto& certBuf : certChain)
+    {
+        cf_ref<CFDataRef> certDataRef =
+            CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
+                                        reinterpret_cast<const unsigned char*>(certBuf.c_str()),
+                                        certBuf.size(),
+                                        kCFAllocatorNull);
+        if (certDataRef.get() == nullptr)
+        {
+            return false;
+        }
+
+        cf_ref<SecCertificateRef> certObj = SecCertificateCreateWithData(nullptr, certDataRef.get());
+        if (certObj.get() == nullptr)
+        {
+            return false;
+        }
+        certs.push_back(std::move(certObj));
+    }
+    cf_ref<CFArrayRef> certsArray = CFArrayCreate(
+        kCFAllocatorDefault, const_cast<const void**>(reinterpret_cast<void**>(&certs[0])), certs.size(), nullptr);
+    if (certsArray.get() == nullptr)
+    {
+        return false;
+    }
+
+    // Create trust management object with certificates and SSL policy.
+    // Note: SecTrustCreateWithCertificates expects the certificate to be
+    // verified is the first element.
+    cf_ref<CFStringRef> cfHostName = CFStringCreateWithCStringNoCopy(
+        kCFAllocatorDefault, hostName.c_str(), kCFStringEncodingASCII, kCFAllocatorNull);
+    if (cfHostName.get() == nullptr)
+    {
+        return false;
+    }
+    cf_ref<SecPolicyRef> policy = SecPolicyCreateSSL(true /* client side */, cfHostName.get());
+    cf_ref<SecTrustRef> trust;
+    OSStatus status = SecTrustCreateWithCertificates(certsArray.get(), policy.get(), &trust.get());
+    if (status == noErr)
+    {
+        // Perform actual certificate verification.
+        SecTrustResultType trustResult;
+        status = SecTrustEvaluate(trust.get(), &trustResult);
+        if (status == noErr && (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed))
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+#endif
+
+#if defined(_WIN32)
+bool verify_X509_cert_chain(const std::vector<std::string>& certChain, const std::string& hostname)
+{
+    // Create certificate context from server certificate.
+    winhttp_cert_context cert;
+    cert.raw = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+                                            reinterpret_cast<const unsigned char*>(certChain[0].c_str()),
+                                            static_cast<DWORD>(certChain[0].size()));
+    if (cert.raw == nullptr)
+    {
+        return false;
+    }
+
+    // Let the OS build a certificate chain from the server certificate.
+    char oidPkixKpServerAuth[] = szOID_PKIX_KP_SERVER_AUTH;
+    char oidServerGatedCrypto[] = szOID_SERVER_GATED_CRYPTO;
+    char oidSgcNetscape[] = szOID_SGC_NETSCAPE;
+    char* chainUses[] = {
+        oidPkixKpServerAuth,
+        oidServerGatedCrypto,
+        oidSgcNetscape,
+    };
+    CERT_CHAIN_PARA chainPara = {sizeof(chainPara)};
+    chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+    chainPara.RequestedUsage.Usage.cUsageIdentifier = sizeof(chainUses) / sizeof(char*);
+    chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = chainUses;
+
+    winhttp_cert_chain_context chainContext;
+    if (!CertGetCertificateChain(nullptr,
+                                 cert.raw,
+                                 nullptr,
+                                 cert.raw->hCertStore,
+                                 &chainPara,
+                                 CERT_CHAIN_REVOCATION_CHECK_CHAIN,
+                                 nullptr,
+                                 &chainContext.raw))
+    {
+        return false;
+    }
+
+    // Check to see if the certificate chain is actually trusted.
+    if (chainContext.raw->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR)
+    {
+        return false;
+    }
+
+    auto u16HostName = utility::conversions::to_utf16string(hostname);
+    HTTPSPolicyCallbackData policyData = {
+        {sizeof(policyData)},
+        AUTHTYPE_SERVER,
+        0,
+        &u16HostName[0],
+    };
+    CERT_CHAIN_POLICY_PARA policyPara = {sizeof(policyPara)};
+    policyPara.pvExtraPolicyPara = &policyData;
+    CERT_CHAIN_POLICY_STATUS policyStatus = {sizeof(policyStatus)};
+    if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext.raw, &policyPara, &policyStatus))
+    {
+        return false;
+    }
+
+    if (policyStatus.dwError)
+    {
+        return false;
+    }
+
+    return true;
+}
+#endif
+} // namespace details
+} // namespace client
+} // namespace http
+} // namespace web
+
+#endif
diff --git a/Release/src/http/common/connection_pool_helpers.h b/Release/src/http/common/connection_pool_helpers.h
new file mode 100644 (file)
index 0000000..df6877a
--- /dev/null
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "cpprest/details/cpprest_compat.h"
+#include <memory>
+#include <stddef.h>
+#include <vector>
+
+namespace web
+{
+namespace http
+{
+namespace client
+{
+namespace details
+{
+template<class ConnectionIsh>
+class connection_pool_stack
+{
+public:
+    // attempts to acquire a connection from the deque; returns nullptr if no connection is
+    // available
+    std::shared_ptr<ConnectionIsh> try_acquire() CPPREST_NOEXCEPT
+    {
+        const size_t oldConnectionsSize = m_connections.size();
+        if (oldConnectionsSize == 0)
+        {
+            m_staleBefore = 0;
+            return nullptr;
+        }
+
+        auto result = std::move(m_connections.back());
+        m_connections.pop_back();
+        const size_t newConnectionsSize = m_connections.size();
+        if (m_staleBefore > newConnectionsSize)
+        {
+            m_staleBefore = newConnectionsSize;
+        }
+
+        return result;
+    }
+
+    // releases `released` back to the connection pool
+    void release(std::shared_ptr<ConnectionIsh>&& released) { m_connections.push_back(std::move(released)); }
+
+    bool free_stale_connections() CPPREST_NOEXCEPT
+    {
+        assert(m_staleBefore <= m_connections.size());
+        m_connections.erase(m_connections.begin(), m_connections.begin() + m_staleBefore);
+        const size_t connectionsSize = m_connections.size();
+        m_staleBefore = connectionsSize;
+        return (connectionsSize != 0);
+    }
+
+private:
+    std::vector<std::shared_ptr<ConnectionIsh>> m_connections;
+    size_t m_staleBefore = 0;
+};
+
+} // namespace details
+} // namespace client
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/common/http_compression.cpp b/Release/src/http/common/http_compression.cpp
new file mode 100644 (file)
index 0000000..1596ddc
--- /dev/null
@@ -0,0 +1,1142 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Compression and decompression interfaces
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+// CPPREST_EXCLUDE_COMPRESSION is set if we're on a platform that supports compression but we want to explicitly disable
+// 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 !defined(CPPREST_EXCLUDE_WEBSOCKETS) && !defined(CPPREST_EXCLUDE_COMPRESSION)
+#define CPPREST_HTTP_COMPRESSION
+#endif // !defined(CPPREST_EXCLUDE_WEBSOCKETS) && !defined(CPPREST_EXCLUDE_COMPRESSION)
+
+#if defined(CPPREST_HTTP_COMPRESSION)
+#include <zlib.h>
+// zconf.h may define compress
+#ifdef compress
+#undef compress
+#endif
+#if !defined(CPPREST_EXCLUDE_BROTLI)
+#define CPPREST_BROTLI_COMPRESSION
+#endif // CPPREST_EXCLUDE_BROTLI
+#if defined(CPPREST_BROTLI_COMPRESSION)
+#include <brotli/decode.h>
+#include <brotli/encode.h>
+#endif // CPPREST_BROTLI_COMPRESSION
+#endif
+
+namespace web
+{
+namespace http
+{
+namespace compression
+{
+namespace builtin
+{
+#if defined(CPPREST_HTTP_COMPRESSION)
+// A shared base class for the gzip and deflate compressors
+class zlib_compressor_base : public compress_provider
+{
+public:
+    static const utility::string_t GZIP;
+    static const utility::string_t DEFLATE;
+
+    zlib_compressor_base(int windowBits,
+                         int compressionLevel = Z_DEFAULT_COMPRESSION,
+                         int method = Z_DEFLATED,
+                         int strategy = Z_DEFAULT_STRATEGY,
+                         int memLevel = MAX_MEM_LEVEL)
+        : m_algorithm(windowBits >= 16 ? GZIP : DEFLATE)
+    {
+        m_state = deflateInit2(&m_stream, compressionLevel, method, windowBits, memLevel, strategy);
+    }
+
+    const utility::string_t& algorithm() const { return m_algorithm; }
+
+    size_t compress(const uint8_t* input,
+                    size_t input_size,
+                    uint8_t* output,
+                    size_t output_size,
+                    operation_hint hint,
+                    size_t& input_bytes_processed,
+                    bool& done)
+    {
+        if (m_state == Z_STREAM_END || (hint != operation_hint::is_last && !input_size))
+        {
+            input_bytes_processed = 0;
+            done = (m_state == Z_STREAM_END);
+            return 0;
+        }
+
+        if (m_state != Z_OK && m_state != Z_BUF_ERROR && m_state != Z_STREAM_ERROR)
+        {
+            throw std::runtime_error("Prior unrecoverable compression stream error " + std::to_string(m_state));
+        }
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wtautological-constant-compare"
+#endif // __clang__
+        if (input_size > (std::numeric_limits<uInt>::max)() || output_size > (std::numeric_limits<uInt>::max)())
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // __clang__
+        {
+            throw std::runtime_error("Compression input or output size out of range");
+        }
+
+        m_stream.next_in = const_cast<Bytef*>(input);
+        m_stream.avail_in = static_cast<uInt>(input_size);
+        m_stream.next_out = const_cast<Bytef*>(output);
+        m_stream.avail_out = static_cast<uInt>(output_size);
+
+        m_state = deflate(&m_stream, (hint == operation_hint::is_last) ? Z_FINISH : Z_PARTIAL_FLUSH);
+        if (m_state != Z_OK && m_state != Z_STREAM_ERROR &&
+            !(hint == operation_hint::is_last && (m_state == Z_STREAM_END || m_state == Z_BUF_ERROR)))
+        {
+            throw std::runtime_error("Unrecoverable compression stream error " + std::to_string(m_state));
+        }
+
+        input_bytes_processed = input_size - m_stream.avail_in;
+        done = (m_state == Z_STREAM_END);
+        return output_size - m_stream.avail_out;
+    }
+
+    pplx::task<operation_result> compress(
+        const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint)
+    {
+        operation_result r;
+
+        try
+        {
+            r.output_bytes_produced =
+                compress(input, input_size, output, output_size, hint, r.input_bytes_processed, r.done);
+        }
+        catch (...)
+        {
+            pplx::task_completion_event<operation_result> ev;
+            ev.set_exception(std::current_exception());
+            return pplx::create_task(ev);
+        }
+
+        return pplx::task_from_result<operation_result>(r);
+    }
+
+    void reset()
+    {
+        m_state = deflateReset(&m_stream);
+        if (m_state != Z_OK)
+        {
+            throw std::runtime_error("Failed to reset zlib compressor " + std::to_string(m_state));
+        }
+    }
+
+    ~zlib_compressor_base() { (void)deflateEnd(&m_stream); }
+
+private:
+    int m_state {Z_BUF_ERROR};
+    z_stream m_stream {};
+    const utility::string_t& m_algorithm;
+};
+
+const utility::string_t zlib_compressor_base::GZIP(algorithm::GZIP);
+const utility::string_t zlib_compressor_base::DEFLATE(algorithm::DEFLATE);
+
+// A shared base class for the gzip and deflate decompressors
+class zlib_decompressor_base : public decompress_provider
+{
+public:
+    zlib_decompressor_base(int windowBits)
+        : m_algorithm(windowBits >= 16 ? zlib_compressor_base::GZIP : zlib_compressor_base::DEFLATE)
+    {
+        m_state = inflateInit2(&m_stream, windowBits);
+    }
+
+    const utility::string_t& algorithm() const { return m_algorithm; }
+
+    size_t decompress(const uint8_t* input,
+                      size_t input_size,
+                      uint8_t* output,
+                      size_t output_size,
+                      operation_hint hint,
+                      size_t& input_bytes_processed,
+                      bool& done)
+    {
+        if (m_state == Z_STREAM_END || !input_size)
+        {
+            input_bytes_processed = 0;
+            done = (m_state == Z_STREAM_END);
+            return 0;
+        }
+
+        if (m_state != Z_OK && m_state != Z_BUF_ERROR && m_state != Z_STREAM_ERROR)
+        {
+            throw std::runtime_error("Prior unrecoverable decompression stream error " + std::to_string(m_state));
+        }
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wtautological-constant-compare"
+#endif // __clang__
+        if (input_size > (std::numeric_limits<uInt>::max)() || output_size > (std::numeric_limits<uInt>::max)())
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // __clang__
+        {
+            throw std::runtime_error("Compression input or output size out of range");
+        }
+
+        m_stream.next_in = const_cast<Bytef*>(input);
+        m_stream.avail_in = static_cast<uInt>(input_size);
+        m_stream.next_out = const_cast<Bytef*>(output);
+        m_stream.avail_out = static_cast<uInt>(output_size);
+
+        m_state = inflate(&m_stream, (hint == operation_hint::is_last) ? Z_FINISH : Z_PARTIAL_FLUSH);
+        if (m_state != Z_OK && m_state != Z_STREAM_ERROR && m_state != Z_STREAM_END && m_state != Z_BUF_ERROR)
+        {
+            // Z_BUF_ERROR is a success code for Z_FINISH, and the caller can continue as if operation_hint::is_last was
+            // not given
+            throw std::runtime_error("Unrecoverable decompression stream error " + std::to_string(m_state));
+        }
+
+        input_bytes_processed = input_size - m_stream.avail_in;
+        done = (m_state == Z_STREAM_END);
+        return output_size - m_stream.avail_out;
+    }
+
+    pplx::task<operation_result> decompress(
+        const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint)
+    {
+        operation_result r;
+
+        try
+        {
+            r.output_bytes_produced =
+                decompress(input, input_size, output, output_size, hint, r.input_bytes_processed, r.done);
+        }
+        catch (...)
+        {
+            pplx::task_completion_event<operation_result> ev;
+            ev.set_exception(std::current_exception());
+            return pplx::create_task(ev);
+        }
+
+        return pplx::task_from_result<operation_result>(r);
+    }
+
+    void reset()
+    {
+        m_state = inflateReset(&m_stream);
+        if (m_state != Z_OK)
+        {
+            throw std::runtime_error("Failed to reset zlib decompressor " + std::to_string(m_state));
+        }
+    }
+
+    ~zlib_decompressor_base() { (void)inflateEnd(&m_stream); }
+
+private:
+    int m_state {Z_BUF_ERROR};
+    z_stream m_stream {};
+    const utility::string_t& m_algorithm;
+};
+
+class gzip_compressor : public zlib_compressor_base
+{
+public:
+    gzip_compressor() : zlib_compressor_base(31) // 15 is MAX_WBITS in zconf.h; add 16 for gzip
+    {
+    }
+
+    gzip_compressor(int compressionLevel, int method, int strategy, int memLevel)
+        : zlib_compressor_base(31, compressionLevel, method, strategy, memLevel)
+    {
+    }
+};
+
+class gzip_decompressor : public zlib_decompressor_base
+{
+public:
+    gzip_decompressor() : zlib_decompressor_base(31) // 15 is MAX_WBITS in zconf.h; add 16 for gzip
+    {
+    }
+};
+
+class deflate_compressor : public zlib_compressor_base
+{
+public:
+    deflate_compressor() : zlib_compressor_base(15) // 15 is MAX_WBITS in zconf.h
+    {
+    }
+
+    deflate_compressor(int compressionLevel, int method, int strategy, int memLevel)
+        : zlib_compressor_base(15, compressionLevel, method, strategy, memLevel)
+    {
+    }
+};
+
+class deflate_decompressor : public zlib_decompressor_base
+{
+public:
+    deflate_decompressor() : zlib_decompressor_base(0) // deflate auto-detect
+    {
+    }
+};
+
+#if defined(CPPREST_BROTLI_COMPRESSION)
+class brotli_compressor : public compress_provider
+{
+public:
+    static const utility::string_t BROTLI;
+
+    brotli_compressor(uint32_t window = BROTLI_DEFAULT_WINDOW,
+                      uint32_t quality = BROTLI_DEFAULT_QUALITY,
+                      uint32_t mode = BROTLI_DEFAULT_MODE,
+                      uint32_t block = 0,
+                      uint32_t nomodel = 0,
+                      uint32_t hint = 0)
+        : m_window(window)
+        , m_quality(quality)
+        , m_mode(mode)
+        , m_block(block)
+        , m_nomodel(nomodel)
+        , m_hint(hint)
+        , m_algorithm(BROTLI)
+    {
+        (void)reset();
+    }
+
+    const utility::string_t& algorithm() const { return m_algorithm; }
+
+    size_t compress(const uint8_t* input,
+                    size_t input_size,
+                    uint8_t* output,
+                    size_t output_size,
+                    operation_hint hint,
+                    size_t& input_bytes_processed,
+                    bool& done)
+    {
+        if (m_done || (hint != operation_hint::is_last && !input_size))
+        {
+            input_bytes_processed = 0;
+            done = m_done;
+            return 0;
+        }
+
+        if (m_state != BROTLI_TRUE)
+        {
+            throw std::runtime_error("Prior unrecoverable compression stream error");
+        }
+
+        const uint8_t* next_in = input;
+        size_t avail_in = 0;
+        uint8_t* next_out = output;
+        size_t avail_out = output_size;
+        size_t total_out;
+
+        if (BrotliEncoderHasMoreOutput(m_stream) == BROTLI_TRUE)
+        {
+            // Drain any compressed bytes remaining from a prior call
+            do
+            {
+                m_state = BrotliEncoderCompressStream(
+                    m_stream, BROTLI_OPERATION_FLUSH, &avail_in, &next_in, &avail_out, &next_out, &total_out);
+            } while (m_state == BROTLI_TRUE && avail_out && BrotliEncoderHasMoreOutput(m_stream) == BROTLI_TRUE);
+        }
+
+        if (m_state == BROTLI_TRUE && avail_out && input_size)
+        {
+            // Compress the caller-supplied buffer
+            avail_in = input_size;
+            do
+            {
+                m_state = BrotliEncoderCompressStream(
+                    m_stream, BROTLI_OPERATION_FLUSH, &avail_in, &next_in, &avail_out, &next_out, &total_out);
+            } while (m_state == BROTLI_TRUE && avail_out && BrotliEncoderHasMoreOutput(m_stream) == BROTLI_TRUE);
+        }
+        else
+        {
+            // We're not compressing any new data; ensure calculation sanity
+            input_size = 0;
+        }
+
+        if (m_state != BROTLI_TRUE)
+        {
+            throw std::runtime_error("Unrecoverable compression stream error");
+        }
+
+        if (hint == operation_hint::is_last)
+        {
+            if (avail_out)
+            {
+                // Make one more pass to finalize the compressed stream
+                _ASSERTE(!avail_in);
+                m_state = BrotliEncoderCompressStream(
+                    m_stream, BROTLI_OPERATION_FINISH, &avail_in, &next_in, &avail_out, &next_out, &total_out);
+                if (m_state != BROTLI_TRUE)
+                {
+                    throw std::runtime_error("Unrecoverable error finalizing compression stream");
+                }
+                m_done = (BrotliEncoderIsFinished(m_stream) == BROTLI_TRUE);
+            }
+        }
+
+        input_bytes_processed = input_size - avail_in;
+        done = m_done;
+        return output_size - avail_out;
+    }
+
+    pplx::task<operation_result> compress(
+        const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint)
+    {
+        operation_result r;
+
+        try
+        {
+            r.output_bytes_produced =
+                compress(input, input_size, output, output_size, hint, r.input_bytes_processed, r.done);
+        }
+        catch (...)
+        {
+            pplx::task_completion_event<operation_result> ev;
+            ev.set_exception(std::current_exception());
+            return pplx::create_task(ev);
+        }
+
+        return pplx::task_from_result<operation_result>(r);
+    }
+
+    void reset()
+    {
+        if (m_stream)
+        {
+            BrotliEncoderDestroyInstance(m_stream);
+        }
+
+        m_stream = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
+        m_state = m_stream ? BROTLI_TRUE : BROTLI_FALSE;
+
+        if (m_state == BROTLI_TRUE && m_window != BROTLI_DEFAULT_WINDOW)
+        {
+            m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_LGWIN, m_window);
+        }
+        if (m_state == BROTLI_TRUE && m_quality != BROTLI_DEFAULT_QUALITY)
+        {
+            m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_QUALITY, m_quality);
+        }
+        if (m_state == BROTLI_TRUE && m_mode != BROTLI_DEFAULT_MODE)
+        {
+            m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_MODE, m_mode);
+        }
+        if (m_state == BROTLI_TRUE && m_block != 0)
+        {
+            m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_LGBLOCK, m_block);
+        }
+        if (m_state == BROTLI_TRUE && m_nomodel != 0)
+        {
+            m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING, m_nomodel);
+        }
+        if (m_state == BROTLI_TRUE && m_hint != 0)
+        {
+            m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_SIZE_HINT, m_hint);
+        }
+
+        if (m_state != BROTLI_TRUE)
+        {
+            throw std::runtime_error("Failed to reset Brotli compressor");
+        }
+    }
+
+    ~brotli_compressor()
+    {
+        if (m_stream)
+        {
+            BrotliEncoderDestroyInstance(m_stream);
+        }
+    }
+
+private:
+    BROTLI_BOOL m_state {BROTLI_FALSE};
+    BrotliEncoderState* m_stream {nullptr};
+    bool m_done {false};
+    uint32_t m_window;
+    uint32_t m_quality;
+    uint32_t m_mode;
+    uint32_t m_block;
+    uint32_t m_nomodel;
+    uint32_t m_hint;
+    const utility::string_t& m_algorithm;
+};
+
+const utility::string_t brotli_compressor::BROTLI(algorithm::BROTLI);
+
+class brotli_decompressor : public decompress_provider
+{
+public:
+    brotli_decompressor() : m_algorithm(brotli_compressor::BROTLI)
+    {
+        try
+        {
+            reset();
+        }
+        catch (...)
+        {
+        }
+    }
+
+    const utility::string_t& algorithm() const { return m_algorithm; }
+
+    size_t decompress(const uint8_t* input,
+                      size_t input_size,
+                      uint8_t* output,
+                      size_t output_size,
+                      operation_hint hint,
+                      size_t& input_bytes_processed,
+                      bool& done)
+    {
+        if (m_state == BROTLI_DECODER_RESULT_SUCCESS /* || !input_size*/)
+        {
+            input_bytes_processed = 0;
+            done = (m_state == BROTLI_DECODER_RESULT_SUCCESS);
+            return 0;
+        }
+
+        if (m_state == BROTLI_DECODER_RESULT_ERROR)
+        {
+            throw std::runtime_error("Prior unrecoverable decompression stream error");
+        }
+
+        const uint8_t* next_in = input;
+        size_t avail_in = input_size;
+        uint8_t* next_out = output;
+        size_t avail_out = output_size;
+        size_t total_out;
+
+        // N.B. we ignore 'hint' here.  We could instead call BrotliDecoderDecompress() if it's set, but we'd either
+        // have to first allocate a guaranteed-large-enough buffer and then copy out of it, or we'd have to call
+        // reset() if it failed due to insufficient output buffer space (and we'd need to use
+        // BrotliDecoderGetErrorCode() to tell if that's why it failed)
+        (void)hint;
+        m_state = BrotliDecoderDecompressStream(m_stream, &avail_in, &next_in, &avail_out, &next_out, &total_out);
+        if (m_state == BROTLI_DECODER_RESULT_ERROR)
+        {
+            throw std::runtime_error("Unrecoverable decompression stream error");
+        }
+
+        input_bytes_processed = input_size - avail_in;
+        done = (m_state == BROTLI_DECODER_RESULT_SUCCESS);
+        return output_size - avail_out;
+    }
+
+    pplx::task<operation_result> decompress(
+        const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint)
+    {
+        operation_result r;
+
+        try
+        {
+            r.output_bytes_produced =
+                decompress(input, input_size, output, output_size, hint, r.input_bytes_processed, r.done);
+        }
+        catch (...)
+        {
+            pplx::task_completion_event<operation_result> ev;
+            ev.set_exception(std::current_exception());
+            return pplx::create_task(ev);
+        }
+
+        return pplx::task_from_result<operation_result>(r);
+    }
+
+    void reset()
+    {
+        if (m_stream)
+        {
+            BrotliDecoderDestroyInstance(m_stream);
+        }
+
+        m_stream = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+        m_state = m_stream ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT : BROTLI_DECODER_RESULT_ERROR;
+
+        if (m_state != BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT)
+        {
+            throw std::runtime_error("Failed to reset Brotli decompressor");
+        }
+    }
+
+    ~brotli_decompressor()
+    {
+        if (m_stream)
+        {
+            BrotliDecoderDestroyInstance(m_stream);
+        }
+    }
+
+private:
+    BrotliDecoderResult m_state {BROTLI_DECODER_RESULT_ERROR};
+    BrotliDecoderState* m_stream {nullptr};
+    const utility::string_t& m_algorithm;
+};
+#endif // CPPREST_BROTLI_COMPRESSION
+#endif // CPPREST_HTTP_COMPRESSION
+
+// Generic internal implementation of the compress_factory API
+class generic_compress_factory : public compress_factory
+{
+public:
+    ~generic_compress_factory() CPPREST_NOEXCEPT {}
+    generic_compress_factory(const utility::string_t& algorithm,
+                             std::function<std::unique_ptr<compress_provider>()> make_compressor)
+        : m_algorithm(algorithm), _make_compressor(make_compressor)
+    {
+    }
+
+    const utility::string_t& algorithm() const { return m_algorithm; }
+
+    std::unique_ptr<compress_provider> make_compressor() const { return _make_compressor(); }
+
+private:
+    const utility::string_t m_algorithm;
+    std::function<std::unique_ptr<compress_provider>()> _make_compressor;
+};
+
+// Generic internal implementation of the decompress_factory API
+class generic_decompress_factory : public decompress_factory
+{
+public:
+    ~generic_decompress_factory() CPPREST_NOEXCEPT {}
+    generic_decompress_factory(const utility::string_t& algorithm,
+                               uint16_t weight,
+                               std::function<std::unique_ptr<decompress_provider>()> make_decompressor)
+        : m_algorithm(algorithm), m_weight(weight), _make_decompressor(make_decompressor)
+    {
+    }
+
+    const utility::string_t& algorithm() const { return m_algorithm; }
+
+    uint16_t weight() const { return m_weight; }
+
+    std::unique_ptr<decompress_provider> make_decompressor() const { return _make_decompressor(); }
+
+private:
+    const utility::string_t m_algorithm;
+    uint16_t m_weight;
+    std::function<std::unique_ptr<decompress_provider>()> _make_decompressor;
+};
+
+// "Private" algorithm-to-factory tables for namespace static helpers
+static const std::vector<std::shared_ptr<compress_factory>> g_compress_factories
+#if defined(CPPREST_HTTP_COMPRESSION)
+    = {std::make_shared<generic_compress_factory>(
+           algorithm::GZIP,
+           []() -> std::unique_ptr<compress_provider> { return utility::details::make_unique<gzip_compressor>(); }),
+       std::make_shared<generic_compress_factory>(
+           algorithm::DEFLATE,
+           []() -> std::unique_ptr<compress_provider> { return utility::details::make_unique<deflate_compressor>(); }),
+#if defined(CPPREST_BROTLI_COMPRESSION)
+       std::make_shared<generic_compress_factory>(
+           algorithm::BROTLI,
+           []() -> std::unique_ptr<compress_provider> { return utility::details::make_unique<brotli_compressor>(); })
+#endif // CPPREST_BROTLI_COMPRESSION
+};
+#else  // CPPREST_HTTP_COMPRESSION
+    ;
+#endif // CPPREST_HTTP_COMPRESSION
+
+static const std::vector<std::shared_ptr<decompress_factory>> g_decompress_factories
+#if defined(CPPREST_HTTP_COMPRESSION)
+    = {std::make_shared<generic_decompress_factory>(
+           algorithm::GZIP,
+           500,
+           []() -> std::unique_ptr<decompress_provider> { return utility::details::make_unique<gzip_decompressor>(); }),
+       std::make_shared<generic_decompress_factory>(algorithm::DEFLATE,
+                                                    500,
+                                                    []() -> std::unique_ptr<decompress_provider> {
+                                                        return utility::details::make_unique<deflate_decompressor>();
+                                                    }),
+#if defined(CPPREST_BROTLI_COMPRESSION)
+       std::make_shared<generic_decompress_factory>(algorithm::BROTLI,
+                                                    500,
+                                                    []() -> std::unique_ptr<decompress_provider> {
+                                                        return utility::details::make_unique<brotli_decompressor>();
+                                                    })
+#endif // CPPREST_BROTLI_COMPRESSION
+};
+#else  // CPPREST_HTTP_COMPRESSION
+    ;
+#endif // CPPREST_HTTP_COMPRESSION
+
+bool supported() { return !g_compress_factories.empty(); }
+
+bool algorithm::supported(const utility::string_t& algorithm)
+{
+    for (auto& factory : g_compress_factories)
+    {
+        if (utility::details::str_iequal(algorithm, factory->algorithm()))
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static std::unique_ptr<compress_provider> _make_compressor(
+    const std::vector<std::shared_ptr<compress_factory>>& factories, const utility::string_t& algorithm)
+{
+    for (auto& factory : factories)
+    {
+        if (factory && utility::details::str_iequal(algorithm, factory->algorithm()))
+        {
+            return factory->make_compressor();
+        }
+    }
+
+    return std::unique_ptr<compress_provider>();
+}
+
+std::unique_ptr<compress_provider> make_compressor(const utility::string_t& algorithm)
+{
+    return _make_compressor(g_compress_factories, algorithm);
+}
+
+static std::unique_ptr<decompress_provider> _make_decompressor(
+    const std::vector<std::shared_ptr<decompress_factory>>& factories, const utility::string_t& algorithm)
+{
+    for (auto& factory : factories)
+    {
+        if (factory && utility::details::str_iequal(algorithm, factory->algorithm()))
+        {
+            return factory->make_decompressor();
+        }
+    }
+
+    return std::unique_ptr<decompress_provider>();
+}
+
+std::unique_ptr<decompress_provider> make_decompressor(const utility::string_t& algorithm)
+{
+    return _make_decompressor(g_decompress_factories, algorithm);
+}
+
+std::shared_ptr<compress_factory> get_compress_factory(const utility::string_t& algorithm)
+{
+    for (auto& factory : g_compress_factories)
+    {
+        if (utility::details::str_iequal(algorithm, factory->algorithm()))
+        {
+            return factory;
+        }
+    }
+
+    return std::shared_ptr<compress_factory>();
+}
+
+std::shared_ptr<decompress_factory> get_decompress_factory(const utility::string_t& algorithm)
+{
+    for (auto& factory : g_decompress_factories)
+    {
+        if (utility::details::str_iequal(algorithm, factory->algorithm()))
+        {
+            return factory;
+        }
+    }
+
+    return std::shared_ptr<decompress_factory>();
+}
+
+std::unique_ptr<compress_provider> make_gzip_compressor(int compressionLevel, int method, int strategy, int memLevel)
+{
+#if defined(CPPREST_HTTP_COMPRESSION)
+    return utility::details::make_unique<gzip_compressor>(compressionLevel, method, strategy, memLevel);
+#else  // CPPREST_HTTP_COMPRESSION
+    (void)compressionLevel;
+    (void)method;
+    (void)strategy;
+    (void)memLevel;
+    return std::unique_ptr<compress_provider>();
+#endif // CPPREST_HTTP_COMPRESSION
+}
+
+std::unique_ptr<compress_provider> make_deflate_compressor(int compressionLevel, int method, int strategy, int memLevel)
+{
+#if defined(CPPREST_HTTP_COMPRESSION)
+    return utility::details::make_unique<deflate_compressor>(compressionLevel, method, strategy, memLevel);
+#else  // CPPREST_HTTP_COMPRESSION
+    (void)compressionLevel;
+    (void)method;
+    (void)strategy;
+    (void)memLevel;
+    return std::unique_ptr<compress_provider>();
+#endif // CPPREST_HTTP_COMPRESSION
+}
+
+std::unique_ptr<compress_provider> make_brotli_compressor(
+    uint32_t window, uint32_t quality, uint32_t mode, uint32_t block, uint32_t nomodel, uint32_t hint)
+{
+#if defined(CPPREST_HTTP_COMPRESSION) && defined(CPPREST_BROTLI_COMPRESSION)
+    return utility::details::make_unique<brotli_compressor>(window, quality, mode, block, nomodel, hint);
+#else  // CPPREST_BROTLI_COMPRESSION
+    (void)window;
+    (void)quality;
+    (void)mode;
+    (void)block;
+    (void)nomodel;
+    (void)hint;
+    return std::unique_ptr<compress_provider>();
+#endif // CPPREST_BROTLI_COMPRESSION
+}
+} // namespace builtin
+
+std::shared_ptr<compress_factory> make_compress_factory(
+    const utility::string_t& algorithm, std::function<std::unique_ptr<compress_provider>()> make_compressor)
+{
+    return std::make_shared<builtin::generic_compress_factory>(algorithm, make_compressor);
+}
+
+std::shared_ptr<decompress_factory> make_decompress_factory(
+    const utility::string_t& algorithm,
+    uint16_t weight,
+    std::function<std::unique_ptr<decompress_provider>()> make_decompressor)
+{
+    return std::make_shared<builtin::generic_decompress_factory>(algorithm, weight, make_decompressor);
+}
+
+namespace details
+{
+namespace builtin
+{
+const std::vector<std::shared_ptr<decompress_factory>> get_decompress_factories()
+{
+    return web::http::compression::builtin::g_decompress_factories;
+}
+} // namespace builtin
+
+static bool is_http_whitespace(const utility::char_t ch) { return ch == _XPLATSTR(' ') || ch == _XPLATSTR('\t'); }
+
+static void remove_surrounding_http_whitespace(const utility::string_t& encoding, size_t& start, size_t& length)
+{
+    while (length > 0 && is_http_whitespace(encoding.at(start)))
+    {
+        start++;
+        length--;
+    }
+    while (length > 0 && is_http_whitespace(encoding.at(start + length - 1)))
+    {
+        length--;
+    }
+}
+
+std::unique_ptr<compress_provider> get_compressor_from_header(
+    const utility::string_t& encoding,
+    header_types type,
+    const std::vector<std::shared_ptr<compress_factory>>& factories)
+{
+    const std::vector<std::shared_ptr<compress_factory>>& f =
+        factories.empty() ? web::http::compression::builtin::g_compress_factories : factories;
+    std::unique_ptr<compress_provider> compressor;
+    struct _tuple
+    {
+        size_t start;
+        size_t length;
+        size_t rank;
+    } t;
+    std::vector<_tuple> tokens;
+    size_t highest;
+    size_t mark;
+    size_t end;
+    size_t n;
+    bool first;
+
+    _ASSERTE(type == header_types::te || type == header_types::accept_encoding);
+
+    // See https://tools.ietf.org/html/rfc7230#section-4.3 (TE) and
+    // https://tools.ietf.org/html/rfc7231#section-5.3.4 (Accept-Encoding) for details
+
+    n = 0;
+    highest = 0;
+    first = true;
+    while (n != utility::string_t::npos)
+    {
+        // Tokenize by commas first
+        mark = encoding.find(_XPLATSTR(','), n);
+        t.start = n;
+        t.rank = static_cast<size_t>(-1);
+        if (mark == utility::string_t::npos)
+        {
+            t.length = encoding.size() - n;
+            n = utility::string_t::npos;
+        }
+        else
+        {
+            t.length = mark - n;
+            n = mark + 1;
+        }
+
+        // Then remove leading and trailing whitespace
+        remove_surrounding_http_whitespace(encoding, t.start, t.length);
+
+        // Next split at the semicolon, if any, and deal with rank and additional whitespace
+        mark = encoding.find(_XPLATSTR(';'), t.start);
+        if (mark < t.start + t.length)
+        {
+            end = t.start + t.length - 1;
+            t.length = mark - t.start;
+            while (t.length > 0 && is_http_whitespace(encoding.at(t.start + t.length - 1)))
+            {
+                // Skip trailing whitespace in encoding type
+                t.length--;
+            }
+            if (mark < end)
+            {
+                // Check for an optional ranking, max. length "q=0.999"
+                mark = encoding.find(_XPLATSTR("q="), mark + 1);
+                if (mark != utility::string_t::npos && mark + 1 < end && end - mark <= 6)
+                {
+                    // Determine ranking; leading whitespace has been implicitly skipped by find().
+                    // The ranking always starts with '1' or '0' per standard, and has at most 3 decimal places
+                    mark += 1;
+                    t.rank = 1000 * (encoding.at(mark + 1) - _XPLATSTR('0'));
+                    if (mark + 2 < end && encoding.at(mark + 2) == _XPLATSTR('.'))
+                    {
+                        // This is a real number rank; convert decimal part to hundreds and apply it
+                        size_t factor = 100;
+                        mark += 2;
+                        for (size_t i = mark + 1; i <= end; i++)
+                        {
+                            t.rank += (encoding.at(i) - _XPLATSTR('0')) * factor;
+                            factor /= 10;
+                        }
+                    }
+                    if (t.rank > 1000)
+                    {
+                        throw http_exception(status_codes::BadRequest, "Invalid q-value in header");
+                    }
+                }
+            }
+        }
+
+        if (!t.length)
+        {
+            if (!first || n != utility::string_t::npos)
+            {
+                // An entirely empty header is OK per RFC, but an extraneous comma is not
+                throw http_exception(status_codes::BadRequest, "Empty field in header");
+            }
+            return std::unique_ptr<compress_provider>();
+        }
+
+        if (!compressor)
+        {
+            if (t.rank == static_cast<size_t>(1000) || t.rank == static_cast<size_t>(-1))
+            {
+                // Immediately try to instantiate a compressor for any unranked or top-ranked algorithm
+                compressor = web::http::compression::builtin::_make_compressor(f, encoding.substr(t.start, t.length));
+            }
+            else if (t.rank)
+            {
+                // Store off remaining ranked algorithms, sorting as we go
+                if (t.rank >= highest)
+                {
+                    tokens.emplace_back(t);
+                    highest = t.rank;
+                }
+                else
+                {
+                    for (auto x = tokens.begin(); x != tokens.end(); x++)
+                    {
+                        if (t.rank <= x->rank)
+                        {
+                            tokens.emplace(x, t);
+                            break;
+                        }
+                    }
+                }
+            }
+            // else a rank of 0 means "not permitted"
+        }
+        // else we've chosen a compressor; we're just validating the rest of the header
+
+        first = false;
+    }
+    // Note: for Accept-Encoding, we don't currently explicitly handle "identity;q=0" and "*;q=0"
+
+    if (compressor)
+    {
+        return compressor;
+    }
+
+    // If we're here, we didn't match the caller's compressor above;
+    // try any that we saved off in order of highest to lowest rank
+    for (auto t = tokens.rbegin(); t != tokens.rend(); t++)
+    {
+        auto coding = encoding.substr(t->start, t->length);
+
+        // N.B for TE, "trailers" will simply fail to instantiate a
+        // compressor; ditto for "*" and "identity" for Accept-Encoding
+        auto compressor = web::http::compression::builtin::_make_compressor(f, coding);
+        if (compressor)
+        {
+            return compressor;
+        }
+        if (type == header_types::accept_encoding && utility::details::str_iequal(coding, _XPLATSTR("identity")))
+        {
+            // The client specified a preference for "no encoding" vs. anything else we might still have
+            return std::unique_ptr<compress_provider>();
+        }
+    }
+
+    return std::unique_ptr<compress_provider>();
+}
+
+std::unique_ptr<decompress_provider> get_decompressor_from_header(
+    const utility::string_t& encoding,
+    header_types type,
+    const std::vector<std::shared_ptr<decompress_factory>>& factories)
+{
+    const std::vector<std::shared_ptr<decompress_factory>>& f =
+        factories.empty() ? web::http::compression::builtin::g_decompress_factories : factories;
+    std::unique_ptr<decompress_provider> decompressor;
+    utility::string_t token;
+    size_t start;
+    size_t length;
+    size_t comma;
+    size_t n;
+
+    _ASSERTE(type == header_types::transfer_encoding || type == header_types::content_encoding);
+
+    n = 0;
+    while (n != utility::string_t::npos)
+    {
+        // Tokenize by commas first
+        comma = encoding.find(_XPLATSTR(','), n);
+        start = n;
+        if (comma == utility::string_t::npos)
+        {
+            length = encoding.size() - n;
+            n = utility::string_t::npos;
+        }
+        else
+        {
+            length = comma - n;
+            n = comma + 1;
+        }
+
+        // Then remove leading and trailing whitespace
+        remove_surrounding_http_whitespace(encoding, start, length);
+
+        if (!length)
+        {
+            throw http_exception(status_codes::BadRequest, "Empty field in header");
+        }
+
+        // Immediately try to instantiate a decompressor
+        token = encoding.substr(start, length);
+        auto d = web::http::compression::builtin::_make_decompressor(f, token);
+        if (d)
+        {
+            if (decompressor)
+            {
+                status_code code = status_codes::NotImplemented;
+                if (type == header_types::content_encoding)
+                {
+                    code = status_codes::UnsupportedMediaType;
+                }
+                throw http_exception(code, "Multiple compression algorithms not supported for a single request");
+            }
+
+            // We found our decompressor; store it off while we process the rest of the header
+            decompressor = std::move(d);
+        }
+        else
+        {
+            if (n != utility::string_t::npos)
+            {
+                if (type == header_types::transfer_encoding &&
+                    utility::details::str_iequal(_XPLATSTR("chunked"), token))
+                {
+                    throw http_exception(status_codes::BadRequest,
+                                         "Chunked must come last in the Transfer-Encoding header");
+                }
+            }
+            if (!decompressor && !f.empty() && (n != utility::string_t::npos || type == header_types::content_encoding))
+            {
+                // The first encoding type did not match; throw an informative
+                // exception with an encoding-type-appropriate HTTP error code
+                status_code code = status_codes::NotImplemented;
+                if (type == header_types::content_encoding)
+                {
+                    code = status_codes::UnsupportedMediaType;
+                }
+                throw http_exception(code, "Unsupported encoding type");
+            }
+        }
+    }
+
+    if (type == header_types::transfer_encoding && !utility::details::str_iequal(_XPLATSTR("chunked"), token))
+    {
+        throw http_exception(status_codes::BadRequest, "Transfer-Encoding header missing chunked");
+    }
+
+    // Either the response is compressed and we have a decompressor that can handle it, or
+    // built-in compression is not enabled and we don't have an alternate set of decompressors
+    return decompressor;
+}
+
+utility::string_t build_supported_header(header_types type,
+                                         const std::vector<std::shared_ptr<decompress_factory>>& factories)
+{
+    const std::vector<std::shared_ptr<decompress_factory>>& f =
+        factories.empty() ? web::http::compression::builtin::g_decompress_factories : factories;
+    utility::string_t result;
+    bool start;
+
+    _ASSERTE(type == header_types::te || type == header_types::accept_encoding);
+
+    // Add all specified algorithms and their weights to the header
+    start = true;
+    for (auto& factory : f)
+    {
+        if (factory)
+        {
+            auto weight = factory->weight();
+
+            if (!start)
+            {
+                result += _XPLATSTR(", ");
+            }
+            result += factory->algorithm();
+            if (weight <= 1000)
+            {
+                result += _XPLATSTR(";q=");
+                result += utility::conversions::details::to_string_t(weight / 1000);
+                result += _XPLATSTR('.');
+                result += utility::conversions::details::to_string_t(weight % 1000);
+            }
+            start = false;
+        }
+    }
+
+    if (start && type == header_types::accept_encoding)
+    {
+        // Request that no encoding be applied
+        result += _XPLATSTR("identity;q=1, *;q=0");
+    }
+
+    return result;
+}
+} // namespace details
+} // namespace compression
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/common/http_helpers.cpp b/Release/src/http/common/http_helpers.cpp
new file mode 100644 (file)
index 0000000..9ffbd20
--- /dev/null
@@ -0,0 +1,132 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Implementation Details of the http.h layer of messaging
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "internal_http_helpers.h"
+
+using namespace web;
+using namespace utility;
+using namespace utility::conversions;
+
+namespace web
+{
+namespace http
+{
+namespace details
+{
+// Remove once VS 2013 is no longer supported.
+#if defined(_WIN32) && _MSC_VER < 1900
+static const http_status_to_phrase idToPhraseMap[] = {
+#define _PHRASES
+#define DAT(a, b, c) {status_codes::a, c},
+#include "cpprest/details/http_constants.dat"
+#undef _PHRASES
+#undef DAT
+};
+#endif
+utility::string_t get_default_reason_phrase(status_code code)
+{
+#if !defined(_WIN32) || _MSC_VER >= 1900
+    // Future improvement: why is this stored as an array of structs instead of a map
+    // indexed on the status code for faster lookup?
+    // Not a big deal because it is uncommon to not include a reason phrase.
+    static const http_status_to_phrase idToPhraseMap[] = {
+#define _PHRASES
+#define DAT(a, b, c) {status_codes::a, c},
+#include "cpprest/details/http_constants.dat"
+#undef _PHRASES
+#undef DAT
+    };
+#endif
+
+    utility::string_t phrase;
+    for (const auto& elm : idToPhraseMap)
+    {
+        if (elm.id == code)
+        {
+            phrase = elm.phrase;
+            break;
+        }
+    }
+    return phrase;
+}
+
+size_t chunked_encoding::add_chunked_delimiters(_Out_writes_(buffer_size) uint8_t* data,
+                                                _In_ size_t buffer_size,
+                                                size_t bytes_read)
+{
+    size_t offset = 0;
+
+    if (buffer_size < bytes_read + http::details::chunked_encoding::additional_encoding_space)
+    {
+        throw http_exception(_XPLATSTR("Insufficient buffer size."));
+    }
+
+    if (bytes_read == 0)
+    {
+        offset = 7;
+        data[7] = '0';
+        data[8] = '\r';
+        data[9] = '\n'; // The end of the size.
+        data[10] = '\r';
+        data[11] = '\n'; // The end of the message.
+    }
+    else
+    {
+        char buffer[9];
+#ifdef _WIN32
+        sprintf_s(buffer, sizeof(buffer), "%8IX", bytes_read);
+#else
+        snprintf(buffer, sizeof(buffer), "%8zX", bytes_read);
+#endif
+        memcpy(&data[0], buffer, 8);
+        while (data[offset] == ' ')
+            ++offset;
+        data[8] = '\r';
+        data[9] = '\n'; // The end of the size.
+        data[10 + bytes_read] = '\r';
+        data[11 + bytes_read] = '\n'; // The end of the chunk.
+    }
+
+    return offset;
+}
+
+static const std::array<bool, 128> valid_chars = {{
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
+    0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32-47
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48-63
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64-79
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 80-95
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96-111
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0  // 112-127
+}};
+
+// Checks if the method contains any invalid characters
+bool validate_method(const utility::string_t& method)
+{
+    for (const auto& ch : method)
+    {
+        size_t ch_sz = static_cast<size_t>(ch);
+        if (ch_sz >= 128) return false;
+
+        if (!valid_chars[ch_sz]) return false;
+    }
+
+    return true;
+}
+
+} // namespace details
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/common/http_msg.cpp b/Release/src/http/common/http_msg.cpp
new file mode 100644 (file)
index 0000000..a3c51c6
--- /dev/null
@@ -0,0 +1,1187 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Request and reply message definitions.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "../common/internal_http_helpers.h"
+#include "cpprest/producerconsumerstream.h"
+#include <sstream>
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace utility::conversions;
+using namespace http::details;
+
+namespace web
+{
+namespace http
+{
+#define CRLF _XPLATSTR("\r\n")
+
+utility::string_t http_headers::content_type() const
+{
+    utility::string_t result;
+    match(http::header_names::content_type, result);
+    return result;
+}
+
+/// Helper functions to convert a series of bytes from a charset to utf-8 or utf-16.
+/// These APIs deal with checking for and handling byte order marker (BOM).
+namespace
+{
+enum endianness
+{
+    little_endian,
+    big_endian,
+    unknown
+};
+endianness check_byte_order_mark(const utf16string& str)
+{
+    if (str.empty())
+    {
+        return unknown;
+    }
+    const unsigned char* src = reinterpret_cast<const unsigned char*>(str.data());
+
+    // little endian
+    if (src[0] == 0xFF && src[1] == 0xFE)
+    {
+        return little_endian;
+    }
+
+    // big endian
+    else if (src[0] == 0xFE && src[1] == 0xFF)
+    {
+        return big_endian;
+    }
+
+    return unknown;
+}
+
+std::string convert_utf16le_to_utf8(utf16string src, bool erase_bom)
+{
+    if (erase_bom && !src.empty())
+    {
+        src.erase(0, 1);
+    }
+    return utf16_to_utf8(std::move(src));
+}
+
+utility::string_t convert_utf16le_to_string_t(utf16string src, bool erase_bom)
+{
+    if (erase_bom && !src.empty())
+    {
+        src.erase(0, 1);
+    }
+#ifdef _UTF16_STRINGS
+    return src;
+#else
+    return utf16_to_utf8(std::move(src));
+#endif
+}
+
+// Helper function to change endian ness from big endian to little endian
+utf16string big_endian_to_little_endian(utf16string src, bool erase_bom)
+{
+    if (erase_bom && !src.empty())
+    {
+        src.erase(0, 1);
+    }
+    if (src.empty())
+    {
+        return src;
+    }
+
+    const size_t size = src.size();
+    for (size_t i = 0; i < size; ++i)
+    {
+        utf16char ch = src[i];
+        src[i] = static_cast<utf16char>(ch << 8);
+        src[i] = static_cast<utf16char>(src[i] | ch >> 8);
+    }
+
+    return src;
+}
+
+std::string convert_utf16be_to_utf8(utf16string src, bool erase_bom)
+{
+    return utf16_to_utf8(big_endian_to_little_endian(std::move(src), erase_bom));
+}
+
+utf16string convert_utf16be_to_utf16le(utf16string src, bool erase_bom)
+{
+    return big_endian_to_little_endian(std::move(src), erase_bom);
+}
+
+utility::string_t convert_utf16be_to_string_t(utf16string src, bool erase_bom)
+{
+#ifdef _UTF16_STRINGS
+    return convert_utf16be_to_utf16le(std::move(src), erase_bom);
+#else
+    return convert_utf16be_to_utf8(std::move(src), erase_bom);
+#endif
+}
+
+std::string convert_utf16_to_utf8(utf16string src)
+{
+    const endianness endian = check_byte_order_mark(src);
+    switch (endian)
+    {
+        case little_endian: return convert_utf16le_to_utf8(std::move(src), true);
+        case big_endian: return convert_utf16be_to_utf8(std::move(src), true);
+        case unknown:
+            // unknown defaults to big endian.
+            return convert_utf16be_to_utf8(std::move(src), false);
+    }
+    __assume(0);
+}
+
+utf16string convert_utf16_to_utf16(utf16string src)
+{
+    const endianness endian = check_byte_order_mark(src);
+    switch (endian)
+    {
+        case little_endian: src.erase(0, 1); return src;
+        case big_endian: return convert_utf16be_to_utf16le(std::move(src), true);
+        case unknown:
+            // unknown defaults to big endian.
+            return convert_utf16be_to_utf16le(std::move(src), false);
+    }
+    __assume(0);
+}
+utility::string_t convert_utf16_to_string_t(utf16string src)
+{
+#ifdef _UTF16_STRINGS
+    return convert_utf16_to_utf16(std::move(src));
+#else
+    return convert_utf16_to_utf8(std::move(src));
+#endif
+}
+} // namespace
+
+void http_headers::set_content_type(utility::string_t type)
+{
+    m_headers[http::header_names::content_type] = std::move(type);
+}
+
+utility::string_t http_headers::cache_control() const
+{
+    utility::string_t result;
+    match(http::header_names::cache_control, result);
+    return result;
+}
+
+void http_headers::set_cache_control(utility::string_t control)
+{
+    add(http::header_names::cache_control, std::move(control));
+}
+
+utility::string_t http_headers::date() const
+{
+    utility::string_t result;
+    match(http::header_names::date, result);
+    return result;
+}
+
+void http_headers::set_date(const utility::datetime& date)
+{
+    m_headers[http::header_names::date] = date.to_string(utility::datetime::RFC_1123);
+}
+
+utility::size64_t http_headers::content_length() const
+{
+    utility::size64_t length = 0;
+    match(http::header_names::content_length, length);
+    return length;
+}
+
+void http_headers::set_content_length(utility::size64_t length)
+{
+    m_headers[http::header_names::content_length] = utility::conversions::details::to_string_t(length);
+}
+
+namespace details
+{
+utility::string_t flatten_http_headers(const http_headers& headers)
+{
+    utility::string_t flattened_headers;
+    for (auto iter = headers.begin(); iter != headers.end(); ++iter)
+    {
+        flattened_headers.append(iter->first);
+        flattened_headers.push_back(':');
+        flattened_headers.append(iter->second);
+        flattened_headers.append(CRLF);
+    }
+    return flattened_headers;
+}
+
+void parse_headers_string(_Inout_z_ utility::char_t* headersStr, web::http::http_headers& headers)
+{
+    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(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);
+            web::http::details::trim_whitespace(key);
+            web::http::details::trim_whitespace(value);
+            headers.add(key, value);
+        }
+        startpos = pos + 1;
+        pos = str.find_first_of(_XPLATSTR("\r\n"), pos + 1);
+    }
+}
+
+} // namespace details
+
+http_version __cdecl http_version::from_string(const std::string& http_version_string)
+{
+    std::istringstream str(http_version_string);
+    str.imbue(std::locale::classic());
+
+    std::string http;
+    std::getline(str, http, '/');
+    unsigned int major = 0;
+    str >> major;
+    char dot = '\0';
+    str >> dot;
+    unsigned int minor = 0;
+    str >> minor;
+
+    // check no failure, fully consumed, and correct fixed text
+    if (!str.fail() && str.eof() && "HTTP" == http && '.' == dot)
+    {
+        return {(uint8_t)major, (uint8_t)minor};
+    }
+    return {0, 0};
+}
+
+std::string http_version::to_utf8string() const
+{
+    std::string ret;
+    ret.reserve(8);
+    ret.append("HTTP/");
+    ret.append(std::to_string(static_cast<unsigned int>(major)));
+    ret.append(".");
+    ret.append(std::to_string(static_cast<unsigned int>(minor)));
+    return ret;
+}
+
+static const utility::char_t* stream_was_set_explicitly =
+    _XPLATSTR("A stream was set on the message and extraction is not possible");
+static const utility::char_t* unsupported_charset =
+    _XPLATSTR("Charset must be iso-8859-1, utf-8, utf-16, utf-16le, or utf-16be to be extracted.");
+
+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()
+{
+    // See if the user specified an outstream
+    if (!outstream())
+    {
+        // The user did not specify an outstream.
+        // We will create one...
+        concurrency::streams::producer_consumer_buffer<uint8_t> buf;
+        set_outstream(buf.create_ostream(), true);
+
+        // Since we are creating the streambuffer, set the input stream
+        // so that the user can retrieve the data.
+        set_instream(buf.create_istream());
+    }
+
+    // If the user did specify an outstream we leave the instream
+    // as invalid. It is assumed that user either has a read head
+    // to the out streambuffer or the data is streamed into a container
+    // or media (like file) that the user can read from...
+}
+
+size_t http_msg_base::_get_stream_length()
+{
+    auto& stream = instream();
+
+    if (stream.can_seek())
+    {
+        auto offset = stream.tell();
+        auto end = stream.seek(0, std::ios_base::end);
+        stream.seek(offset);
+        return static_cast<size_t>(end - offset);
+    }
+
+    return (std::numeric_limits<size_t>::max)();
+}
+
+size_t http_msg_base::_get_content_length(bool honor_compression)
+{
+    // An invalid response_stream indicates that there is no body
+    if ((bool)instream())
+    {
+        size_t content_length;
+        utility::string_t transfer_encoding;
+
+        if (headers().match(header_names::transfer_encoding, transfer_encoding))
+        {
+            // Transfer encoding is set; it trumps any content length that may or may not be present
+            if (honor_compression && m_compressor)
+            {
+                http::http_headers tmp;
+
+                // Build a header for comparison with the existing one
+                tmp.add(header_names::transfer_encoding, m_compressor->algorithm());
+                tmp.add(header_names::transfer_encoding, _XPLATSTR("chunked"));
+
+                if (!utility::details::str_iequal(transfer_encoding, tmp[header_names::transfer_encoding]))
+                {
+                    // Some external entity added this header, and it doesn't match our
+                    // expectations; bail out, since the caller's intentions are not clear
+                    throw http_exception("Transfer-Encoding header is internally managed when compressing");
+                }
+            }
+
+            return (std::numeric_limits<size_t>::max)();
+        }
+
+        if (honor_compression && m_compressor)
+        {
+            // A compressor is set; this implies transfer encoding, since we don't know the compressed length
+            // 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<size_t>::max)();
+        }
+
+        if (headers().match(header_names::content_length, content_length))
+        {
+            // An explicit content length is set; trust it, since we
+            // may not be required to send the stream's entire contents
+            return content_length;
+        }
+
+        content_length = _get_stream_length();
+        if (content_length != (std::numeric_limits<size_t>::max)())
+        {
+            // The content length wasn't explicitly set, but we figured it out;
+            // use it, since sending this way is more efficient than chunking
+            headers().add(header_names::content_length, content_length);
+            return content_length;
+        }
+
+        // We don't know the content length; we'll chunk the stream
+        headers().add(header_names::transfer_encoding, _XPLATSTR("chunked"));
+        return (std::numeric_limits<size_t>::max)();
+    }
+
+    // There is no content
+    return 0;
+}
+
+size_t http_msg_base::_get_content_length_and_set_compression() { return _get_content_length(true); }
+
+size_t http_msg_base::_get_content_length() { return _get_content_length(false); }
+
+// Helper function to inline continuation if possible.
+struct inline_continuation
+{
+    inline_continuation(pplx::task<void>& prev, const std::function<void(pplx::task<void>)>& next)
+        : m_prev(prev), m_next(next)
+    {
+    }
+    ~inline_continuation()
+    {
+        if (m_prev.is_done())
+        {
+            m_next(m_prev);
+        }
+        else
+        {
+            m_prev.then(m_next);
+        }
+    }
+    pplx::task<void>& m_prev;
+    std::function<void(pplx::task<void>)> m_next;
+
+private:
+    inline_continuation(const inline_continuation&);
+    inline_continuation& operator=(const inline_continuation&);
+};
+
+void http_msg_base::_complete(utility::size64_t body_size, const std::exception_ptr& exceptionPtr)
+{
+    const auto& completionEvent = _get_data_available();
+    auto closeTask = pplx::task_from_result();
+    if (m_default_outstream)
+    {
+        // if the outstream is one we created by default on the customer's behalf, try to close it
+        auto& out = outstream();
+        if (out.is_valid())
+        {
+            if (exceptionPtr == std::exception_ptr())
+            {
+                closeTask = out.close();
+            }
+            else
+            {
+                closeTask = out.close(exceptionPtr);
+            }
+        }
+    }
+
+    if (exceptionPtr == std::exception_ptr())
+    {
+        inline_continuation(closeTask, [completionEvent, body_size](pplx::task<void> t) {
+            try
+            {
+                t.get();
+                completionEvent.set(body_size);
+            }
+            catch (...)
+            {
+                // If close throws an exception report back to user.
+                completionEvent.set_exception(std::current_exception());
+                pplx::create_task(completionEvent).then([](pplx::task<utility::size64_t> t) {
+                    try
+                    {
+                        t.get();
+                    }
+                    catch (...)
+                    {
+                    }
+                });
+            }
+        });
+    }
+    else
+    {
+        inline_continuation(closeTask, [completionEvent, exceptionPtr](pplx::task<void> t) {
+            // If closing stream throws an exception ignore since we already have an error.
+            try
+            {
+                t.get();
+            }
+            catch (...)
+            {
+            }
+            completionEvent.set_exception(exceptionPtr);
+            pplx::create_task(completionEvent).then([](pplx::task<utility::size64_t> t) {
+                try
+                {
+                    t.get();
+                }
+                catch (...)
+                {
+                }
+            });
+        });
+    }
+}
+
+static bool is_content_type_one_of(const utility::string_t* first,
+                                   const utility::string_t* last,
+                                   const utility::string_t& value)
+{
+    while (first != last)
+    {
+        if (utility::details::str_iequal(*first, value))
+        {
+            return true;
+        }
+        ++first;
+    }
+    return false;
+}
+
+// Remove once VS 2013 is no longer supported.
+#if defined(_WIN32) && _MSC_VER < 1900
+// Not referring to mime_types to avoid static initialization order fiasco.
+static const utility::string_t textual_types[] = {U("message/http"),
+                                                  U("application/json"),
+                                                  U("application/xml"),
+                                                  U("application/atom+xml"),
+                                                  U("application/http"),
+                                                  U("application/x-www-form-urlencoded")};
+#endif
+
+/// <summary>
+/// Determines whether or not the given content type is 'textual' according the feature specifications.
+/// </summary>
+static bool is_content_type_textual(const utility::string_t& content_type)
+{
+#if !defined(_WIN32) || _MSC_VER >= 1900
+    static const utility::string_t textual_types[] = {mime_types::message_http,
+                                                      mime_types::application_json,
+                                                      mime_types::application_xml,
+                                                      mime_types::application_atom_xml,
+                                                      mime_types::application_http,
+                                                      mime_types::application_x_www_form_urlencoded};
+#endif
+
+    if (content_type.size() >= 4 && utility::details::str_iequal(content_type.substr(0, 4), _XPLATSTR("text")))
+    {
+        return true;
+    }
+    return (is_content_type_one_of(std::begin(textual_types), std::end(textual_types), content_type));
+}
+
+// Remove once VS 2013 is no longer supported.
+#if defined(_WIN32) && _MSC_VER < 1900
+// Not referring to mime_types to avoid static initialization order fiasco.
+static const utility::string_t json_types[] = {U("application/json"),
+                                               U("application/x-json"),
+                                               U("text/json"),
+                                               U("text/x-json"),
+                                               U("text/javascript"),
+                                               U("text/x-javascript"),
+                                               U("application/javascript"),
+                                               U("application/x-javascript")};
+#endif
+
+/// <summary>
+/// Determines whether or not the given content type is JSON according the feature specifications.
+/// </summary>
+static bool is_content_type_json(const utility::string_t& content_type)
+{
+#if !defined(_WIN32) || _MSC_VER >= 1900
+    static const utility::string_t json_types[] = {mime_types::application_json,
+                                                   mime_types::application_xjson,
+                                                   mime_types::text_json,
+                                                   mime_types::text_xjson,
+                                                   mime_types::text_javascript,
+                                                   mime_types::text_xjavascript,
+                                                   mime_types::application_javascript,
+                                                   mime_types::application_xjavascript};
+#endif
+
+    return (is_content_type_one_of(std::begin(json_types), std::end(json_types), content_type));
+}
+
+/// <summary>
+/// Gets the default charset for given content type. If the MIME type is not textual or recognized Latin1 will be
+/// returned.
+/// </summary>
+static utility::string_t get_default_charset(const utility::string_t& content_type)
+{
+    // We are defaulting everything to Latin1 except JSON which is utf-8.
+    if (is_content_type_json(content_type))
+    {
+        return charset_types::utf8;
+    }
+    else
+    {
+        return charset_types::latin1;
+    }
+}
+
+/// <summary>
+/// Parses the given Content-Type header value to get out actual content type and charset.
+/// If the charset isn't specified the default charset for the content type will be set.
+/// </summary>
+static void parse_content_type_and_charset(const utility::string_t& content_type,
+                                           utility::string_t& content,
+                                           utility::string_t& charset)
+{
+    const size_t semi_colon_index = content_type.find_first_of(_XPLATSTR(";"));
+
+    // No charset specified.
+    if (semi_colon_index == utility::string_t::npos)
+    {
+        content = content_type;
+        trim_whitespace(content);
+        charset = get_default_charset(content);
+        return;
+    }
+
+    // Split into content type and second part which could be charset.
+    content = content_type.substr(0, semi_colon_index);
+    trim_whitespace(content);
+    utility::string_t possible_charset = content_type.substr(semi_colon_index + 1);
+    trim_whitespace(possible_charset);
+    const size_t equals_index = possible_charset.find_first_of(_XPLATSTR("="));
+
+    // No charset specified.
+    if (equals_index == utility::string_t::npos)
+    {
+        charset = get_default_charset(content);
+        return;
+    }
+
+    // Split and make sure 'charset'
+    utility::string_t charset_key = possible_charset.substr(0, equals_index);
+    trim_whitespace(charset_key);
+    if (!utility::details::str_iequal(charset_key, _XPLATSTR("charset")))
+    {
+        charset = get_default_charset(content);
+        return;
+    }
+    charset = possible_charset.substr(equals_index + 1);
+    // Remove the redundant ';' at the end of charset.
+    while (charset.back() == ';')
+    {
+        charset.pop_back();
+    }
+    trim_whitespace(charset);
+    if (charset.front() == _XPLATSTR('"') && charset.back() == _XPLATSTR('"'))
+    {
+        charset = charset.substr(1, charset.size() - 2);
+        trim_whitespace(charset);
+    }
+}
+
+utility::string_t details::http_msg_base::parse_and_check_content_type(
+    bool ignore_content_type, const std::function<bool(const utility::string_t&)>& check_content_type)
+{
+    if (!instream())
+    {
+        throw http_exception(stream_was_set_explicitly);
+    }
+
+    utility::string_t content, charset = charset_types::utf8;
+    if (!ignore_content_type)
+    {
+        parse_content_type_and_charset(headers().content_type(), content, charset);
+
+        // If no Content-Type or empty body then just return an empty string.
+        if (content.empty() || instream().streambuf().in_avail() == 0)
+        {
+            return utility::string_t();
+        }
+
+        if (!check_content_type(content))
+        {
+            throw http_exception(
+                _XPLATSTR("Incorrect Content-Type: must be textual to extract_string, JSON to extract_json."));
+        }
+    }
+    return charset;
+}
+
+utf8string details::http_msg_base::extract_utf8string(bool ignore_content_type)
+{
+    const auto& charset = parse_and_check_content_type(ignore_content_type, is_content_type_textual);
+    if (charset.empty())
+    {
+        return utf8string();
+    }
+    auto buf_r = instream().streambuf();
+
+    // Perform the correct character set conversion if one is necessary.
+    if (utility::details::str_iequal(charset, charset_types::utf8) ||
+        utility::details::str_iequal(charset, charset_types::usascii) ||
+        utility::details::str_iequal(charset, charset_types::ascii))
+    {
+        std::string body;
+        body.resize((std::string::size_type)buf_r.in_avail());
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
+            .get(); // There is no risk of blocking.
+        return body;
+    }
+
+    // Latin1
+    else if (utility::details::str_iequal(charset, charset_types::latin1))
+    {
+        std::string body;
+        body.resize((std::string::size_type)buf_r.in_avail());
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
+            .get(); // There is no risk of blocking.
+        return latin1_to_utf8(std::move(body));
+    }
+
+    // utf-16
+    else if (utility::details::str_iequal(charset, charset_types::utf16))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return convert_utf16_to_utf8(std::move(body));
+    }
+
+    // utf-16le
+    else if (utility::details::str_iequal(charset, charset_types::utf16le))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return utility::conversions::utf16_to_utf8(std::move(body));
+    }
+
+    // utf-16be
+    else if (utility::details::str_iequal(charset, charset_types::utf16be))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return convert_utf16be_to_utf8(std::move(body), false);
+    }
+
+    else
+    {
+        throw http_exception(unsupported_charset);
+    }
+}
+
+utf16string details::http_msg_base::extract_utf16string(bool ignore_content_type)
+{
+    const auto& charset = parse_and_check_content_type(ignore_content_type, is_content_type_textual);
+    if (charset.empty())
+    {
+        return utf16string();
+    }
+    auto buf_r = instream().streambuf();
+
+    // Perform the correct character set conversion if one is necessary.
+    if (utility::details::str_iequal(charset, charset_types::utf16le))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return body;
+    }
+
+    // utf-8, ascii
+    else if (utility::details::str_iequal(charset, charset_types::utf8) ||
+             utility::details::str_iequal(charset, charset_types::usascii) ||
+             utility::details::str_iequal(charset, charset_types::ascii))
+    {
+        std::string body;
+        body.resize((std::string::size_type)buf_r.in_avail());
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
+            .get(); // There is no risk of blocking.
+        return utility::conversions::utf8_to_utf16(std::move(body));
+    }
+
+    // Latin1
+    else if (utility::details::str_iequal(charset, charset_types::latin1))
+    {
+        std::string body;
+        body.resize((std::string::size_type)buf_r.in_avail());
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
+            .get(); // There is no risk of blocking.
+        return latin1_to_utf16(std::move(body));
+    }
+
+    // utf-16
+    else if (utility::details::str_iequal(charset, charset_types::utf16))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return convert_utf16_to_utf16(std::move(body));
+    }
+
+    // utf-16be
+    else if (utility::details::str_iequal(charset, charset_types::utf16be))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return convert_utf16be_to_utf16le(std::move(body), false);
+    }
+
+    else
+    {
+        throw http_exception(unsupported_charset);
+    }
+}
+
+utility::string_t details::http_msg_base::extract_string(bool ignore_content_type)
+{
+    const auto& charset = parse_and_check_content_type(ignore_content_type, is_content_type_textual);
+    if (charset.empty())
+    {
+        return utility::string_t();
+    }
+    auto buf_r = instream().streambuf();
+
+    // Perform the correct character set conversion if one is necessary.
+    if (utility::details::str_iequal(charset, charset_types::usascii) ||
+        utility::details::str_iequal(charset, charset_types::ascii))
+    {
+        std::string body;
+        body.resize((std::string::size_type)buf_r.in_avail());
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
+            .get(); // There is no risk of blocking.
+        return to_string_t(std::move(body));
+    }
+
+    // Latin1
+    if (utility::details::str_iequal(charset, charset_types::latin1))
+    {
+        std::string body;
+        body.resize((std::string::size_type)buf_r.in_avail());
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
+            .get(); // There is no risk of blocking.
+        // Could optimize for linux in the future if a latin1_to_utf8 function was written.
+        return to_string_t(latin1_to_utf16(std::move(body)));
+    }
+
+    // utf-8.
+    else if (utility::details::str_iequal(charset, charset_types::utf8))
+    {
+        std::string body;
+        body.resize((std::string::size_type)buf_r.in_avail());
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
+            .get(); // There is no risk of blocking.
+        return to_string_t(std::move(body));
+    }
+
+    // utf-16.
+    else if (utility::details::str_iequal(charset, charset_types::utf16))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return convert_utf16_to_string_t(std::move(body));
+    }
+
+    // utf-16le
+    else if (utility::details::str_iequal(charset, charset_types::utf16le))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return convert_utf16le_to_string_t(std::move(body), false);
+    }
+
+    // utf-16be
+    else if (utility::details::str_iequal(charset, charset_types::utf16be))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return convert_utf16be_to_string_t(std::move(body), false);
+    }
+
+    else
+    {
+        throw http_exception(unsupported_charset);
+    }
+}
+
+json::value details::http_msg_base::_extract_json(bool ignore_content_type)
+{
+    const auto& charset = parse_and_check_content_type(ignore_content_type, is_content_type_json);
+    if (charset.empty())
+    {
+        return json::value();
+    }
+    auto buf_r = instream().streambuf();
+
+    // Latin1
+    if (utility::details::str_iequal(charset, charset_types::latin1))
+    {
+        std::string body;
+        body.resize(buf_r.in_avail());
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
+            .get(); // There is no risk of blocking.
+        // On Linux could optimize in the future if a latin1_to_utf8 function is written.
+        return json::value::parse(to_string_t(latin1_to_utf16(std::move(body))));
+    }
+
+    // utf-8, usascii and ascii
+    else if (utility::details::str_iequal(charset, charset_types::utf8) ||
+             utility::details::str_iequal(charset, charset_types::usascii) ||
+             utility::details::str_iequal(charset, charset_types::ascii))
+    {
+        std::string body;
+        body.resize(buf_r.in_avail());
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
+            .get(); // There is no risk of blocking.
+        return json::value::parse(to_string_t(std::move(body)));
+    }
+
+    // utf-16.
+    else if (utility::details::str_iequal(charset, charset_types::utf16))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return json::value::parse(convert_utf16_to_string_t(std::move(body)));
+    }
+
+    // utf-16le
+    else if (utility::details::str_iequal(charset, charset_types::utf16le))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return json::value::parse(convert_utf16le_to_string_t(std::move(body), false));
+    }
+
+    // utf-16be
+    else if (utility::details::str_iequal(charset, charset_types::utf16be))
+    {
+        utf16string body;
+        body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
+        buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
+                   body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
+        return json::value::parse(convert_utf16be_to_string_t(std::move(body), false));
+    }
+
+    else
+    {
+        throw http_exception(unsupported_charset);
+    }
+}
+
+std::vector<uint8_t> details::http_msg_base::_extract_vector()
+{
+    if (!instream())
+    {
+        throw http_exception(stream_was_set_explicitly);
+    }
+
+    std::vector<uint8_t> body;
+    auto buf_r = instream().streambuf();
+    const size_t size = buf_r.in_avail();
+    body.resize(size);
+    buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), size)
+        .get(); // There is no risk of blocking.
+
+    return body;
+}
+
+// Helper function to convert message body without extracting.
+static utility::string_t convert_body_to_string_t(const utility::string_t& content_type,
+                                                  concurrency::streams::istream instream)
+{
+    if (!instream)
+    {
+        // The instream is not yet set
+        return utility::string_t();
+    }
+
+    concurrency::streams::streambuf<uint8_t> streambuf = instream.streambuf();
+
+    _ASSERTE((bool)streambuf);
+    _ASSERTE(streambuf.is_open());
+    _ASSERTE(streambuf.can_read());
+
+    utility::string_t content, charset;
+    parse_content_type_and_charset(content_type, content, charset);
+
+    // Content-Type must have textual type.
+    if (!is_content_type_textual(content) || streambuf.in_avail() == 0)
+    {
+        return utility::string_t();
+    }
+
+    // Latin1
+    if (utility::details::str_iequal(charset, charset_types::latin1))
+    {
+        std::string body;
+        body.resize(streambuf.in_avail());
+        if (streambuf.scopy((unsigned char*)&body[0], body.size()) == 0) return string_t();
+        return to_string_t(latin1_to_utf16(std::move(body)));
+    }
+
+    // utf-8.
+    else if (utility::details::str_iequal(charset, charset_types::utf8))
+    {
+        std::string body;
+        body.resize(streambuf.in_avail());
+        if (streambuf.scopy((unsigned char*)&body[0], body.size()) == 0) return string_t();
+        return to_string_t(std::move(body));
+    }
+
+    // utf-16.
+    else if (utility::details::str_iequal(charset, charset_types::utf16))
+    {
+        utf16string body;
+        body.resize(streambuf.in_avail() / sizeof(utf16string::value_type));
+        if (streambuf.scopy((unsigned char*)&body[0], body.size() * sizeof(utf16string::value_type)) == 0)
+            return string_t();
+        return convert_utf16_to_string_t(std::move(body));
+    }
+
+    // utf-16le
+    else if (utility::details::str_iequal(charset, charset_types::utf16le))
+    {
+        utf16string body;
+        body.resize(streambuf.in_avail() / sizeof(utf16string::value_type));
+        if (streambuf.scopy((unsigned char*)&body[0], body.size() * sizeof(utf16string::value_type)) == 0)
+            return string_t();
+        return convert_utf16le_to_string_t(std::move(body), false);
+    }
+
+    // utf-16be
+    else if (utility::details::str_iequal(charset, charset_types::utf16be))
+    {
+        utf16string body;
+        body.resize(streambuf.in_avail() / sizeof(utf16string::value_type));
+        if (streambuf.scopy((unsigned char*)&body[0], body.size() * sizeof(utf16string::value_type)) == 0)
+            return string_t();
+        return convert_utf16be_to_string_t(std::move(body), false);
+    }
+
+    else
+    {
+        return utility::string_t();
+    }
+}
+
+//
+// Helper function to generate a wstring from given http_headers and message body.
+//
+static utility::string_t http_headers_body_to_string(const http_headers& headers,
+                                                     concurrency::streams::istream instream)
+{
+    utility::string_t result;
+    for (const auto& header : headers)
+    {
+        result += header.first;
+        result += _XPLATSTR(": ");
+        result += header.second;
+        result += CRLF;
+    }
+
+    result += CRLF;
+
+    utility::string_t content_type;
+    if (headers.match(http::header_names::content_type, content_type))
+    {
+        result += convert_body_to_string_t(content_type, instream);
+    }
+
+    return result;
+}
+
+utility::string_t details::http_msg_base::to_string() const
+{
+    return http_headers_body_to_string(m_headers, instream());
+}
+
+static void set_content_type_if_not_present(http::http_headers& headers, const utility::string_t& content_type)
+{
+    utility::string_t temp;
+    if (!headers.match(http::header_names::content_type, temp))
+    {
+        headers.add(http::header_names::content_type, content_type);
+    }
+}
+
+void details::http_msg_base::set_body(const streams::istream& instream, const utf8string& contentType)
+{
+    set_content_type_if_not_present(headers(),
+#ifdef _UTF16_STRINGS
+                                    utility::conversions::utf8_to_utf16(contentType));
+#else
+                                    contentType);
+#endif
+    set_instream(instream);
+}
+
+void details::http_msg_base::set_body(const streams::istream& instream, const utf16string& contentType)
+{
+    set_content_type_if_not_present(headers(),
+#ifdef _UTF16_STRINGS
+                                    contentType);
+#else
+                                    utility::conversions::utf16_to_utf8(contentType));
+#endif
+    set_instream(instream);
+}
+
+void details::http_msg_base::set_body(const streams::istream& instream,
+                                      utility::size64_t contentLength,
+                                      const utf8string& contentType)
+{
+    headers().set_content_length(contentLength);
+    set_body(instream, contentType);
+    m_data_available.set(contentLength);
+}
+
+void details::http_msg_base::set_body(const concurrency::streams::istream& instream,
+                                      utility::size64_t contentLength,
+                                      const utf16string& contentType)
+{
+    headers().set_content_length(contentLength);
+    set_body(instream, contentType);
+    m_data_available.set(contentLength);
+}
+
+details::_http_request::_http_request(http::method mtd)
+    : m_method(std::move(mtd))
+    , m_initiated_response(0)
+    , m_server_context()
+    , m_cancellationToken(pplx::cancellation_token::none())
+{
+    if (m_method.empty())
+    {
+        throw std::invalid_argument("Invalid HTTP method specified. Method can't be an empty string.");
+    }
+}
+
+details::_http_request::_http_request(std::unique_ptr<http::details::_http_server_context> server_context)
+    : m_initiated_response(0)
+    , m_server_context(std::move(server_context))
+    , m_cancellationToken(pplx::cancellation_token::none())
+{
+}
+
+void http_request::set_decompress_factories()
+{
+    return _m_impl->set_decompress_factories(compression::details::builtin::get_decompress_factories());
+}
+
+const http_version http_versions::HTTP_0_9 = {0, 9};
+const http_version http_versions::HTTP_1_0 = {1, 0};
+const http_version http_versions::HTTP_1_1 = {1, 1};
+
+#define _METHODS
+#define DAT(a, b) const method methods::a = b;
+#include "cpprest/details/http_constants.dat"
+#undef _METHODS
+#undef DAT
+
+#define _HEADER_NAMES
+#define DAT(a, b) const utility::string_t header_names::a = _XPLATSTR(b);
+#include "cpprest/details/http_constants.dat"
+#undef _HEADER_NAMES
+#undef DAT
+
+#define _MIME_TYPES
+#define DAT(a, b) const utility::string_t mime_types::a = _XPLATSTR(b);
+#include "cpprest/details/http_constants.dat"
+#undef _MIME_TYPES
+#undef DAT
+
+#define _CHARSET_TYPES
+#define DAT(a, b) const utility::string_t charset_types::a = _XPLATSTR(b);
+#include "cpprest/details/http_constants.dat"
+#undef _CHARSET_TYPES
+#undef DAT
+
+// This is necessary for Linux because of a bug in GCC 4.7
+#ifndef _WIN32
+#define _PHRASES
+#define DAT(a, b, c) const status_code status_codes::a;
+#include "cpprest/details/http_constants.dat"
+#undef _PHRASES
+#undef DAT
+#endif
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/common/internal_http_helpers.h b/Release/src/http/common/internal_http_helpers.h
new file mode 100644 (file)
index 0000000..1fbdfdf
--- /dev/null
@@ -0,0 +1,119 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ ****/
+
+#pragma once
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/details/basic_types.h"
+#include <string>
+
+namespace web
+{
+namespace http
+{
+namespace details
+{
+/// <summary>
+/// Helper function to get the default HTTP reason phrase for a status code.
+/// </summary>
+utility::string_t get_default_reason_phrase(status_code code);
+
+template<class Char, class Fn>
+void trim_if(std::basic_string<Char>& 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<class Char>
+void trim_nulls(std::basic_string<Char>& str)
+{
+    trim_if(str, [](const Char c) { return c == Char {}; });
+}
+
+template<class Char>
+void trim_whitespace(std::basic_string<Char>& str)
+{
+    trim_if(str, [](const Char c) { return ::utility::details::is_space(c); });
+}
+
+bool validate_method(const utility::string_t& method);
+
+} // namespace details
+} // namespace http
+} // namespace web
+
+namespace web
+{
+namespace http
+{
+namespace compression
+{
+class decompress_factory;
+
+namespace details
+{
+namespace builtin
+{
+/// <summary>
+/// Helper function to get the set of built-in decompress factories
+/// </summary>
+
+const std::vector<std::shared_ptr<web::http::compression::decompress_factory>> get_decompress_factories();
+
+} // namespace builtin
+} // namespace details
+
+} // namespace compression
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/common/x509_cert_utilities.h b/Release/src/http/common/x509_cert_utilities.h
new file mode 100644 (file)
index 0000000..854e305
--- /dev/null
@@ -0,0 +1,110 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Contains utility functions for helping to verify server certificates in OS X/iOS and Android.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if defined(_WIN32)
+#include <Wincrypt.h>
+
+namespace web
+{
+namespace http
+{
+namespace client
+{
+namespace details
+{
+struct winhttp_cert_context
+{
+    PCCERT_CONTEXT raw;
+    winhttp_cert_context() CPPREST_NOEXCEPT : raw(nullptr) {}
+    winhttp_cert_context(const winhttp_cert_context&) = delete;
+    winhttp_cert_context& operator=(const winhttp_cert_context&) = delete;
+    ~winhttp_cert_context()
+    {
+        // https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-certfreecertificatecontext
+        // "The function always returns nonzero."
+        if (raw)
+        {
+            (void)CertFreeCertificateContext(raw);
+        }
+    }
+};
+
+struct winhttp_cert_chain_context
+{
+    PCCERT_CHAIN_CONTEXT raw;
+    winhttp_cert_chain_context() CPPREST_NOEXCEPT : raw(nullptr) {}
+    winhttp_cert_chain_context(const winhttp_cert_chain_context&) = delete;
+    winhttp_cert_chain_context& operator=(const winhttp_cert_chain_context&) = delete;
+    ~winhttp_cert_chain_context()
+    {
+        if (raw)
+        {
+            CertFreeCertificateChain(raw);
+        }
+    }
+};
+} // namespace details
+} // namespace client
+} // namespace http
+} // namespace web
+#endif // _WIN32
+
+#if defined(__APPLE__) || (defined(ANDROID) || defined(__ANDROID__)) ||                                                \
+    (defined(_WIN32) && defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)) ||                                                    \
+    (defined(_WIN32) && !defined(__cplusplus_winrt) && !defined(_M_ARM) && !defined(CPPREST_EXCLUDE_WEBSOCKETS))
+#define CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+#endif
+
+#ifdef CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+#include <string>
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4005)
+#endif
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+#endif
+#include <boost/asio/ssl.hpp>
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+namespace web
+{
+namespace http
+{
+namespace client
+{
+namespace details
+{
+/// <summary>
+/// Using platform specific APIs verifies server certificate.
+/// Currently implemented to work on Windows, iOS, Android, and OS X.
+/// </summary>
+/// <param name="verifyCtx">Boost.ASIO context to get certificate chain from.</param>
+/// <param name="hostName">Host name from the URI.</param>
+/// <returns>True if verification passed and server can be trusted, false otherwise.</returns>
+bool verify_cert_chain_platform_specific(boost::asio::ssl::verify_context& verifyCtx, const std::string& hostName);
+} // namespace details
+} // namespace client
+} // namespace http
+} // namespace web
+
+#endif // CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
diff --git a/Release/src/http/listener/http_listener.cpp b/Release/src/http/listener/http_listener.cpp
new file mode 100644 (file)
index 0000000..6a2edf6
--- /dev/null
@@ -0,0 +1,172 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: HTTP listener (server-side) APIs
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if !defined(_WIN32) || (_WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)) ||                         \
+    defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+
+using namespace web::http::experimental;
+
+namespace web
+{
+namespace http
+{
+namespace experimental
+{
+namespace listener
+{
+// Helper function to check URI components.
+static void check_listener_uri(const http::uri& address)
+{
+    // Some things like proper URI schema are verified by the URI class.
+    // We only need to check certain things specific to HTTP.
+
+    // HTTP Server API includes SSL support
+    if (address.scheme() != U("http") && address.scheme() != U("https"))
+    {
+        throw std::invalid_argument("URI scheme must be 'http' or 'https'");
+    }
+
+    if (address.host().empty())
+    {
+        throw std::invalid_argument("URI must contain a hostname.");
+    }
+
+    if (!address.query().empty())
+    {
+        throw std::invalid_argument("URI can't contain a query.");
+    }
+
+    if (!address.fragment().empty())
+    {
+        throw std::invalid_argument("URI can't contain a fragment.");
+    }
+}
+
+details::http_listener_impl::http_listener_impl(http::uri address) : m_uri(std::move(address)), m_closed(true)
+{
+    check_listener_uri(m_uri);
+}
+
+details::http_listener_impl::http_listener_impl(http::uri address, http_listener_config config)
+    : m_uri(std::move(address)), m_config(std::move(config)), m_closed(true)
+{
+    check_listener_uri(m_uri);
+}
+
+pplx::task<void> details::http_listener_impl::open()
+{
+    // Do nothing if the open operation was already attempted
+    // Not thread safe
+    if (!m_closed) return pplx::task_from_result();
+
+    if (m_uri.is_empty()) throw std::invalid_argument("No URI defined for listener.");
+    m_closed = false;
+
+    return web::http::experimental::details::http_server_api::register_listener(this).then(
+        [this](pplx::task<void> openOp) {
+            try
+            {
+                // If failed to open need to mark as closed.
+                openOp.wait();
+            }
+            catch (...)
+            {
+                m_closed = true;
+                throw;
+            }
+            return openOp;
+        });
+}
+
+pplx::task<void> details::http_listener_impl::close()
+{
+    // Do nothing if the close operation was already attempted
+    // Not thread safe.
+    // Note: Return the previous close task
+    if (m_closed) return m_close_task;
+
+    m_closed = true;
+    m_close_task = web::http::experimental::details::http_server_api::unregister_listener(this);
+    return m_close_task;
+}
+
+void details::http_listener_impl::handle_request(http_request msg)
+{
+    // Specific method handler takes priority over general.
+    const method& mtd = msg.method();
+    if (m_supported_methods.count(mtd))
+    {
+        m_supported_methods[mtd](msg);
+    }
+    else if (mtd == methods::OPTIONS)
+    {
+        handle_options(msg);
+    }
+    else if (mtd == methods::TRCE)
+    {
+        handle_trace(msg);
+    }
+    else if (m_all_requests != nullptr)
+    {
+        m_all_requests(msg);
+    }
+    else
+    {
+        // Method is not supported.
+        // Send back a list of supported methods to the client.
+        http_response response(status_codes::MethodNotAllowed);
+        response.headers().add(U("Allow"), get_supported_methods());
+        msg.reply(response);
+    }
+}
+
+utility::string_t details::http_listener_impl::get_supported_methods() const
+{
+    utility::string_t allowed;
+    bool first = true;
+    for (auto iter = m_supported_methods.begin(); iter != m_supported_methods.end(); ++iter)
+    {
+        if (!first)
+        {
+            allowed += U(", ");
+        }
+        else
+        {
+            first = false;
+        }
+        allowed += (iter->first);
+    }
+    return allowed;
+}
+
+void details::http_listener_impl::handle_trace(http_request message)
+{
+    utility::string_t data = message.to_string();
+    message.reply(status_codes::OK, data, U("message/http"));
+}
+
+void details::http_listener_impl::handle_options(http_request message)
+{
+    http_response response(status_codes::OK);
+    response.headers().add(U("Allow"), get_supported_methods());
+    message.reply(response);
+}
+
+} // namespace listener
+} // namespace experimental
+} // namespace http
+} // namespace web
+
+#endif
diff --git a/Release/src/http/listener/http_listener_msg.cpp b/Release/src/http/listener/http_listener_msg.cpp
new file mode 100644 (file)
index 0000000..3cc4cc4
--- /dev/null
@@ -0,0 +1,89 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Request and reply message definitions (server side).
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "../common/internal_http_helpers.h"
+using namespace web;
+using namespace utility;
+
+namespace web
+{
+namespace http
+{
+// Actual initiates sending the response.
+pplx::task<void> details::_http_request::_reply_impl(http_response response)
+{
+    // If the user didn't explicitly set a reason phrase then we should have it default
+    // if they used one of the standard known status codes.
+    if (response.reason_phrase().empty())
+    {
+        response.set_reason_phrase(get_default_reason_phrase(response.status_code()));
+    }
+
+    pplx::task<void> response_completed;
+
+#if !defined(__cplusplus_winrt) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+    auto server_api = experimental::details::http_server_api::server_api();
+
+    if (m_server_context && server_api)
+    {
+        // Add a task-based continuation so no exceptions thrown from the task go 'unobserved'.
+        response._set_server_context(std::move(m_server_context));
+        response_completed = server_api->respond(response);
+        response_completed.then([](pplx::task<void> t) {
+            try
+            {
+                t.wait();
+            }
+            catch (...)
+            {
+            }
+        });
+    }
+    else
+#endif
+    {
+        // There's no server context. The message may be replied to locally, as in a HTTP client
+        // pipeline stage. There's no sending required, so we can simply consider the reply
+        // done and return an already filled-in task.
+        response_completed = pplx::task_from_result();
+    }
+
+    m_response.set(response);
+    return response_completed;
+}
+
+pplx::task<void> details::_http_request::_reply_if_not_already(status_code status)
+{
+    const long expected = 0;
+    const long desired = 2;
+    if (pplx::details::atomic_compare_exchange(m_initiated_response, desired, expected) == expected)
+    {
+        return _reply_impl(http_response(status));
+    }
+    return pplx::task_from_result();
+}
+
+pplx::task<void> details::_http_request::reply(const http_response& response)
+{
+    const long expected = 0;
+    const long desired = 1;
+    switch (pplx::details::atomic_compare_exchange(m_initiated_response, desired, expected))
+    {
+        case 0: return _reply_impl(response); // success
+        case 1: throw http_exception(U("Error: trying to send multiple responses to an HTTP request"));
+        case 2: return pplx::task_from_result(); // already handled
+        default: abort();
+    }
+}
+
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/listener/http_server_api.cpp b/Release/src/http/listener/http_server_api.cpp
new file mode 100644 (file)
index 0000000..ea985f4
--- /dev/null
@@ -0,0 +1,173 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: exposes the entry points to the http server transport apis.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if !defined(_WIN32) || (_WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)) ||                         \
+    defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+#include "http_server_impl.h"
+
+using namespace web;
+using namespace utility;
+using namespace web::http::experimental::listener;
+
+namespace web
+{
+namespace http
+{
+namespace experimental
+{
+namespace details
+{
+pplx::extensibility::critical_section_t http_server_api::s_lock;
+
+std::unique_ptr<http_server> http_server_api::s_server_api((http_server*)nullptr);
+
+pplx::details::atomic_long http_server_api::s_registrations(0L);
+
+bool http_server_api::has_listener() { return s_registrations > 0L; }
+
+void http_server_api::register_server_api(std::unique_ptr<http_server> server_api)
+{
+    pplx::extensibility::scoped_critical_section_t lock(s_lock);
+    http_server_api::unsafe_register_server_api(std::move(server_api));
+}
+
+void http_server_api::unregister_server_api()
+{
+    pplx::extensibility::scoped_critical_section_t lock(s_lock);
+
+    if (http_server_api::has_listener())
+    {
+        throw http_exception(_XPLATSTR("Server API was cleared while listeners were still attached"));
+    }
+
+    s_server_api.reset();
+}
+
+void http_server_api::unsafe_register_server_api(std::unique_ptr<http_server> server_api)
+{
+    // we assume that the lock has been taken here.
+    if (http_server_api::has_listener())
+    {
+        throw http_exception(_XPLATSTR("Current server API instance has listeners attached."));
+    }
+
+    s_server_api.swap(server_api);
+}
+
+pplx::task<void> http_server_api::register_listener(
+    _In_ web::http::experimental::listener::details::http_listener_impl* listener)
+{
+    return pplx::create_task([listener]() {
+        pplx::extensibility::scoped_critical_section_t lock(s_lock);
+
+        // the server API was not initialized, register a default
+        if (s_server_api == nullptr)
+        {
+#if defined(_WIN32) && !defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+            auto server_api = make_http_httpsys_server();
+#else
+            auto server_api = make_http_asio_server();
+#endif
+            http_server_api::unsafe_register_server_api(std::move(server_api));
+
+            _ASSERTE(s_server_api != nullptr);
+        }
+
+        std::exception_ptr except;
+        try
+        {
+            // start the server if necessary
+            if (pplx::details::atomic_increment(s_registrations) == 1L)
+            {
+                s_server_api->start().wait();
+            }
+
+            // register listener
+            s_server_api->register_listener(listener).wait();
+        }
+        catch (...)
+        {
+            except = std::current_exception();
+        }
+
+        // Registration failed, need to decrement registration count.
+        if (except != nullptr)
+        {
+            if (pplx::details::atomic_decrement(s_registrations) == 0L)
+            {
+                try
+                {
+                    server_api()->stop().wait();
+                    http_server_api::unsafe_register_server_api(nullptr);
+                }
+                catch (...)
+                {
+                    // ignore this exception since we want to report the original one
+                }
+            }
+            std::rethrow_exception(except);
+        }
+    });
+}
+
+pplx::task<void> http_server_api::unregister_listener(
+    _In_ web::http::experimental::listener::details::http_listener_impl* pListener)
+{
+    return pplx::create_task([pListener]() {
+        pplx::extensibility::scoped_critical_section_t lock(s_lock);
+
+        // unregister listener
+        std::exception_ptr except;
+        try
+        {
+            server_api()->unregister_listener(pListener).wait();
+        }
+        catch (...)
+        {
+            except = std::current_exception();
+        }
+
+        // stop server if necessary
+        try
+        {
+            if (pplx::details::atomic_decrement(s_registrations) == 0L)
+            {
+                server_api()->stop().wait();
+                http_server_api::unsafe_register_server_api(nullptr);
+            }
+        }
+        catch (...)
+        {
+            // save the original exception from unregister listener
+            if (except == nullptr)
+            {
+                except = std::current_exception();
+            }
+        }
+
+        // rethrow exception if one occurred
+        if (except != nullptr)
+        {
+            std::rethrow_exception(except);
+        }
+    });
+}
+
+http_server* http_server_api::server_api() { return s_server_api.get(); }
+
+} // namespace details
+} // namespace experimental
+} // namespace http
+} // namespace web
+
+#endif
diff --git a/Release/src/http/listener/http_server_asio.cpp b/Release/src/http/listener/http_server_asio.cpp
new file mode 100644 (file)
index 0000000..e83b9ff
--- /dev/null
@@ -0,0 +1,1437 @@
+/***
+* Copyright (C) Microsoft. All rights reserved.
+* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+*
+* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+*
+* HTTP Library: HTTP listener (server-side) APIs
+
+* This file contains a cross platform implementation based on Boost.ASIO.
+*
+* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+*
+* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+*/
+#include "stdafx.h"
+
+#include <boost/algorithm/string/find.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/asio/read_until.hpp>
+#include <set>
+#include <sstream>
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+#endif
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#include "../common/internal_http_helpers.h"
+#include "cpprest/asyncrt_utils.h"
+#include "http_server_impl.h"
+#include "pplx/threadpool.h"
+
+#ifdef __ANDROID__
+using utility::conversions::details::to_string;
+#else
+using std::to_string;
+#endif
+
+using namespace boost::asio;
+using namespace boost::asio::ip;
+
+#define CRLF std::string("\r\n")
+#define CRLFCRLF std::string("\r\n\r\n")
+
+namespace listener = web::http::experimental::listener;
+namespace chunked_encoding = web::http::details::chunked_encoding;
+
+using web::uri;
+using web::http::header_names;
+using web::http::http_exception;
+using web::http::http_request;
+using web::http::http_response;
+using web::http::methods;
+using web::http::status_codes;
+using web::http::experimental::listener::http_listener_config;
+using web::http::experimental::listener::details::http_listener_impl;
+
+using utility::details::make_unique;
+
+namespace
+{
+class hostport_listener;
+class http_linux_server;
+class asio_server_connection;
+} // namespace
+
+namespace
+{
+struct iequal_to
+{
+    bool operator()(const std::string& left, const std::string& right) const
+    {
+        return boost::ilexicographical_compare(left, right);
+    }
+};
+
+class http_linux_server : public web::http::experimental::details::http_server
+{
+private:
+    friend class asio_server_connection;
+
+    pplx::extensibility::reader_writer_lock_t m_listeners_lock;
+    std::map<std::string, std::unique_ptr<hostport_listener>, iequal_to> m_listeners;
+    std::unordered_map<http_listener_impl*, std::unique_ptr<pplx::extensibility::reader_writer_lock_t>>
+        m_registered_listeners;
+    bool m_started;
+
+public:
+    http_linux_server() : m_listeners_lock(), m_listeners(), m_started(false) {}
+
+    ~http_linux_server() { stop(); }
+
+    virtual pplx::task<void> start();
+    virtual pplx::task<void> stop();
+
+    virtual pplx::task<void> register_listener(http_listener_impl* listener);
+    virtual pplx::task<void> unregister_listener(http_listener_impl* listener);
+
+    pplx::task<void> respond(http_response response);
+};
+
+struct linux_request_context : web::http::details::_http_server_context
+{
+    linux_request_context() {}
+
+    pplx::task_completion_event<void> m_response_completed;
+
+private:
+    linux_request_context(const linux_request_context&) = delete;
+    linux_request_context& operator=(const linux_request_context&) = delete;
+};
+
+class hostport_listener
+{
+private:
+    int m_backlog;
+    std::unique_ptr<boost::asio::ip::tcp::acceptor> m_acceptor;
+    std::map<std::string, http_listener_impl*> m_listeners;
+    pplx::extensibility::reader_writer_lock_t m_listeners_lock;
+
+    std::mutex m_connections_lock;
+    pplx::extensibility::event_t m_all_connections_complete;
+    std::set<asio_server_connection*> m_connections;
+
+    http_linux_server* m_p_server;
+
+    std::string m_host;
+    std::string m_port;
+
+    bool m_is_https;
+    const std::function<void(boost::asio::ssl::context&)>& m_ssl_context_callback;
+
+public:
+    hostport_listener(http_linux_server* server,
+                      const std::string& hostport,
+                      bool is_https,
+                      const http_listener_config& config)
+        : m_backlog(config.backlog())
+        , m_acceptor()
+        , m_listeners()
+        , m_listeners_lock()
+        , m_connections_lock()
+        , m_connections()
+        , m_p_server(server)
+        , m_is_https(is_https)
+        , m_ssl_context_callback(config.get_ssl_context_callback())
+    {
+        m_all_connections_complete.set();
+
+        std::istringstream hostport_in(hostport);
+        hostport_in.imbue(std::locale::classic());
+
+        std::getline(hostport_in, m_host, ':');
+        std::getline(hostport_in, m_port);
+    }
+
+    ~hostport_listener() { stop(); }
+
+    void start();
+    void stop();
+
+    void add_listener(const std::string& path, http_listener_impl* listener);
+    void remove_listener(const std::string& path, http_listener_impl* listener);
+
+    void internal_erase_connection(asio_server_connection*);
+
+    http_listener_impl* find_listener(uri const& u)
+    {
+        auto path_segments = uri::split_path(uri::decode(u.path()));
+        for (auto i = static_cast<long>(path_segments.size()); i >= 0; --i)
+        {
+            std::string path;
+            for (size_t j = 0; j < static_cast<size_t>(i); ++j)
+            {
+                path += "/" + utility::conversions::to_utf8string(path_segments[j]);
+            }
+            path += "/";
+
+            pplx::extensibility::scoped_read_lock_t lock(m_listeners_lock);
+            auto it = m_listeners.find(path);
+            if (it != m_listeners.end())
+            {
+                return it->second;
+            }
+        }
+        return nullptr;
+    }
+
+private:
+    void on_accept(std::unique_ptr<boost::asio::ip::tcp::socket> socket, const boost::system::error_code& ec);
+};
+
+} // namespace
+
+namespace
+{
+/// This class replaces the regex "\r\n\r\n|[\x00-\x1F]|[\x80-\xFF]"
+// It was added due to issues with regex on Android, however since
+// regex was rather overkill for such a simple parse it makes sense
+// to use it on all *nix platforms.
+//
+// This is used as part of the async_read_until call below; see the
+// following for more details:
+// http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference/async_read_until/overload4.html
+struct crlfcrlf_nonascii_searcher_t
+{
+    enum class State
+    {
+        none = 0,  // ".\r\n\r\n$"
+        cr = 1,    // "\r.\n\r\n$"
+                   // "  .\r\n\r\n$"
+        crlf = 2,  // "\r\n.\r\n$"
+                   // "    .\r\n\r\n$"
+        crlfcr = 3 // "\r\n\r.\n$"
+                   // "    \r.\n\r\n$"
+                   // "      .\r\n\r\n$"
+    };
+
+    // This function implements the searcher which "consumes" a certain amount of the input
+    // and returns whether or not there was a match (see above).
+
+    // From the Boost documentation:
+    // "The first member of the return value is an iterator marking one-past-the-end of the
+    //  bytes that have been consumed by the match function. This iterator is used to
+    //  calculate the begin parameter for any subsequent invocation of the match condition.
+    //  The second member of the return value is true if a match has been found, false
+    //  otherwise."
+    template<typename Iter>
+    std::pair<Iter, bool> operator()(const Iter begin, const Iter end) const
+    {
+        // In the case that we end inside a partially parsed match (like abcd\r\n\r),
+        // we need to signal the matcher to give us the partial match back again (\r\n\r).
+        // We use the excluded variable to keep track of this sequence point (abcd.\r\n\r
+        // in the previous example).
+        Iter excluded = begin;
+        Iter cur = begin;
+        State state = State::none;
+        while (cur != end)
+        {
+            const auto c = static_cast<unsigned char>(*cur);
+            if (c == '\r')
+            {
+                if (state == State::crlf)
+                {
+                    state = State::crlfcr;
+                }
+                else
+                {
+                    // In the case of State::cr or State::crlfcr, setting the state here
+                    // "skips" a none state and therefore fails to move up the excluded
+                    // counter.
+                    excluded = cur;
+                    state = State::cr;
+                }
+            }
+            else if (c == '\n')
+            {
+                if (state == State::cr)
+                {
+                    state = State::crlf;
+                }
+                else if (state == State::crlfcr)
+                {
+                    ++cur;
+                    return std::make_pair(cur, true);
+                }
+                else
+                {
+                    state = State::none;
+                }
+            }
+            else if (c <= 0x1Fu || c >= 0x80)
+            {
+                ++cur;
+                return std::make_pair(cur, true);
+            }
+            else
+            {
+                state = State::none;
+            }
+            ++cur;
+            if (state == State::none) excluded = cur;
+        }
+        return std::make_pair(excluded, false);
+    }
+} crlfcrlf_nonascii_searcher;
+
+// These structures serve as proof witnesses
+struct will_erase_from_parent_t
+{
+};
+struct will_deref_and_erase_t
+{
+};
+struct will_deref_t
+{
+};
+
+class asio_server_connection
+{
+private:
+    typedef void (asio_server_connection::*ResponseFuncPtr)(const http_response& response,
+                                                            const boost::system::error_code& ec);
+
+    std::unique_ptr<boost::asio::ip::tcp::socket> m_socket;
+    boost::asio::streambuf m_request_buf;
+    boost::asio::streambuf m_response_buf;
+    http_linux_server* m_p_server;
+    hostport_listener* m_p_parent;
+    mutable std::mutex m_request_mtx;
+    http_request m_request_tmp;
+
+    void set_request(http_request req)
+    {
+        std::lock_guard<std::mutex> lck(m_request_mtx);
+        m_request_tmp = std::move(req);
+    }
+
+    http_request get_request() const
+    {
+        std::lock_guard<std::mutex> lck(m_request_mtx);
+        return m_request_tmp;
+    }
+
+    size_t m_read, m_write;
+    size_t m_read_size, m_write_size;
+    bool m_close;
+    bool m_chunked;
+    std::atomic<int> m_refs; // track how many threads are still referring to this
+
+    using ssl_stream = boost::asio::ssl::stream<boost::asio::ip::tcp::socket&>;
+
+    std::unique_ptr<boost::asio::ssl::context> m_ssl_context;
+    std::unique_ptr<ssl_stream> m_ssl_stream;
+
+    asio_server_connection(std::unique_ptr<boost::asio::ip::tcp::socket> socket,
+                           http_linux_server* server,
+                           hostport_listener* parent)
+        : m_socket(std::move(socket))
+        , m_request_buf()
+        , m_response_buf()
+        , m_p_server(server)
+        , m_p_parent(parent)
+        , m_close(false)
+        , m_chunked(false)
+        , m_refs(1)
+    {
+    }
+
+    struct Dereferencer
+    {
+        void operator()(asio_server_connection* conn) const { conn->deref(); }
+    };
+
+public:
+    using refcount_ptr = std::unique_ptr<asio_server_connection, Dereferencer>;
+
+    static refcount_ptr create(std::unique_ptr<boost::asio::ip::tcp::socket> socket,
+                               http_linux_server* server,
+                               hostport_listener* parent)
+    {
+        return refcount_ptr(new asio_server_connection(std::move(socket), server, parent));
+    }
+
+    refcount_ptr get_reference()
+    {
+        ++m_refs;
+        return refcount_ptr(this);
+    }
+
+    will_erase_from_parent_t start_connection(
+        bool is_https, const std::function<void(boost::asio::ssl::context&)>& ssl_context_callback)
+    {
+        auto unique_reference = this->get_reference();
+
+        if (is_https)
+        {
+            m_ssl_context = make_unique<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
+            if (ssl_context_callback)
+            {
+                ssl_context_callback(*m_ssl_context);
+            }
+            m_ssl_stream = make_unique<ssl_stream>(*m_socket, *m_ssl_context);
+
+            m_ssl_stream->async_handshake(
+                boost::asio::ssl::stream_base::server,
+                [this](const boost::system::error_code&) { (will_deref_and_erase_t) this->start_request_response(); });
+            unique_reference.release();
+            return will_erase_from_parent_t {};
+        }
+        else
+        {
+            (will_deref_and_erase_t) start_request_response();
+            unique_reference.release();
+            return will_erase_from_parent_t {};
+        }
+    }
+
+    void close();
+
+    asio_server_connection(const asio_server_connection&) = delete;
+    asio_server_connection& operator=(const asio_server_connection&) = delete;
+
+private:
+    ~asio_server_connection() = default;
+
+    will_deref_and_erase_t start_request_response();
+    will_deref_and_erase_t handle_http_line(const boost::system::error_code& ec);
+    will_deref_and_erase_t handle_headers();
+    will_deref_t handle_body(const boost::system::error_code& ec);
+    will_deref_t handle_chunked_header(const boost::system::error_code& ec);
+    will_deref_t handle_chunked_body(const boost::system::error_code& ec, int toWrite);
+    will_deref_and_erase_t dispatch_request_to_listener();
+    will_erase_from_parent_t do_response()
+    {
+        auto unique_reference = this->get_reference();
+        get_request().get_response().then([=](pplx::task<http_response> r_task) {
+            http_response response;
+            try
+            {
+                response = r_task.get();
+            }
+            catch (...)
+            {
+                response = http_response(status_codes::InternalError);
+            }
+
+            serialize_headers(response);
+
+            // before sending response, the full incoming message need to be processed.
+            return get_request().content_ready().then([=](pplx::task<http_request>) {
+                (will_deref_and_erase_t) this->async_write(&asio_server_connection::handle_headers_written, response);
+            });
+        });
+        unique_reference.release();
+        return will_erase_from_parent_t {};
+    }
+    will_erase_from_parent_t do_bad_response()
+    {
+        auto unique_reference = this->get_reference();
+        get_request().get_response().then([=](pplx::task<http_response> r_task) {
+            http_response response;
+            try
+            {
+                response = r_task.get();
+            }
+            catch (...)
+            {
+                response = http_response(status_codes::InternalError);
+            }
+
+            // before sending response, the full incoming message need to be processed.
+            serialize_headers(response);
+
+            (will_deref_and_erase_t) async_write(&asio_server_connection::handle_headers_written, response);
+        });
+        unique_reference.release();
+        return will_erase_from_parent_t {};
+    }
+
+    will_deref_t async_handle_chunked_header();
+    template<typename ReadHandler>
+    void async_read_until_buffersize(size_t size, const ReadHandler& handler);
+    void serialize_headers(http_response response);
+    will_deref_and_erase_t cancel_sending_response_with_error(const http_response& response, const std::exception_ptr&);
+    will_deref_and_erase_t handle_headers_written(const http_response& response, const boost::system::error_code& ec);
+    will_deref_and_erase_t handle_write_large_response(const http_response& response,
+                                                       const boost::system::error_code& ec);
+    will_deref_and_erase_t handle_write_chunked_response(const http_response& response,
+                                                         const boost::system::error_code& ec);
+    will_deref_and_erase_t handle_response_written(const http_response& response, const boost::system::error_code& ec);
+    will_deref_and_erase_t finish_request_response();
+
+    using WriteFunc = decltype(&asio_server_connection::handle_headers_written);
+    will_deref_and_erase_t async_write(WriteFunc response_func_ptr, const http_response& response);
+
+    inline will_deref_t deref()
+    {
+        if (--m_refs == 0) delete this;
+        return will_deref_t {};
+    }
+};
+
+} // namespace
+
+namespace boost
+{
+namespace asio
+{
+template<>
+struct is_match_condition<crlfcrlf_nonascii_searcher_t> : public boost::true_type
+{
+};
+} // namespace asio
+} // namespace boost
+
+namespace
+{
+const size_t ChunkSize = 4 * 1024;
+
+void hostport_listener::internal_erase_connection(asio_server_connection* conn)
+{
+    std::lock_guard<std::mutex> lock(m_connections_lock);
+    m_connections.erase(conn);
+    if (m_connections.empty())
+    {
+        m_all_connections_complete.set();
+    }
+}
+
+void hostport_listener::start()
+{
+    // resolve the endpoint address
+    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, 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);
+
+    m_acceptor.reset(new tcp::acceptor(service));
+    m_acceptor->open(endpoint.protocol());
+    m_acceptor->set_option(socket_base::reuse_address(true));
+    m_acceptor->bind(endpoint);
+    m_acceptor->listen(0 != m_backlog ? m_backlog : socket_base::max_connections);
+
+    auto socket = new ip::tcp::socket(service);
+    std::unique_ptr<ip::tcp::socket> usocket(socket);
+    m_acceptor->async_accept(*socket, [this, socket](const boost::system::error_code& ec) {
+        std::unique_ptr<ip::tcp::socket> usocket(socket);
+        this->on_accept(std::move(usocket), ec);
+    });
+    usocket.release();
+}
+
+void asio_server_connection::close()
+{
+    m_close = true;
+    auto sock = m_socket.get();
+    if (sock != nullptr)
+    {
+        boost::system::error_code ec;
+        sock->cancel(ec);
+        sock->shutdown(tcp::socket::shutdown_both, ec);
+        sock->close(ec);
+    }
+    get_request()._reply_if_not_already(status_codes::InternalError);
+}
+
+will_deref_and_erase_t asio_server_connection::start_request_response()
+{
+    m_read_size = 0;
+    m_read = 0;
+    m_request_buf.consume(m_request_buf.size()); // clear the buffer
+
+    if (m_ssl_stream)
+    {
+        boost::asio::async_read_until(
+            *m_ssl_stream, m_request_buf, CRLFCRLF, [this](const boost::system::error_code& ec, std::size_t) {
+                (will_deref_and_erase_t) this->handle_http_line(ec);
+            });
+    }
+    else
+    {
+        boost::asio::async_read_until(*m_socket,
+                                      m_request_buf,
+                                      crlfcrlf_nonascii_searcher,
+                                      [this](const boost::system::error_code& ec, std::size_t) {
+                                          (will_deref_and_erase_t) this->handle_http_line(ec);
+                                      });
+    }
+    return will_deref_and_erase_t {};
+}
+
+void hostport_listener::on_accept(std::unique_ptr<ip::tcp::socket> socket, const boost::system::error_code& ec)
+{
+    // Listener closed
+    if (ec == boost::asio::error::operation_aborted)
+    {
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(m_connections_lock);
+
+    // 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());
+        try
+        {
+            (will_erase_from_parent_t) conn->start_connection(m_is_https, m_ssl_context_callback);
+            // at this point an asynchronous task has been launched which will call
+            // m_connections.erase(conn.get()) eventually
+
+            // the following cannot throw
+            if (m_connections.size() == 1) m_all_connections_complete.reset();
+        }
+        catch (boost::system::system_error&)
+        {
+            // boost ssl apis throw boost::system::system_error.
+            // Exception indicates something went wrong setting ssl context.
+            // Drop connection and continue handling other connections.
+            m_connections.erase(conn.get());
+        }
+    }
+
+    if (m_acceptor)
+    {
+        // spin off another async accept
+        auto newSocket = new ip::tcp::socket(crossplat::threadpool::shared_instance().service());
+        std::unique_ptr<ip::tcp::socket> usocket(newSocket);
+        m_acceptor->async_accept(*newSocket, [this, newSocket](const boost::system::error_code& ec) {
+            std::unique_ptr<ip::tcp::socket> usocket(newSocket);
+            this->on_accept(std::move(usocket), ec);
+        });
+        usocket.release();
+    }
+}
+
+will_deref_and_erase_t asio_server_connection::handle_http_line(const boost::system::error_code& ec)
+{
+    auto thisRequest = http_request::_create_request(make_unique<linux_request_context>());
+    set_request(thisRequest);
+    if (ec)
+    {
+        // client closed connection
+        if (ec == boost::asio::error::eof || // peer has performed an orderly shutdown
+            ec ==
+                boost::asio::error::operation_aborted ||  // this can be removed. ECONNABORTED happens only for accept()
+            ec == boost::asio::error::connection_reset || // connection reset by peer
+            ec == boost::asio::error::timed_out           // connection timed out
+        )
+        {
+            return finish_request_response();
+        }
+        else
+        {
+            thisRequest._reply_if_not_already(status_codes::BadRequest);
+            m_close = true;
+            (will_erase_from_parent_t) do_bad_response();
+            (will_deref_t) deref();
+            return will_deref_and_erase_t {};
+        }
+    }
+    else
+    {
+        // read http status line
+        std::istream request_stream(&m_request_buf);
+        request_stream.imbue(std::locale::classic());
+        std::skipws(request_stream);
+
+        web::http::method http_verb;
+#ifndef _UTF16_STRINGS
+        request_stream >> http_verb;
+#else
+        {
+            std::string tmp;
+            request_stream >> tmp;
+            http_verb = utility::conversions::latin1_to_utf16(tmp);
+        }
+#endif
+
+        if (boost::iequals(http_verb, methods::GET))
+            http_verb = methods::GET;
+        else if (boost::iequals(http_verb, methods::POST))
+            http_verb = methods::POST;
+        else if (boost::iequals(http_verb, methods::PUT))
+            http_verb = methods::PUT;
+        else if (boost::iequals(http_verb, methods::DEL))
+            http_verb = methods::DEL;
+        else if (boost::iequals(http_verb, methods::HEAD))
+            http_verb = methods::HEAD;
+        else if (boost::iequals(http_verb, methods::TRCE))
+            http_verb = methods::TRCE;
+        else if (boost::iequals(http_verb, methods::CONNECT))
+            http_verb = methods::CONNECT;
+        else if (boost::iequals(http_verb, methods::OPTIONS))
+            http_verb = methods::OPTIONS;
+
+        // Check to see if there is not allowed character on the input
+        if (!web::http::details::validate_method(http_verb))
+        {
+            thisRequest.reply(status_codes::BadRequest);
+            m_close = true;
+            (will_erase_from_parent_t) do_bad_response();
+            (will_deref_t) deref();
+            return will_deref_and_erase_t {};
+        }
+
+        thisRequest.set_method(http_verb);
+
+        std::string http_path_and_version;
+        std::getline(request_stream, http_path_and_version);
+        const size_t VersionPortionSize = sizeof(" HTTP/1.1\r") - 1;
+
+        // Make sure path and version is long enough to contain the HTTP version
+        if (http_path_and_version.size() < VersionPortionSize + 2)
+        {
+            thisRequest.reply(status_codes::BadRequest);
+            m_close = true;
+            (will_erase_from_parent_t) do_bad_response();
+            (will_deref_t) deref();
+            return will_deref_and_erase_t {};
+        }
+
+        // Get the path - remove the version portion and prefix space
+        try
+        {
+            thisRequest.set_request_uri(utility::conversions::to_string_t(
+                http_path_and_version.substr(1, http_path_and_version.size() - VersionPortionSize - 1)));
+        }
+        catch (const std::exception& e) // may be std::range_error indicating invalid Unicode, or web::uri_exception
+        {
+            thisRequest.reply(status_codes::BadRequest, e.what());
+            m_close = true;
+            (will_erase_from_parent_t) do_bad_response();
+            (will_deref_t) deref();
+            return will_deref_and_erase_t {};
+        }
+
+        // Get the version
+        std::string http_version =
+            http_path_and_version.substr(http_path_and_version.size() - VersionPortionSize + 1, VersionPortionSize - 2);
+
+        auto requestImpl = thisRequest._get_impl().get();
+        web::http::http_version parsed_version = web::http::http_version::from_string(http_version);
+        requestImpl->_set_http_version(parsed_version);
+
+        // if HTTP version is 1.0 then disable pipelining
+        if (parsed_version == web::http::http_versions::HTTP_1_0)
+        {
+            m_close = true;
+        }
+
+        // Get the remote IP address
+        boost::system::error_code socket_ec;
+        auto endpoint = m_socket->remote_endpoint(socket_ec);
+        if (!socket_ec)
+        {
+            requestImpl->_set_remote_address(utility::conversions::to_string_t(endpoint.address().to_string()));
+        }
+
+        return handle_headers();
+    }
+}
+
+will_deref_and_erase_t asio_server_connection::handle_headers()
+{
+    std::istream request_stream(&m_request_buf);
+    request_stream.imbue(std::locale::classic());
+    std::string header;
+
+    auto currentRequest = get_request();
+    auto& headers = currentRequest.headers();
+
+    while (std::getline(request_stream, header) && header != "\r")
+    {
+        auto colon = header.find(':');
+        if (colon != std::string::npos && colon != 0)
+        {
+            auto name = utility::conversions::to_string_t(header.substr(0, colon));
+            auto value = utility::conversions::to_string_t(
+                header.substr(colon + 1, header.length() - (colon + 1))); // also exclude '\r'
+            web::http::details::trim_whitespace(name);
+            web::http::details::trim_whitespace(value);
+
+            if (boost::iequals(name, header_names::content_length))
+            {
+                headers[header_names::content_length] = value;
+            }
+            else
+            {
+                headers.add(name, value);
+            }
+        }
+        else
+        {
+            currentRequest.reply(status_codes::BadRequest);
+            m_close = true;
+            (will_erase_from_parent_t) do_bad_response();
+            (will_deref_t) deref();
+            return will_deref_and_erase_t {};
+        }
+    }
+
+    m_chunked = false;
+    utility::string_t name;
+    // check if the client has requested we close the connection
+    if (currentRequest.headers().match(header_names::connection, name))
+    {
+        m_close = boost::iequals(name, U("close"));
+    }
+
+    if (currentRequest.headers().match(header_names::transfer_encoding, name))
+    {
+        m_chunked = boost::ifind_first(name, U("chunked"));
+    }
+
+    currentRequest._get_impl()->_prepare_to_receive_data();
+    if (m_chunked)
+    {
+        ++m_refs;
+        (will_deref_t) async_handle_chunked_header();
+        return dispatch_request_to_listener();
+    }
+
+    if (!currentRequest.headers().match(header_names::content_length, m_read_size))
+    {
+        m_read_size = 0;
+    }
+
+    if (m_read_size == 0)
+    {
+        currentRequest._get_impl()->_complete(0);
+    }
+    else // need to read the sent data
+    {
+        m_read = 0;
+        ++m_refs;
+        async_read_until_buffersize(
+            (std::min)(ChunkSize, m_read_size),
+            [this](const boost::system::error_code& ec, size_t) { (will_deref_t) this->handle_body(ec); });
+    }
+
+    return dispatch_request_to_listener();
+}
+
+will_deref_t asio_server_connection::handle_chunked_header(const boost::system::error_code& ec)
+{
+    auto requestImpl = get_request()._get_impl();
+    if (ec)
+    {
+        requestImpl->_complete(0, std::make_exception_ptr(http_exception(ec.value())));
+        return deref();
+    }
+    else
+    {
+        std::istream is(&m_request_buf);
+        is.imbue(std::locale::classic());
+        int len;
+        is >> std::hex >> len;
+        m_request_buf.consume(CRLF.size());
+        m_read += len;
+        if (len == 0)
+        {
+            requestImpl->_complete(m_read);
+            return deref();
+        }
+        else
+        {
+            async_read_until_buffersize(len + 2, [this, len](const boost::system::error_code& ec, size_t) {
+                (will_deref_t) this->handle_chunked_body(ec, len);
+            });
+            return will_deref_t {};
+        }
+    }
+}
+
+will_deref_t asio_server_connection::handle_chunked_body(const boost::system::error_code& ec, int toWrite)
+{
+    auto requestImpl = get_request()._get_impl();
+    if (ec)
+    {
+        requestImpl->_complete(0, std::make_exception_ptr(http_exception(ec.value())));
+        return deref();
+    }
+    else
+    {
+        auto writebuf = requestImpl->outstream().streambuf();
+        writebuf.putn_nocopy(buffer_cast<const uint8_t*>(m_request_buf.data()), toWrite)
+            .then([=](pplx::task<size_t> writeChunkTask) -> will_deref_t {
+                try
+                {
+                    writeChunkTask.get();
+                }
+                catch (...)
+                {
+                    requestImpl->_complete(0, std::current_exception());
+                    return deref();
+                }
+
+                m_request_buf.consume(2 + toWrite);
+                return async_handle_chunked_header();
+            });
+        return will_deref_t {};
+    }
+}
+
+will_deref_t asio_server_connection::handle_body(const boost::system::error_code& ec)
+{
+    auto requestImpl = get_request()._get_impl();
+    // read body
+    if (ec)
+    {
+        requestImpl->_complete(0, std::make_exception_ptr(http_exception(ec.value())));
+        return deref();
+    }
+    else if (m_read < m_read_size) // there is more to read
+    {
+        auto writebuf = requestImpl->outstream().streambuf();
+        writebuf
+            .putn_nocopy(boost::asio::buffer_cast<const uint8_t*>(m_request_buf.data()),
+                         (std::min)(m_request_buf.size(), m_read_size - m_read))
+            .then([this](pplx::task<size_t> writtenSizeTask) -> will_deref_t {
+                size_t writtenSize = 0;
+                try
+                {
+                    writtenSize = writtenSizeTask.get();
+                }
+                catch (...)
+                {
+                    get_request()._get_impl()->_complete(0, std::current_exception());
+                    return deref();
+                }
+                m_read += writtenSize;
+                m_request_buf.consume(writtenSize);
+
+                async_read_until_buffersize(
+                    (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 {};
+            });
+        return will_deref_t {};
+    }
+    else // have read request body
+    {
+        requestImpl->_complete(m_read);
+        return deref();
+    }
+}
+
+will_deref_and_erase_t asio_server_connection::async_write(WriteFunc response_func_ptr, const http_response& response)
+{
+    if (m_ssl_stream)
+    {
+        boost::asio::async_write(*m_ssl_stream, m_response_buf, [=](const boost::system::error_code& ec, std::size_t) {
+            (this->*response_func_ptr)(response, ec);
+        });
+    }
+    else
+    {
+        boost::asio::async_write(*m_socket, m_response_buf, [=](const boost::system::error_code& ec, std::size_t) {
+            (this->*response_func_ptr)(response, ec);
+        });
+    }
+    return will_deref_and_erase_t {};
+}
+
+will_deref_t asio_server_connection::async_handle_chunked_header()
+{
+    if (m_ssl_stream)
+    {
+        boost::asio::async_read_until(
+            *m_ssl_stream, m_request_buf, CRLF, [this](const boost::system::error_code& ec, size_t) {
+                (will_deref_t) this->handle_chunked_header(ec);
+            });
+    }
+    else
+    {
+        boost::asio::async_read_until(
+            *m_socket, m_request_buf, CRLF, [this](const boost::system::error_code& ec, size_t) {
+                (will_deref_t) this->handle_chunked_header(ec);
+            });
+    }
+    return will_deref_t {};
+}
+
+template<typename ReadHandler>
+void asio_server_connection::async_read_until_buffersize(size_t size, const ReadHandler& handler)
+{
+    // The condition is such that after completing the async_read below, m_request_buf will contain at least `size`
+    // bytes.
+    auto condition = transfer_at_least(0);
+
+    auto bufsize = m_request_buf.size();
+    if (size > bufsize)
+    {
+        condition = transfer_at_least(size - bufsize);
+    }
+
+    if (m_ssl_stream)
+    {
+        boost::asio::async_read(*m_ssl_stream, m_request_buf, condition, handler);
+    }
+    else
+    {
+        boost::asio::async_read(*m_socket, m_request_buf, condition, handler);
+    }
+}
+
+will_deref_and_erase_t asio_server_connection::dispatch_request_to_listener()
+{
+    // locate the listener:
+    http_listener_impl* pListener = nullptr;
+    auto currentRequest = get_request();
+    try
+    {
+        pListener = m_p_parent->find_listener(currentRequest.relative_uri());
+    }
+    catch (const std::exception&) // may be web::uri_exception, or std::range_error indicating invalid Unicode
+    {
+        currentRequest.reply(status_codes::BadRequest);
+        (will_erase_from_parent_t) do_response();
+        (will_deref_t) deref();
+        return will_deref_and_erase_t {};
+    }
+
+    if (pListener == nullptr)
+    {
+        currentRequest.reply(status_codes::NotFound);
+        (will_erase_from_parent_t) do_response();
+        (will_deref_t) deref();
+        return will_deref_and_erase_t {};
+    }
+
+    currentRequest._set_listener_path(pListener->uri().path());
+    (will_erase_from_parent_t) do_response();
+
+    // Look up the lock for the http_listener.
+    pplx::extensibility::reader_writer_lock_t* pListenerLock;
+    {
+        pplx::extensibility::reader_writer_lock_t::scoped_lock_read lock(m_p_server->m_listeners_lock);
+
+        // It is possible the listener could have unregistered.
+        if (m_p_server->m_registered_listeners.find(pListener) == m_p_server->m_registered_listeners.end())
+        {
+            currentRequest.reply(status_codes::NotFound);
+
+            (will_deref_t) deref();
+            return will_deref_and_erase_t {};
+        }
+        pListenerLock = m_p_server->m_registered_listeners[pListener].get();
+
+        // We need to acquire the listener's lock before releasing the registered listeners lock.
+        // But we don't need to hold the registered listeners lock when calling into the user's code.
+        pListenerLock->lock_read();
+    }
+
+    try
+    {
+        pListener->handle_request(currentRequest);
+        pListenerLock->unlock();
+    }
+    catch (...)
+    {
+        pListenerLock->unlock();
+        currentRequest._reply_if_not_already(status_codes::InternalError);
+    }
+
+    (will_deref_t) deref();
+    return will_deref_and_erase_t {};
+}
+
+void asio_server_connection::serialize_headers(http_response response)
+{
+    m_response_buf.consume(m_response_buf.size()); // clear the buffer
+    std::ostream os(&m_response_buf);
+    os.imbue(std::locale::classic());
+
+    os << "HTTP/1.1 " << response.status_code() << " " << utility::conversions::to_utf8string(response.reason_phrase())
+       << CRLF;
+
+    m_chunked = false;
+    m_write = m_write_size = 0;
+
+    std::string transferencoding;
+    if (response.headers().match(header_names::transfer_encoding, transferencoding) && transferencoding == "chunked")
+    {
+        m_chunked = true;
+    }
+    if (!response.headers().match(header_names::content_length, m_write_size) && response.body())
+    {
+        m_chunked = true;
+        response.headers()[header_names::transfer_encoding] = U("chunked");
+    }
+    if (!response.body())
+    {
+        response.headers().add(header_names::content_length, 0);
+    }
+
+    for (const auto& header : response.headers())
+    {
+        // check if the responder has requested we close the connection
+        if (boost::iequals(header.first, U("connection")))
+        {
+            if (boost::iequals(header.second, U("close")))
+            {
+                m_close = true;
+            }
+        }
+        os << utility::conversions::to_utf8string(header.first) << ": "
+           << utility::conversions::to_utf8string(header.second) << CRLF;
+    }
+    os << CRLF;
+}
+
+will_deref_and_erase_t asio_server_connection::cancel_sending_response_with_error(const http_response& response,
+                                                                                  const std::exception_ptr& eptr)
+{
+    auto* context = static_cast<linux_request_context*>(response._get_server_context());
+    context->m_response_completed.set_exception(eptr);
+
+    // always terminate the connection since error happens
+    return finish_request_response();
+}
+
+will_deref_and_erase_t asio_server_connection::handle_write_chunked_response(const http_response& response,
+                                                                             const boost::system::error_code& ec)
+{
+    if (ec)
+    {
+        return handle_response_written(response, ec);
+    }
+
+    auto readbuf = response._get_impl()->instream().streambuf();
+    if (readbuf.is_eof())
+    {
+        return cancel_sending_response_with_error(
+            response, std::make_exception_ptr(http_exception("Response stream close early!")));
+    }
+    auto membuf = m_response_buf.prepare(ChunkSize + chunked_encoding::additional_encoding_space);
+
+    readbuf.getn(buffer_cast<uint8_t*>(membuf) + chunked_encoding::data_offset, ChunkSize)
+        .then([=](pplx::task<size_t> actualSizeTask) -> will_deref_and_erase_t {
+            size_t actualSize = 0;
+            try
+            {
+                actualSize = actualSizeTask.get();
+            }
+            catch (...)
+            {
+                return cancel_sending_response_with_error(response, std::current_exception());
+            }
+            size_t offset = chunked_encoding::add_chunked_delimiters(
+                buffer_cast<uint8_t*>(membuf), ChunkSize + chunked_encoding::additional_encoding_space, actualSize);
+            m_response_buf.commit(actualSize + chunked_encoding::additional_encoding_space);
+            m_response_buf.consume(offset);
+            if (actualSize == 0)
+                return async_write(&asio_server_connection::handle_response_written, response);
+            else
+                return async_write(&asio_server_connection::handle_write_chunked_response, response);
+        });
+    return will_deref_and_erase_t {};
+}
+
+will_deref_and_erase_t asio_server_connection::handle_write_large_response(const http_response& response,
+                                                                           const boost::system::error_code& ec)
+{
+    if (ec || m_write == m_write_size) return handle_response_written(response, ec);
+
+    auto readbuf = response._get_impl()->instream().streambuf();
+    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);
+    readbuf.getn(buffer_cast<uint8_t*>(m_response_buf.prepare(readBytes)), readBytes)
+        .then([=](pplx::task<size_t> actualSizeTask) -> will_deref_and_erase_t {
+            size_t actualSize = 0;
+            try
+            {
+                actualSize = actualSizeTask.get();
+            }
+            catch (...)
+            {
+                return cancel_sending_response_with_error(response, std::current_exception());
+            }
+            m_write += actualSize;
+            m_response_buf.commit(actualSize);
+            return async_write(&asio_server_connection::handle_write_large_response, response);
+        });
+    return will_deref_and_erase_t {};
+}
+
+will_deref_and_erase_t asio_server_connection::handle_headers_written(const http_response& response,
+                                                                      const boost::system::error_code& ec)
+{
+    if (ec)
+    {
+        return cancel_sending_response_with_error(
+            response, std::make_exception_ptr(http_exception(ec.value(), "error writing headers")));
+    }
+    else
+    {
+        if (m_chunked)
+            return handle_write_chunked_response(response, ec);
+        else
+            return handle_write_large_response(response, ec);
+    }
+}
+
+will_deref_and_erase_t asio_server_connection::handle_response_written(const http_response& response,
+                                                                       const boost::system::error_code& ec)
+{
+    auto* context = static_cast<linux_request_context*>(response._get_server_context());
+    if (ec)
+    {
+        return cancel_sending_response_with_error(
+            response, std::make_exception_ptr(http_exception(ec.value(), "error writing response")));
+    }
+    else
+    {
+        context->m_response_completed.set();
+        if (!m_close)
+        {
+            return start_request_response();
+        }
+        else
+        {
+            return finish_request_response();
+        }
+    }
+}
+
+will_deref_and_erase_t asio_server_connection::finish_request_response()
+{
+    // kill the connection
+    m_p_parent->internal_erase_connection(this);
+
+    close();
+    (will_deref_t) deref();
+
+    // internal_erase_connection has been called above.
+    return will_deref_and_erase_t {};
+}
+
+void hostport_listener::stop()
+{
+    // halt existing connections
+    {
+        std::lock_guard<std::mutex> lock(m_connections_lock);
+        m_acceptor.reset();
+        for (auto connection : m_connections)
+        {
+            connection->close();
+        }
+    }
+
+    m_all_connections_complete.wait();
+}
+
+void hostport_listener::add_listener(const std::string& path, http_listener_impl* listener)
+{
+    pplx::extensibility::scoped_rw_lock_t lock(m_listeners_lock);
+
+    if (m_is_https != (listener->uri().scheme() == U("https")))
+        throw std::invalid_argument(
+            "Error: http_listener can not simultaneously listen both http and https paths of one host");
+    else if (!m_listeners.insert(std::map<std::string, http_listener_impl*>::value_type(path, listener)).second)
+        throw std::invalid_argument("Error: http_listener is already registered for this path");
+}
+
+void hostport_listener::remove_listener(const std::string& path, http_listener_impl*)
+{
+    pplx::extensibility::scoped_rw_lock_t lock(m_listeners_lock);
+
+    if (m_listeners.erase(path) != 1) throw std::invalid_argument("Error: no http_listener found for this path");
+}
+
+pplx::task<void> http_linux_server::start()
+{
+    pplx::extensibility::reader_writer_lock_t::scoped_lock_read lock(m_listeners_lock);
+
+    auto it = m_listeners.begin();
+    try
+    {
+        for (; it != m_listeners.end(); ++it)
+        {
+            it->second->start();
+        }
+    }
+    catch (...)
+    {
+        while (it != m_listeners.begin())
+        {
+            --it;
+            it->second->stop();
+        }
+        return pplx::task_from_exception<void>(std::current_exception());
+    }
+
+    m_started = true;
+    return pplx::task_from_result();
+}
+
+pplx::task<void> http_linux_server::stop()
+{
+    pplx::extensibility::reader_writer_lock_t::scoped_lock_read lock(m_listeners_lock);
+
+    m_started = false;
+
+    for (auto& listener : m_listeners)
+    {
+        listener.second->stop();
+    }
+
+    return pplx::task_from_result();
+}
+
+std::pair<std::string, std::string> canonical_parts(const uri& uri)
+{
+    std::string endpoint;
+    endpoint += utility::conversions::to_utf8string(uri::decode(uri.host()));
+    endpoint += ":";
+    endpoint += to_string(uri.port());
+
+    auto path = utility::conversions::to_utf8string(uri::decode(uri.path()));
+
+    if (path.size() > 1 && path[path.size() - 1] != '/')
+    {
+        path += "/"; // ensure the end slash is present
+    }
+
+    return std::make_pair(std::move(endpoint), std::move(path));
+}
+
+pplx::task<void> http_linux_server::register_listener(http_listener_impl* listener)
+{
+    auto parts = canonical_parts(listener->uri());
+    auto hostport = parts.first;
+    auto path = parts.second;
+    bool is_https = listener->uri().scheme() == U("https");
+
+    {
+        pplx::extensibility::scoped_rw_lock_t lock(m_listeners_lock);
+        if (m_registered_listeners.find(listener) != m_registered_listeners.end())
+        {
+            throw std::invalid_argument("listener already registered");
+        }
+
+        try
+        {
+            m_registered_listeners[listener] = make_unique<pplx::extensibility::reader_writer_lock_t>();
+
+            auto found_hostport_listener = m_listeners.find(hostport);
+            if (found_hostport_listener == m_listeners.end())
+            {
+                found_hostport_listener =
+                    m_listeners
+                        .insert(std::make_pair(
+                            hostport,
+                            make_unique<hostport_listener>(this, hostport, is_https, listener->configuration())))
+                        .first;
+
+                if (m_started)
+                {
+                    found_hostport_listener->second->start();
+                }
+            }
+
+            found_hostport_listener->second->add_listener(path, listener);
+        }
+        catch (...)
+        {
+            // Future improvement - really this API should entirely be asynchronously.
+            // the hostport_listener::start() method should be made to return a task
+            // throwing the exception.
+            m_registered_listeners.erase(listener);
+            m_listeners.erase(hostport);
+            throw;
+        }
+    }
+
+    return pplx::task_from_result();
+}
+
+pplx::task<void> http_linux_server::unregister_listener(http_listener_impl* listener)
+{
+    auto parts = canonical_parts(listener->uri());
+    auto hostport = parts.first;
+    auto path = parts.second;
+    // First remove the listener from hostport listener
+    {
+        pplx::extensibility::scoped_read_lock_t lock(m_listeners_lock);
+        auto itr = m_listeners.find(hostport);
+        if (itr == m_listeners.end())
+        {
+            throw std::invalid_argument("Error: no listener registered for that host");
+        }
+
+        itr->second->remove_listener(path, listener);
+    }
+
+    // Second remove the listener form listener collection
+    std::unique_ptr<pplx::extensibility::reader_writer_lock_t> pListenerLock = nullptr;
+    {
+        pplx::extensibility::scoped_rw_lock_t lock(m_listeners_lock);
+        pListenerLock = std::move(m_registered_listeners[listener]);
+        m_registered_listeners[listener] = nullptr;
+        m_registered_listeners.erase(listener);
+    }
+
+    // Then take the listener write lock to make sure there are no calls into the listener's
+    // request handler.
+    if (pListenerLock != nullptr)
+    {
+        pplx::extensibility::scoped_rw_lock_t lock(*pListenerLock);
+    }
+
+    return pplx::task_from_result();
+}
+
+pplx::task<void> http_linux_server::respond(http_response response)
+{
+    linux_request_context* p_context = static_cast<linux_request_context*>(response._get_server_context());
+    return pplx::create_task(p_context->m_response_completed);
+}
+
+} // namespace
+
+namespace web
+{
+namespace http
+{
+namespace experimental
+{
+namespace details
+{
+std::unique_ptr<http_server> make_http_asio_server() { return make_unique<http_linux_server>(); }
+
+} // namespace details
+} // namespace experimental
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/listener/http_server_httpsys.cpp b/Release/src/http/listener/http_server_httpsys.cpp
new file mode 100644 (file)
index 0000000..2eac0ba
--- /dev/null
@@ -0,0 +1,1267 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: HTTP listener (server-side) APIs
+ *
+ * This file contains implementation built on Windows HTTP Server APIs.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "cpprest/rawptrstream.h"
+
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+
+#pragma comment(lib, "Ws2_32")
+
+#include "http_server_httpsys.h"
+#include "http_server_impl.h"
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace utility::conversions;
+using namespace http::details;
+using namespace http::experimental::listener;
+using namespace http::experimental::details;
+
+#define CHUNK_SIZE (64 * 1024)
+
+namespace web
+{
+namespace http
+{
+namespace experimental
+{
+namespace details
+{
+/// <summary>
+/// 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.
+/// </summary>
+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)
+{
+    dest = utility::conversions::to_utf16string(std::string(src));
+}
+
+http::method parse_request_method(const HTTP_REQUEST* p_request)
+{
+    http::method method;
+
+    switch (p_request->Verb)
+    {
+        case HttpVerbGET: method = methods::GET; break;
+        case HttpVerbPOST: method = methods::POST; break;
+        case HttpVerbPUT: method = methods::PUT; break;
+        case HttpVerbDELETE: method = methods::DEL; break;
+        case HttpVerbHEAD: method = methods::HEAD; break;
+        case HttpVerbOPTIONS: method = methods::OPTIONS; break;
+        case HttpVerbTRACE: method = methods::TRCE; break;
+        case HttpVerbCONNECT: method = methods::CONNECT; break;
+        case HttpVerbUnknown: char_to_wstring(method, p_request->pUnknownVerb); break;
+        case HttpVerbMOVE: method = _XPLATSTR("MOVE"); break;
+        case HttpVerbCOPY: method = _XPLATSTR("COPY"); break;
+        case HttpVerbPROPFIND: method = _XPLATSTR("PROPFIND"); break;
+        case HttpVerbPROPPATCH: method = _XPLATSTR("PROPPATCH"); break;
+        case HttpVerbMKCOL: method = _XPLATSTR("MKCOL"); break;
+        case HttpVerbLOCK: method = _XPLATSTR("LOCK"); break;
+        case HttpVerbUNLOCK: method = _XPLATSTR("UNLOCK"); break;
+        case HttpVerbSEARCH: method = _XPLATSTR("SEARCH"); break;
+        default: break;
+    }
+    return method;
+}
+
+void parse_http_headers(const HTTP_REQUEST_HEADERS& headers, http::http_headers& msgHeaders)
+{
+    //
+    // This is weird for the 'KnownHeaders' but there is no way I can find with the HTTP Server API
+    // to get all the raw headers. The known ones are stored in an array index by a HTTP Server API
+    // enumeration.
+    //
+    // TFS 354587 As a perf optimization we could parse the headers from Windows on demand in the
+    //      http_header class itself.
+    for (USHORT i = 0; i < headers.UnknownHeaderCount; ++i)
+    {
+        utf16string unknown_header_name;
+        char_to_wstring(unknown_header_name, headers.pUnknownHeaders[i].pName);
+
+        // header value can be empty
+        if (headers.pUnknownHeaders[i].RawValueLength > 0)
+        {
+            msgHeaders.add(unknown_header_name,
+                           utility::conversions::to_utf16string(headers.pUnknownHeaders[i].pRawValue));
+        }
+        else
+        {
+            msgHeaders[unknown_header_name].clear();
+        }
+    }
+    for (int i = 0; i < HttpHeaderMaximum; ++i)
+    {
+        if (headers.KnownHeaders[i].RawValueLength > 0)
+        {
+            msgHeaders.add(HttpServerAPIRequestKnownHeaders[i],
+                           utility::conversions::to_utf16string(headers.KnownHeaders[i].pRawValue));
+        }
+    }
+}
+
+http_windows_server::http_windows_server()
+{
+    HTTPAPI_VERSION httpApiVersion = HTTPAPI_VERSION_2;
+    HttpInitialize(httpApiVersion, HTTP_INITIALIZE_SERVER, NULL);
+}
+
+http_windows_server::~http_windows_server() { HttpTerminate(HTTP_INITIALIZE_SERVER, NULL); }
+
+pplx::task<void> http_windows_server::register_listener(
+    _In_ web::http::experimental::listener::details::http_listener_impl* pListener)
+{
+    unsigned long errorCode;
+
+    // Create a url group for this listener.
+    HTTP_URL_GROUP_ID urlGroupId;
+    errorCode = HttpCreateUrlGroup(m_serverSessionId, &urlGroupId, 0);
+    if (errorCode != NO_ERROR)
+    {
+        return pplx::task_from_exception<void>(http_exception(errorCode));
+    }
+
+    // Add listener's URI to the new group.
+    http::uri u = pListener->uri();
+    if (u.is_port_default())
+    {
+        // Windows HTTP Server API has issues when the port isn't set to 80 here -- it expects a url prefix string
+        // which always includes the port number
+        // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364698(v=vs.85).aspx
+        http::uri_builder builder(u);
+        builder.set_port(80);
+        u = builder.to_uri();
+    }
+
+    // Windows HTTP Server API will not accept a uri with an empty path, it must have a '/'.
+    // Windows HTTP Server API will only accept decoded uri strings.
+    utility::string_t host_uri = http::uri::decode(u.to_string());
+    if (host_uri.back() != U('/') && u.query().empty() && u.fragment().empty())
+    {
+        host_uri.push_back(U('/'));
+    }
+
+    // inside here we check for a few specific error types that know about
+    // there may be more possibilities for windows to return a different error
+    errorCode = HttpAddUrlToUrlGroup(urlGroupId, host_uri.c_str(), (HTTP_URL_CONTEXT)pListener, 0);
+    if (errorCode)
+    {
+        HttpCloseUrlGroup(urlGroupId);
+        if (errorCode == ERROR_ALREADY_EXISTS || errorCode == ERROR_SHARING_VIOLATION)
+        {
+            return pplx::task_from_exception<void>(http_exception(
+                errorCode, _XPLATSTR("Address '") + pListener->uri().to_string() + _XPLATSTR("' is already in use")));
+        }
+        else if (errorCode == ERROR_ACCESS_DENIED)
+        {
+            return pplx::task_from_exception<void>(
+                http_exception(errorCode,
+                               _XPLATSTR("Access denied: attempting to add Address '") + pListener->uri().to_string() +
+                                   _XPLATSTR("'. Run as administrator to listen on an hostname other ")
+                                       _XPLATSTR("than localhost, or to listen on port 80.")));
+        }
+        else
+        {
+            return pplx::task_from_exception<void>(
+                http_exception(errorCode, _XPLATSTR("Error adding url to url group")));
+        }
+    }
+
+    // Set timeouts.
+    HTTP_TIMEOUT_LIMIT_INFO timeouts;
+    const USHORT secs = static_cast<USHORT>(pListener->configuration().timeout().count());
+    timeouts.EntityBody = secs;
+    timeouts.DrainEntityBody = secs;
+    timeouts.RequestQueue = secs;
+    timeouts.IdleConnection = secs;
+    timeouts.HeaderWait = secs;
+    timeouts.Flags.Present = 1;
+    errorCode =
+        HttpSetUrlGroupProperty(urlGroupId, HttpServerTimeoutsProperty, &timeouts, sizeof(HTTP_TIMEOUT_LIMIT_INFO));
+    if (errorCode)
+    {
+        HttpCloseUrlGroup(urlGroupId);
+        return pplx::task_from_exception<void>(http_exception(errorCode));
+    }
+
+    // Add listener registration.
+    {
+        pplx::extensibility::scoped_rw_lock_t lock(_M_listenersLock);
+        if (_M_registeredListeners.find(pListener) != _M_registeredListeners.end())
+        {
+            HttpCloseUrlGroup(urlGroupId);
+            throw std::invalid_argument("Error: http_listener is already registered");
+        }
+        _M_registeredListeners[pListener] =
+            std::unique_ptr<listener_registration>(new listener_registration(urlGroupId));
+    }
+
+    // Associate Url group with request queue.
+    HTTP_BINDING_INFO bindingInfo;
+    bindingInfo.RequestQueueHandle = m_hRequestQueue;
+    bindingInfo.Flags.Present = 1;
+    errorCode = HttpSetUrlGroupProperty(urlGroupId, HttpServerBindingProperty, &bindingInfo, sizeof(HTTP_BINDING_INFO));
+    if (errorCode)
+    {
+        HttpCloseUrlGroup(urlGroupId);
+        return pplx::task_from_exception<void>(http_exception(errorCode));
+    }
+
+    return pplx::task_from_result();
+}
+
+pplx::task<void> http_windows_server::unregister_listener(
+    _In_ web::http::experimental::listener::details::http_listener_impl* pListener)
+{
+    return pplx::create_task([=]() {
+        // First remove listener registration.
+        std::unique_ptr<listener_registration> registration;
+        {
+            pplx::extensibility::scoped_rw_lock_t lock(_M_listenersLock);
+            registration = std::move(_M_registeredListeners[pListener]);
+            _M_registeredListeners[pListener] = nullptr;
+            _M_registeredListeners.erase(pListener);
+        }
+
+        // Then take the listener write lock to make sure there are no calls into the listener's
+        // request handler.
+        {
+            pplx::extensibility::scoped_rw_lock_t lock(registration->m_requestHandlerLock);
+        }
+
+        // Next close Url group, no need to remove individual Urls.
+        const unsigned long error_code = HttpCloseUrlGroup(registration->m_urlGroupId);
+        if (error_code != NO_ERROR)
+        {
+            throw http_exception(error_code);
+        }
+    });
+}
+
+pplx::task<void> http_windows_server::start()
+{
+    // Initialize data.
+    m_serverSessionId = 0;
+    m_hRequestQueue = nullptr;
+    m_threadpool_io = nullptr;
+    m_numOutstandingRequests = 0;
+    m_zeroOutstandingRequests.set();
+
+    // Open server session.
+    HTTPAPI_VERSION httpApiVersion = HTTPAPI_VERSION_2;
+    ULONG errorCode = HttpCreateServerSession(httpApiVersion, &m_serverSessionId, 0);
+    if (errorCode)
+    {
+        return pplx::task_from_exception<void>(http_exception(errorCode));
+    }
+
+    // Create request queue.
+    errorCode = HttpCreateRequestQueue(httpApiVersion, NULL, NULL, NULL, &m_hRequestQueue);
+    if (errorCode)
+    {
+        return pplx::task_from_exception<void>(http_exception(errorCode));
+    }
+
+    // Create and start ThreadPool I/O so we can process asynchronous I/O.
+    m_threadpool_io = CreateThreadpoolIo(m_hRequestQueue, &http_overlapped::io_completion_callback, NULL, NULL);
+    if (m_threadpool_io == nullptr)
+    {
+        return pplx::task_from_exception<void>(http_exception(errorCode));
+    }
+
+    // Start request receiving task.
+    m_receivingTask = pplx::create_task([this]() { receive_requests(); });
+
+    return pplx::task_from_result();
+}
+
+pplx::task<void> http_windows_server::stop()
+{
+    // Shutdown request queue.
+    if (m_hRequestQueue != nullptr)
+    {
+        HttpShutdownRequestQueue(m_hRequestQueue);
+        m_receivingTask.wait();
+
+        // Wait for all requests to be finished processing.
+        m_zeroOutstandingRequests.wait();
+
+        HttpCloseRequestQueue(m_hRequestQueue);
+    }
+
+    // Release resources.
+    if (m_serverSessionId != 0)
+    {
+        HttpCloseServerSession(m_serverSessionId);
+    }
+    if (m_threadpool_io != nullptr)
+    {
+        CloseThreadpoolIo(m_threadpool_io);
+        m_threadpool_io = nullptr;
+    }
+
+    return pplx::task_from_result();
+}
+
+void http_windows_server::receive_requests()
+{
+    HTTP_REQUEST p_request;
+    ULONG bytes_received;
+
+    // Oversubscribe since this is a blocking call and we don't want to count
+    // towards the concurrency runtime's thread count. A more proper fix
+    // would be to use Overlapped I/O and asynchronously call HttpReceiveHttpRequest.
+    // This requires additional work to be careful synchronizing with the listener
+    // shutdown. This is much easier especially given the http_listener is 'experimental'
+    // and with VS2015 PPL tasks run on the threadpool.
+#if _MSC_VER < 1900
+    concurrency::Context::Oversubscribe(true);
+#endif
+    for (;;)
+    {
+        unsigned long error_code = HttpReceiveHttpRequest(
+            m_hRequestQueue, HTTP_NULL_ID, 0, &p_request, sizeof(HTTP_REQUEST), &bytes_received, 0);
+
+        if (error_code != NO_ERROR && error_code != ERROR_MORE_DATA)
+        {
+            break;
+        }
+
+        // Start processing the request
+        auto pContext = new windows_request_context();
+        auto pRequestContext = std::unique_ptr<_http_server_context>(pContext);
+        http_request msg = http_request::_create_request(std::move(pRequestContext));
+        pContext->async_process_request(p_request.RequestId, msg, bytes_received);
+    }
+#if _MSC_VER < 1900
+    concurrency::Context::Oversubscribe(false);
+#endif
+}
+
+pplx::task<void> http_windows_server::respond(http::http_response response)
+{
+    windows_request_context* p_context = static_cast<windows_request_context*>(response._get_server_context());
+    return pplx::create_task(p_context->m_response_completed);
+}
+
+windows_request_context::windows_request_context()
+    : m_sending_in_chunks(false), m_transfer_encoding(false), m_remaining_to_write(0)
+{
+    auto* pServer = static_cast<http_windows_server*>(http_server_api::server_api());
+    if (++pServer->m_numOutstandingRequests == 1)
+    {
+        pServer->m_zeroOutstandingRequests.reset();
+    }
+}
+
+windows_request_context::~windows_request_context()
+{
+    // Unfortunately have to work around a ppl task_completion_event bug that can cause AVs.
+    // Bug is that task_completion_event accesses internal state after setting.
+    // Workaround is to use a lock incurring additional synchronization, if can acquire
+    // the lock then setting of the event has completed.
+    std::lock_guard<std::mutex> lock(m_responseCompletedLock);
+
+    // Add a task-based continuation so no exceptions thrown from the task go 'unobserved'.
+    pplx::create_task(m_response_completed).then([](pplx::task<void> t) {
+        try
+        {
+            t.wait();
+        }
+        catch (...)
+        {
+        }
+    });
+
+    auto* pServer = static_cast<http_windows_server*>(http_server_api::server_api());
+    if (--pServer->m_numOutstandingRequests == 0)
+    {
+        pServer->m_zeroOutstandingRequests.set();
+    }
+}
+
+void windows_request_context::async_process_request(HTTP_REQUEST_ID request_id,
+                                                    http_request msg,
+                                                    const unsigned long headers_size)
+{
+    auto* pServer = static_cast<http_windows_server*>(http_server_api::server_api());
+    m_request_id = request_id;
+
+    // Save the http_request as the member of windows_request_context for the callback use.
+    m_msg = msg;
+
+    m_request_buffer =
+        std::unique_ptr<unsigned char[]>(new unsigned char[msl::safeint3::SafeInt<unsigned long>(headers_size)]);
+    m_request = (HTTP_REQUEST*)m_request_buffer.get();
+
+    // The read_headers_io_completion callback function.
+    m_overlapped.set_http_io_completion(
+        [this](DWORD error, DWORD nBytes) { read_headers_io_completion(error, nBytes); });
+
+    StartThreadpoolIo(pServer->m_threadpool_io);
+
+    const unsigned long error_code =
+        HttpReceiveHttpRequest(pServer->m_hRequestQueue, m_request_id, 0, m_request, headers_size, NULL, &m_overlapped);
+
+    if (error_code != NO_ERROR && error_code != ERROR_IO_PENDING)
+    {
+        CancelThreadpoolIo(pServer->m_threadpool_io);
+        m_msg.reply(status_codes::InternalError);
+        init_response_callbacks(ShouldWaitForBody::DontWait);
+    }
+}
+
+/// <summary>
+///  The read request headers completion callback function.
+/// </summary>
+void windows_request_context::read_headers_io_completion(DWORD error_code, DWORD)
+{
+    if (error_code != NO_ERROR)
+    {
+        m_msg.reply(status_codes::InternalError);
+        init_response_callbacks(ShouldWaitForBody::DontWait);
+    }
+    else
+    {
+        utility::string_t header;
+        std::string badRequestMsg;
+        try
+        {
+            // HTTP_REQUEST::pRawUrl contains the raw URI that came across the wire.
+            // Use this instead since the CookedUrl is a mess of the URI components
+            // some encoded and some not.
+            m_msg.set_request_uri(utf8_to_utf16(m_request->pRawUrl));
+        }
+        catch (const uri_exception& e)
+        {
+            // If an exception occurred, finish processing the request below but
+            // respond with BadRequest instead of dispatching to the user's
+            // request handlers.
+            badRequestMsg = e.what();
+        }
+        m_msg.set_method(parse_request_method(m_request));
+        parse_http_headers(m_request->Headers, m_msg.headers());
+
+        // See if we need to compress or decompress the incoming request body, and if so, prepare for it
+        try
+        {
+            if (m_msg.headers().match(header_names::transfer_encoding, header))
+            {
+                try
+                {
+                    m_decompressor = http::compression::details::get_decompressor_from_header(
+                        header, http::compression::details::header_types::transfer_encoding);
+                }
+                catch (http_exception& e)
+                {
+                    if (e.error_code().value() != status_codes::NotImplemented)
+                    {
+                        // Something is wrong with the header; we'll fail here
+                        throw;
+                    }
+                    // We could not find a decompressor; we'll see if the user's handler adds one later
+                    m_decompress_header_type = http::compression::details::header_types::transfer_encoding;
+                    m_decompress_header = std::move(header);
+                }
+            }
+            else if (m_msg.headers().match(header_names::content_encoding, header))
+            {
+                try
+                {
+                    m_decompressor = http::compression::details::get_decompressor_from_header(
+                        header, http::compression::details::header_types::content_encoding);
+                }
+                catch (http_exception& e)
+                {
+                    if (e.error_code().value() != status_codes::UnsupportedMediaType)
+                    {
+                        // Something is wrong with the header; we'll fail here
+                        throw;
+                    }
+                    // We could not find a decompressor; we'll see if the user's handler adds one later
+                    m_decompress_header_type = http::compression::details::header_types::content_encoding;
+                    m_decompress_header = std::move(header);
+                }
+            }
+            else if (m_msg.headers().match(header_names::te, header))
+            {
+                // Note that init_response_headers throws away m_msg, so we need to set our compressor here.  If
+                // the header contains all unsupported algorithms, it's not an error -- we just won't compress
+                m_compressor = http::compression::details::get_compressor_from_header(
+                    header, http::compression::details::header_types::te);
+            }
+            else if (m_msg.headers().match(header_names::accept_encoding, header))
+            {
+                // This would require pre-compression of the input stream, since we MUST send Content-Length, so we'll
+                // (legally) ignore it
+                // m_compressor = http::compression::details::get_compressor_from_header(header,
+                // http::compression::details::header_types:accept_encoding);
+            }
+        }
+        catch (http_exception& e)
+        {
+            if (badRequestMsg.empty())
+            {
+                // Respond with a reasonable message
+                badRequestMsg = e.what();
+            }
+        }
+
+        m_msg._get_impl()->_set_http_version(
+            {(uint8_t)m_request->Version.MajorVersion, (uint8_t)m_request->Version.MinorVersion});
+
+        // Retrieve the remote IP address
+        std::vector<wchar_t> remoteAddressBuffer(50);
+
+        if (m_request->Address.pRemoteAddress->sa_family == AF_INET6)
+        {
+            auto inAddr = &reinterpret_cast<SOCKADDR_IN6*>(m_request->Address.pRemoteAddress)->sin6_addr;
+            InetNtopW(AF_INET6, inAddr, &remoteAddressBuffer[0], remoteAddressBuffer.size());
+        }
+        else if (m_request->Address.pRemoteAddress->sa_family == AF_INET)
+        {
+            auto inAddr = &reinterpret_cast<SOCKADDR_IN*>(m_request->Address.pRemoteAddress)->sin_addr;
+            InetNtopW(AF_INET, inAddr, &remoteAddressBuffer[0], remoteAddressBuffer.size());
+        }
+        else
+        {
+            remoteAddressBuffer[0] = L'\0';
+        }
+
+        m_msg._get_impl()->_set_remote_address(&remoteAddressBuffer[0]);
+
+        // Start reading in body from the network.
+        m_msg._get_impl()->_prepare_to_receive_data();
+        read_request_body_chunk();
+
+        // Dispatch request to the http_listener.
+        if (badRequestMsg.empty())
+        {
+            dispatch_request_to_listener(
+                (web::http::experimental::listener::details::http_listener_impl*)m_request->UrlContext);
+        }
+        else
+        {
+            m_msg.reply(status_codes::BadRequest, badRequestMsg);
+
+            // Even though we have a bad request, we should wait for the body otherwise we risk racing over m_overlapped
+            init_response_callbacks(ShouldWaitForBody::Wait);
+        }
+    }
+}
+
+void windows_request_context::read_request_body_chunk()
+{
+    auto* pServer = static_cast<http_windows_server*>(http_server_api::server_api());
+    PVOID body;
+
+    // The read_body_io_completion callback function
+    m_overlapped.set_http_io_completion([this](DWORD error, DWORD nBytes) { read_body_io_completion(error, nBytes); });
+
+    auto request_body_buf = m_msg._get_impl()->outstream().streambuf();
+    if (!m_decompressor)
+    {
+        body = request_body_buf.alloc(CHUNK_SIZE);
+    }
+    else
+    {
+        if (m_compress_buffer.size() < CHUNK_SIZE)
+        {
+            m_compress_buffer.resize(CHUNK_SIZE);
+        }
+        body = m_compress_buffer.data();
+    }
+
+    // Once we allow users to set the output stream the following assert could fail.
+    // At that time we would need compensation code that would allocate a buffer from the heap instead.
+    _ASSERTE(body != nullptr);
+
+    StartThreadpoolIo(pServer->m_threadpool_io);
+    const ULONG error_code = HttpReceiveRequestEntityBody(pServer->m_hRequestQueue,
+                                                          m_request_id,
+                                                          HTTP_RECEIVE_REQUEST_ENTITY_BODY_FLAG_FILL_BUFFER,
+                                                          (PVOID)body,
+                                                          CHUNK_SIZE,
+                                                          NULL,
+                                                          &m_overlapped);
+
+    if (error_code != ERROR_IO_PENDING && error_code != NO_ERROR)
+    {
+        // There was no more data to read.
+        CancelThreadpoolIo(pServer->m_threadpool_io);
+        if (!m_decompressor)
+        {
+            request_body_buf.commit(0);
+        }
+        if (error_code == ERROR_HANDLE_EOF)
+        {
+            m_msg._get_impl()->_complete(request_body_buf.in_avail());
+        }
+        else
+        {
+            m_msg._get_impl()->_complete(0, std::make_exception_ptr(http_exception(error_code)));
+        }
+    }
+}
+
+/// <summary>
+///  The read request body completion callback function.
+/// </summary>
+void windows_request_context::read_body_io_completion(DWORD error_code, DWORD bytes_read)
+{
+    auto request_body_buf = m_msg._get_impl()->outstream().streambuf();
+
+    if (error_code == NO_ERROR)
+    {
+        if (!m_decompressor)
+        {
+            request_body_buf.commit(bytes_read);
+        }
+        else
+        {
+            size_t got;
+            size_t used;
+            size_t total_used = 0;
+
+            do
+            {
+                auto body = request_body_buf.alloc(CHUNK_SIZE);
+                try
+                {
+                    bool done_unused;
+                    got = m_decompressor->decompress(m_compress_buffer.data() + total_used,
+                                                     bytes_read - total_used,
+                                                     body,
+                                                     CHUNK_SIZE,
+                                                     http::compression::operation_hint::has_more,
+                                                     used,
+                                                     done_unused);
+                }
+                catch (...)
+                {
+                    request_body_buf.commit(0);
+                    m_msg._get_impl()->_complete(0, std::current_exception());
+                    return;
+                }
+                request_body_buf.commit(got);
+                total_used += used;
+            } while (total_used != bytes_read);
+        }
+        read_request_body_chunk();
+    }
+    else if (error_code == ERROR_HANDLE_EOF)
+    {
+        if (!m_decompressor)
+        {
+            request_body_buf.commit(0);
+        }
+        m_msg._get_impl()->_complete(request_body_buf.in_avail());
+    }
+    else
+    {
+        if (!m_decompressor)
+        {
+            request_body_buf.commit(0);
+        }
+        m_msg._get_impl()->_complete(0, std::make_exception_ptr(http_exception(error_code)));
+    }
+}
+
+void windows_request_context::dispatch_request_to_listener(
+    _In_ web::http::experimental::listener::details::http_listener_impl* pListener)
+{
+    m_msg._set_listener_path(pListener->uri().path());
+
+    // Save http_request copy to dispatch to user's handler in case content_ready() completes before.
+    http_request request = m_msg;
+
+    init_response_callbacks(ShouldWaitForBody::Wait);
+
+    // Look up the lock for the http_listener.
+    auto* pServer = static_cast<http_windows_server*>(http_server_api::server_api());
+    pplx::extensibility::reader_writer_lock_t* pListenerLock;
+    {
+        pplx::extensibility::scoped_read_lock_t lock(pServer->_M_listenersLock);
+
+        // It is possible the listener could have unregistered.
+        if (pServer->_M_registeredListeners.find(pListener) == pServer->_M_registeredListeners.end())
+        {
+            request.reply(status_codes::NotFound);
+            return;
+        }
+        pListenerLock = &pServer->_M_registeredListeners[pListener]->m_requestHandlerLock;
+
+        // We need to acquire the listener's lock before releasing the registered listeners lock.
+        // But we don't need to hold the registered listeners lock when calling into the user's code.
+        pListenerLock->lock_read();
+    }
+
+    try
+    {
+        pListener->handle_request(request);
+        pListenerLock->unlock();
+    }
+    catch (...)
+    {
+        pListenerLock->unlock();
+        request._reply_if_not_already(status_codes::InternalError);
+    }
+}
+
+void windows_request_context::init_response_callbacks(ShouldWaitForBody shouldWait)
+{
+    // Use a proxy event so we're not causing a circular reference between the http_request and the response task
+    pplx::task_completion_event<void> proxy_content_ready;
+
+    auto content_ready_task = m_msg.content_ready();
+    auto get_response_task = m_msg.get_response();
+
+    content_ready_task.then([this, proxy_content_ready](pplx::task<http_request> requestBody) {
+        // If an exception occurred while processing the body then there is no reason
+        // to even try sending the response, just re-surface the same exception.
+        try
+        {
+            requestBody.wait();
+        }
+        catch (...)
+        {
+            // Copy the request reference in case it's the last
+            http_request request = m_msg;
+            m_msg = http_request();
+            auto exc = std::current_exception();
+            proxy_content_ready.set_exception(exc);
+            cancel_request(exc);
+            return;
+        }
+
+        // At this point the user entirely controls the lifetime of the http_request.
+        m_msg = http_request();
+        proxy_content_ready.set();
+    });
+
+    get_response_task.then([this, proxy_content_ready](pplx::task<http::http_response> responseTask) {
+        // Don't let an exception from sending the response bring down the server.
+        try
+        {
+            m_response = responseTask.get();
+        }
+        catch (const pplx::task_canceled&)
+        {
+            // This means the user didn't respond to the request, allowing the
+            // http_request instance to be destroyed. There is nothing to do then
+            // so don't send a response.
+            // Avoid unobserved exception handler
+            pplx::create_task(proxy_content_ready).then([](pplx::task<void> t) {
+                try
+                {
+                    t.wait();
+                }
+                catch (...)
+                {
+                }
+            });
+            return;
+        }
+        catch (...)
+        {
+            // Should never get here, if we do there's a chance that a circular reference will cause leaks,
+            // or worse, undefined behaviour as we don't know who owns 'this' anymore
+            _ASSERTE(false);
+            m_response = http::http_response(status_codes::InternalError);
+        }
+
+        pplx::create_task(m_response_completed).then([this](pplx::task<void> t) {
+            // After response is sent, break circular reference between http_response and the request context.
+            // Otherwise http_listener::close() can hang.
+            m_response._get_impl()->_set_server_context(nullptr);
+        });
+
+        // Wait until the content download finished before replying because m_overlapped is reused,
+        // and we don't want to delete 'this' if the body is still downloading
+        pplx::create_task(proxy_content_ready)
+            .then([this](pplx::task<void> t) {
+                try
+                {
+                    t.wait();
+                    async_process_response();
+                }
+                catch (...)
+                {
+                }
+            })
+            .wait();
+    });
+
+    if (shouldWait == ShouldWaitForBody::DontWait)
+    {
+        // Fake a body completion so the content_ready() task doesn't keep the http_request alive forever
+        m_msg._get_impl()->_complete(0);
+    }
+}
+
+void windows_request_context::async_process_response()
+{
+    auto* pServer = static_cast<http_windows_server*>(http_server_api::server_api());
+
+    HTTP_RESPONSE win_api_response;
+    ZeroMemory(&win_api_response, sizeof(win_api_response));
+    win_api_response.StatusCode = m_response.status_code();
+    const std::string reason = utf16_to_utf8(m_response.reason_phrase());
+    win_api_response.pReason = reason.c_str();
+    win_api_response.ReasonLength = (USHORT)reason.size();
+    size_t content_length;
+
+    if (m_compressor || m_response._get_impl()->compressor())
+    {
+        if (m_response.headers().has(header_names::content_length))
+        {
+            // Content-Length should not be sent with Transfer-Encoding
+            m_response.headers().remove(header_names::content_length);
+        }
+        if (!m_response._get_impl()->compressor())
+        {
+            // Temporarily move the compressor to the response, so _get_content_length() will honor it
+            m_response._get_impl()->set_compressor(std::move(m_compressor));
+        } // else one was already set from a callback, and we'll (blindly) use it
+        content_length = m_response._get_impl()->_get_content_length_and_set_compression();
+        m_compressor = std::move(m_response._get_impl()->compressor());
+        m_response._get_impl()->set_compressor(nullptr);
+    }
+    else
+    {
+        if (!m_decompress_header.empty())
+        {
+            auto factories = m_response._get_impl()->decompress_factories();
+            try
+            {
+                m_decompressor = http::compression::details::get_decompressor_from_header(
+                    m_decompress_header, m_decompress_header_type, factories);
+                m_decompress_header.clear();
+                if (!m_decompressor)
+                {
+                    http::status_code code = http::status_codes::NotImplemented;
+                    if (m_decompress_header_type == http::compression::details::header_types::content_encoding)
+                    {
+                        code = status_codes::UnsupportedMediaType;
+                    }
+                    throw http_exception(code);
+                }
+            }
+            catch (http_exception& e)
+            {
+                // No matching decompressor was supplied via callback
+                CancelThreadpoolIo(pServer->m_threadpool_io);
+                cancel_request(std::make_exception_ptr(e));
+                return;
+            }
+        }
+        content_length = m_response._get_impl()->_get_content_length();
+    }
+
+    m_headers = std::unique_ptr<HTTP_UNKNOWN_HEADER[]>(
+        new HTTP_UNKNOWN_HEADER[msl::safeint3::SafeInt<size_t>(m_response.headers().size())]);
+    m_headers_buffer.resize(msl::safeint3::SafeInt<size_t>(m_response.headers().size()) * 2);
+
+    win_api_response.Headers.UnknownHeaderCount = (USHORT)m_response.headers().size();
+    win_api_response.Headers.pUnknownHeaders = m_headers.get();
+    int headerIndex = 0;
+    for (auto iter = m_response.headers().begin(); iter != m_response.headers().end(); ++iter, ++headerIndex)
+    {
+        m_headers_buffer[headerIndex * 2] = utf16_to_utf8(iter->first);
+        m_headers_buffer[headerIndex * 2 + 1] = utf16_to_utf8(iter->second);
+        win_api_response.Headers.pUnknownHeaders[headerIndex].NameLength =
+            (USHORT)m_headers_buffer[headerIndex * 2].size();
+        win_api_response.Headers.pUnknownHeaders[headerIndex].pName = m_headers_buffer[headerIndex * 2].c_str();
+        win_api_response.Headers.pUnknownHeaders[headerIndex].RawValueLength =
+            (USHORT)m_headers_buffer[headerIndex * 2 + 1].size();
+        win_api_response.Headers.pUnknownHeaders[headerIndex].pRawValue = m_headers_buffer[headerIndex * 2 + 1].c_str();
+    }
+
+    // Send response callback function
+    m_overlapped.set_http_io_completion(
+        [this](DWORD error, DWORD nBytes) { send_response_io_completion(error, nBytes); });
+
+    // Figure out how to send the entity body of the message.
+    if (content_length == 0)
+    {
+        // There's no data. This is easy!
+        StartThreadpoolIo(pServer->m_threadpool_io);
+        const unsigned long error_code = HttpSendHttpResponse(pServer->m_hRequestQueue,
+                                                              m_request_id,
+                                                              NULL,
+                                                              &win_api_response,
+                                                              NULL,
+                                                              NULL,
+                                                              NULL,
+                                                              NULL,
+                                                              &m_overlapped,
+                                                              NULL);
+
+        if (error_code != NO_ERROR && error_code != ERROR_IO_PENDING)
+        {
+            CancelThreadpoolIo(pServer->m_threadpool_io);
+            cancel_request(std::make_exception_ptr(http_exception(error_code)));
+        }
+
+        return;
+    }
+
+    // OK, so we need to chunk it up.
+    _ASSERTE(content_length > 0);
+    m_sending_in_chunks = (content_length != (std::numeric_limits<size_t>::max)());
+    m_transfer_encoding = (content_length == (std::numeric_limits<size_t>::max)());
+    m_remaining_to_write = content_length;
+    if (content_length == (std::numeric_limits<size_t>::max)())
+    {
+        // Attempt to figure out the remaining length of the input stream
+        m_remaining_to_write = m_response._get_impl()->_get_stream_length();
+    }
+
+    StartThreadpoolIo(pServer->m_threadpool_io);
+    const unsigned long error_code = HttpSendHttpResponse(pServer->m_hRequestQueue,
+                                                          m_request_id,
+                                                          HTTP_SEND_RESPONSE_FLAG_MORE_DATA,
+                                                          &win_api_response,
+                                                          NULL,
+                                                          NULL,
+                                                          NULL,
+                                                          NULL,
+                                                          &m_overlapped,
+                                                          NULL);
+
+    if (error_code != NO_ERROR && error_code != ERROR_IO_PENDING)
+    {
+        CancelThreadpoolIo(pServer->m_threadpool_io);
+        cancel_request(std::make_exception_ptr(http_exception(error_code)));
+    }
+}
+
+/// <summary>
+///  The send response headers completion callback function.
+/// </summary>
+void windows_request_context::send_response_io_completion(DWORD error_code, DWORD)
+{
+    if (error_code != NO_ERROR)
+    {
+        cancel_request(std::make_exception_ptr(http_exception(error_code)));
+    }
+    else
+    {
+        transmit_body();
+    }
+}
+
+// Transmit the response body to the network
+void windows_request_context::transmit_body()
+{
+    if (!m_sending_in_chunks && !m_transfer_encoding)
+    {
+        // We are done sending data.
+        std::lock_guard<std::mutex> lock(m_responseCompletedLock);
+        m_response_completed.set();
+        return;
+    }
+
+    msl::safeint3::SafeInt<size_t> safeCount = m_remaining_to_write;
+    size_t next_chunk_size = safeCount.Min(CHUNK_SIZE);
+
+    // In both cases here we could perform optimizations to try and use acquire on the streams to avoid an extra copy.
+    if (m_sending_in_chunks)
+    {
+        m_body_data.resize(CHUNK_SIZE);
+
+        streams::rawptr_buffer<unsigned char> buf(&m_body_data[0], next_chunk_size);
+
+        m_response.body().read(buf, next_chunk_size).then([this](pplx::task<size_t> op) {
+            size_t bytes_read = 0;
+
+            // If an exception occurs surface the error to user on the server side
+            // and cancel the request so the client sees the error.
+            try
+            {
+                bytes_read = op.get();
+            }
+            catch (...)
+            {
+                cancel_request(std::current_exception());
+                return;
+            }
+            if (bytes_read == 0)
+            {
+                cancel_request(std::make_exception_ptr(
+                    http_exception(_XPLATSTR("Error unexpectedly encountered the end of the response stream early"))));
+                return;
+            }
+
+            // Check whether this is the last one to send...
+            m_remaining_to_write = m_remaining_to_write - bytes_read;
+            m_sending_in_chunks = (m_remaining_to_write > 0);
+
+            send_entity_body(&m_body_data[0], bytes_read);
+        });
+    }
+    else
+    {
+        // We're transfer-encoding...
+        if (m_compressor)
+        {
+            // ...and compressing.  For simplicity, we allocate a buffer that's "too large to fail" while compressing.
+            const size_t body_data_length = 2 * CHUNK_SIZE + http::details::chunked_encoding::additional_encoding_space;
+            m_body_data.resize(body_data_length);
+
+            // We'll read into a temporary buffer before compressing
+            if (m_compress_buffer.capacity() < next_chunk_size)
+            {
+                m_compress_buffer.reserve(next_chunk_size);
+            }
+
+            streams::rawptr_buffer<unsigned char> buf(m_compress_buffer.data(), next_chunk_size);
+
+            m_response.body().read(buf, next_chunk_size).then([this, body_data_length](pplx::task<size_t> op) {
+                size_t bytes_read = 0;
+
+                // If an exception occurs surface the error to user on the server side
+                // and cancel the request so the client sees the error.
+                try
+                {
+                    bytes_read = op.get();
+                }
+                catch (...)
+                {
+                    cancel_request(std::current_exception());
+                    return;
+                }
+                _ASSERTE(bytes_read >= 0);
+
+                // Compress this chunk; if we read no data, allow the compressor to finalize its stream
+                http::compression::operation_hint hint = http::compression::operation_hint::has_more;
+                if (!bytes_read)
+                {
+                    hint = http::compression::operation_hint::is_last;
+                }
+                m_compressor
+                    ->compress(m_compress_buffer.data(),
+                               bytes_read,
+                               &m_body_data[http::details::chunked_encoding::data_offset],
+                               body_data_length,
+                               hint)
+                    .then([this, bytes_read, body_data_length](pplx::task<http::compression::operation_result> op) {
+                        http::compression::operation_result r;
+
+                        try
+                        {
+                            r = op.get();
+                        }
+                        catch (...)
+                        {
+                            cancel_request(std::current_exception());
+                            return;
+                        }
+
+                        if (r.input_bytes_processed != bytes_read ||
+                            r.output_bytes_produced ==
+                                body_data_length - http::details::chunked_encoding::additional_encoding_space ||
+                            r.done != !bytes_read)
+                        {
+                            // We chose our parameters so that compression should
+                            // never overflow body_data_length; fail if it does
+                            cancel_request(std::make_exception_ptr(
+                                std::exception("Compressed data exceeds internal buffer size.")));
+                            return;
+                        }
+
+                        // Check whether this is the last one to send; note that this is a
+                        // few lines of near-duplicate code with the non-compression path
+                        _ASSERTE(bytes_read <= m_remaining_to_write);
+                        m_remaining_to_write -= bytes_read;
+                        m_transfer_encoding = (r.output_bytes_produced > 0);
+                        size_t offset = http::details::chunked_encoding::add_chunked_delimiters(
+                            &m_body_data[0], body_data_length, r.output_bytes_produced);
+                        send_entity_body(&m_body_data[offset],
+                                         r.output_bytes_produced +
+                                             http::details::chunked_encoding::additional_encoding_space - offset);
+                    });
+            });
+        }
+        else
+        {
+            const size_t body_data_length = CHUNK_SIZE + http::details::chunked_encoding::additional_encoding_space;
+            m_body_data.resize(body_data_length);
+
+            streams::rawptr_buffer<unsigned char> buf(&m_body_data[http::details::chunked_encoding::data_offset],
+                                                      body_data_length);
+
+            m_response.body().read(buf, next_chunk_size).then([this, body_data_length](pplx::task<size_t> op) {
+                size_t bytes_read = 0;
+
+                // If an exception occurs surface the error to user on the server side
+                // and cancel the request so the client sees the error.
+                try
+                {
+                    bytes_read = op.get();
+                }
+                catch (...)
+                {
+                    cancel_request(std::current_exception());
+                    return;
+                }
+
+                // Check whether this is the last one to send...
+                m_transfer_encoding = (bytes_read > 0);
+                size_t offset = http::details::chunked_encoding::add_chunked_delimiters(
+                    &m_body_data[0], body_data_length, bytes_read);
+
+                auto data_length = bytes_read + (http::details::chunked_encoding::additional_encoding_space - offset);
+                send_entity_body(&m_body_data[offset], data_length);
+            });
+        }
+    }
+}
+
+// Send the body through HTTP.sys
+void windows_request_context::send_entity_body(_In_reads_(data_length) unsigned char* data, _In_ size_t data_length)
+{
+    HTTP_DATA_CHUNK dataChunk;
+    memset(&dataChunk, 0, sizeof(dataChunk));
+    dataChunk.DataChunkType = HttpDataChunkFromMemory;
+    dataChunk.FromMemory.pBuffer = data;
+    dataChunk.FromMemory.BufferLength = (ULONG)data_length;
+    const bool this_is_the_last_chunk = !m_transfer_encoding && !m_sending_in_chunks;
+
+    // Send response.
+    auto* pServer = static_cast<http_windows_server*>(http_server_api::server_api());
+
+    // Send response body callback function
+    m_overlapped.set_http_io_completion(
+        [this](DWORD error, DWORD nBytes) { send_response_body_io_completion(error, nBytes); });
+
+    StartThreadpoolIo(pServer->m_threadpool_io);
+    auto error_code = HttpSendResponseEntityBody(pServer->m_hRequestQueue,
+                                                 m_request_id,
+                                                 this_is_the_last_chunk ? NULL : HTTP_SEND_RESPONSE_FLAG_MORE_DATA,
+                                                 1,
+                                                 &dataChunk,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL,
+                                                 &m_overlapped,
+                                                 NULL);
+
+    if (error_code != NO_ERROR && error_code != ERROR_IO_PENDING)
+    {
+        CancelThreadpoolIo(pServer->m_threadpool_io);
+        cancel_request(std::make_exception_ptr(http_exception(error_code)));
+    }
+}
+
+/// <summary>
+///  The send response body completion callback function.
+/// </summary>
+void windows_request_context::send_response_body_io_completion(DWORD error_code, DWORD)
+{
+    if (error_code != NO_ERROR)
+    {
+        cancel_request(std::make_exception_ptr(http_exception(error_code)));
+        return;
+    }
+    transmit_body();
+}
+
+/// <summary>
+///  The cancel request completion callback function.
+/// </summary>
+void windows_request_context::cancel_request_io_completion(DWORD, DWORD)
+{
+    std::lock_guard<std::mutex> lock(m_responseCompletedLock);
+    m_response_completed.set_exception(m_except_ptr);
+}
+
+void windows_request_context::cancel_request(std::exception_ptr except_ptr)
+{
+    auto* pServer = static_cast<http_windows_server*>(http_server_api::server_api());
+
+    m_except_ptr = except_ptr;
+
+    // Cancel request callback function.
+    m_overlapped.set_http_io_completion(
+        [this](DWORD error, DWORD nBytes) { cancel_request_io_completion(error, nBytes); });
+
+    StartThreadpoolIo(pServer->m_threadpool_io);
+
+    auto error_code = HttpCancelHttpRequest(pServer->m_hRequestQueue, m_request_id, &m_overlapped);
+
+    if (error_code != NO_ERROR && error_code != ERROR_IO_PENDING)
+    {
+        CancelThreadpoolIo(pServer->m_threadpool_io);
+        std::lock_guard<std::mutex> lock(m_responseCompletedLock);
+        m_response_completed.set_exception(except_ptr);
+    }
+}
+
+std::unique_ptr<http_server> make_http_httpsys_server() { return std::make_unique<http_windows_server>(); }
+
+} // namespace details
+} // namespace experimental
+} // namespace http
+} // namespace web
+
+#endif
diff --git a/Release/src/http/listener/http_server_httpsys.h b/Release/src/http/listener/http_server_httpsys.h
new file mode 100644 (file)
index 0000000..40c2a5e
--- /dev/null
@@ -0,0 +1,253 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: implementation of HTTP server API built on Windows HTTP Server APIs.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+#error "Error: http server APIs are not supported in XP"
+#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA
+
+// Windows Sockets are not code analysis clean.
+#pragma warning(push)
+#pragma warning(disable : 6386)
+#include <http.h>
+#pragma warning(pop)
+
+#include "cpprest/details/http_server.h"
+#include <atomic>
+#include <mutex>
+
+namespace web
+{
+namespace http
+{
+namespace experimental
+{
+namespace details
+{
+class http_windows_server;
+struct windows_request_context;
+
+/// <summary>
+/// Class used to wrap OVERLAPPED I/O with any HTTP I/O.
+/// </summary>
+class http_overlapped : public OVERLAPPED
+{
+public:
+    void set_http_io_completion(std::function<void(DWORD, DWORD)> http_io_completion)
+    {
+        ZeroMemory(this, sizeof(OVERLAPPED));
+        m_http_io_completion = http_io_completion;
+    }
+
+    /// <summary>
+    /// Callback for all I/O completions.
+    /// </summary>
+    static void CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance,
+                                                PVOID context,
+                                                PVOID pOverlapped,
+                                                ULONG result,
+                                                ULONG_PTR numberOfBytesTransferred,
+                                                PTP_IO io)
+    {
+        (void)io;
+        (void)context;
+        (void)instance;
+
+        http_overlapped* p_http_overlapped = (http_overlapped*)pOverlapped;
+        p_http_overlapped->m_http_io_completion(result, (DWORD)numberOfBytesTransferred);
+    }
+
+private:
+    std::function<void(DWORD, DWORD)> m_http_io_completion;
+};
+
+/// <summary>
+/// Context for http request through Windows HTTP Server API.
+/// </summary>
+struct windows_request_context : http::details::_http_server_context
+{
+    windows_request_context();
+    virtual ~windows_request_context();
+
+    // Asynchronously starts processing the current request.
+    void async_process_request(HTTP_REQUEST_ID request_id, http::http_request msg, const unsigned long headers_size);
+
+    // Dispatch request to the provided http_listener.
+    void dispatch_request_to_listener(_In_ web::http::experimental::listener::details::http_listener_impl* pListener);
+
+    enum class ShouldWaitForBody
+    {
+        Wait,
+        DontWait
+    };
+    // Initialise the response task callbacks. If the body has been requested, we should wait for it to avoid race
+    // conditions.
+    void init_response_callbacks(ShouldWaitForBody shouldWait);
+
+    // Read in a portion of the request body.
+    void read_request_body_chunk();
+
+    // Start processing the response.
+    void async_process_response();
+
+    void transmit_body();
+
+    // Read request headers io completion callback function .
+    void read_headers_io_completion(DWORD error_code, DWORD bytes_read);
+
+    // Read request body io completion callback function.
+    void read_body_io_completion(DWORD error_code, DWORD bytes_read);
+
+    // Send response io completion callback function .
+    void send_response_io_completion(DWORD error_code, DWORD bytes_read);
+
+    // Send response body io completion callback function.
+    void send_response_body_io_completion(DWORD error_code, DWORD bytes_read);
+
+    // Cancel request io completion callback function.
+    void cancel_request_io_completion(DWORD error_code, DWORD bytes_read);
+
+    // TCE that indicates the completion of response
+    // Workaround for ppl task_completion_event bug.
+    std::mutex m_responseCompletedLock;
+    pplx::task_completion_event<void> m_response_completed;
+
+    // Id of the currently processed request on this connection.
+    HTTP_REQUEST_ID m_request_id;
+
+    bool m_sending_in_chunks;
+    bool m_transfer_encoding;
+
+    size_t m_remaining_to_write;
+
+    HTTP_REQUEST* m_request;
+    std::unique_ptr<unsigned char[]> m_request_buffer;
+
+    std::unique_ptr<HTTP_UNKNOWN_HEADER[]> m_headers;
+    std::vector<std::string> m_headers_buffer;
+
+    http_overlapped m_overlapped;
+
+    http_request m_msg;
+    http_response m_response;
+
+    std::exception_ptr m_except_ptr;
+
+    std::vector<uint8_t> m_compress_buffer;
+    std::unique_ptr<web::http::compression::compress_provider> m_compressor;
+    std::unique_ptr<web::http::compression::decompress_provider> m_decompressor;
+    utility::string_t m_decompress_header;
+    http::compression::details::header_types m_decompress_header_type;
+
+private:
+    windows_request_context(const windows_request_context&);
+    windows_request_context& operator=(const windows_request_context&);
+
+    // Sends entity body chunk.
+    void send_entity_body(_In_reads_(data_length) unsigned char* data, _In_ size_t data_length);
+
+    // Cancels this request.
+    void cancel_request(std::exception_ptr except_ptr);
+
+    std::vector<unsigned char> m_body_data;
+};
+
+/// <summary>
+/// Class to implement HTTP server API on Windows.
+/// </summary>
+class http_windows_server : public http_server
+{
+public:
+    /// <summary>
+    /// Constructs a http_windows_server.
+    /// </summary>
+    http_windows_server();
+
+    /// <summary>
+    /// Releases resources held.
+    /// </summary>
+    ~http_windows_server();
+
+    /// <summary>
+    /// Start listening for incoming requests.
+    /// </summary>
+    virtual pplx::task<void> start();
+
+    /// <summary>
+    /// Registers an http listener.
+    /// </summary>
+    virtual pplx::task<void> register_listener(
+        _In_ web::http::experimental::listener::details::http_listener_impl* pListener);
+
+    /// <summary>
+    /// Unregisters an http listener.
+    /// </summary>
+    virtual pplx::task<void> unregister_listener(
+        _In_ web::http::experimental::listener::details::http_listener_impl* pListener);
+
+    /// <summary>
+    /// Stop processing and listening for incoming requests.
+    /// </summary>
+    virtual pplx::task<void> stop();
+
+    /// <summary>
+    /// Asynchronously sends the specified http response.
+    /// </summary>
+    /// <param name="response">The http_response to send.</param>
+    /// <returns>A operation which is completed once the response has been sent.</returns>
+    virtual pplx::task<void> respond(http::http_response response);
+
+private:
+    friend struct details::windows_request_context;
+
+    // Structure to hold each registered listener.
+    class listener_registration
+    {
+    public:
+        listener_registration(HTTP_URL_GROUP_ID urlGroupId) : m_urlGroupId(urlGroupId) {}
+
+        // URL group id for this listener. Each listener needs it own URL group
+        // because configuration like timeouts, authentication, etc...
+        HTTP_URL_GROUP_ID m_urlGroupId;
+
+        // Request handler lock to guard against removing a listener while in user code.
+        pplx::extensibility::reader_writer_lock_t m_requestHandlerLock;
+    };
+
+    // Registered listeners
+    pplx::extensibility::reader_writer_lock_t _M_listenersLock;
+    std::unordered_map<web::http::experimental::listener::details::http_listener_impl*,
+                       std::unique_ptr<listener_registration>>
+        _M_registeredListeners;
+
+    // HTTP Server API server session id.
+    HTTP_SERVER_SESSION_ID m_serverSessionId;
+
+    // Tracks the number of outstanding requests being processed.
+    std::atomic<int> m_numOutstandingRequests;
+    pplx::extensibility::event_t m_zeroOutstandingRequests;
+
+    // Handle to HTTP Server API request queue.
+    HANDLE m_hRequestQueue;
+
+    // Threadpool I/O structure for overlapped I/O.
+    TP_IO* m_threadpool_io;
+
+    // Task which actually handles receiving requests from HTTP Server API request queue.
+    pplx::task<void> m_receivingTask;
+    void receive_requests();
+};
+
+} // namespace details
+} // namespace experimental
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/listener/http_server_impl.h b/Release/src/http/listener/http_server_impl.h
new file mode 100644 (file)
index 0000000..7caa5a6
--- /dev/null
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "cpprest/details/http_server.h"
+#include <memory>
+
+namespace web
+{
+namespace http
+{
+namespace experimental
+{
+namespace details
+{
+std::unique_ptr<http_server> make_http_httpsys_server();
+std::unique_ptr<http_server> make_http_asio_server();
+
+} // namespace details
+} // namespace experimental
+} // namespace http
+} // namespace web
diff --git a/Release/src/http/oauth/oauth1.cpp b/Release/src/http/oauth/oauth1.cpp
new file mode 100644 (file)
index 0000000..b313cfc
--- /dev/null
@@ -0,0 +1,458 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Oauth 1.0
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "cpprest/asyncrt_utils.h"
+
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+
+using namespace utility;
+using web::http::client::http_client;
+using web::http::client::http_client_config;
+using web::http::oauth1::details::oauth1_state;
+using web::http::oauth1::details::oauth1_strings;
+
+namespace web
+{
+namespace http
+{
+namespace oauth1
+{
+namespace details
+{
+#define _OAUTH1_STRINGS
+#define DAT(a_, b_) const oauth1_string oauth1_strings::a_(_XPLATSTR(b_));
+#include "cpprest/details/http_constants.dat"
+#undef _OAUTH1_STRINGS
+#undef DAT
+
+} // namespace details
+
+namespace experimental
+{
+//
+// Start of platform-dependent _hmac_sha1() block...
+//
+#if defined(_WIN32) && !defined(__cplusplus_winrt) // Windows desktop
+
+#include <bcrypt.h>
+#include <winternl.h>
+
+// Code analysis complains even though there is no bug.
+#pragma warning(push)
+#pragma warning(disable : 6102)
+std::vector<unsigned char> oauth1_config::_hmac_sha1(const utility::string_t& key, const utility::string_t& data)
+{
+    NTSTATUS status;
+    BCRYPT_ALG_HANDLE alg_handle = nullptr;
+    BCRYPT_HASH_HANDLE hash_handle = nullptr;
+
+    std::vector<unsigned char> hash;
+    DWORD hash_len = 0;
+    ULONG result_len = 0;
+
+    const auto& key_c = conversions::utf16_to_utf8(key);
+    const auto& data_c = conversions::utf16_to_utf8(data);
+
+    status = BCryptOpenAlgorithmProvider(&alg_handle, BCRYPT_SHA1_ALGORITHM, nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG);
+    if (!NT_SUCCESS(status))
+    {
+        goto cleanup;
+    }
+    status = BCryptGetProperty(alg_handle, BCRYPT_HASH_LENGTH, (PBYTE)&hash_len, sizeof(hash_len), &result_len, 0);
+    if (!NT_SUCCESS(status))
+    {
+        goto cleanup;
+    }
+    hash.resize(hash_len);
+
+    status = BCryptCreateHash(alg_handle, &hash_handle, nullptr, 0, (PBYTE)key_c.c_str(), (ULONG)key_c.length(), 0);
+    if (!NT_SUCCESS(status))
+    {
+        goto cleanup;
+    }
+    status = BCryptHashData(hash_handle, (PBYTE)data_c.c_str(), (ULONG)data_c.length(), 0);
+    if (!NT_SUCCESS(status))
+    {
+        goto cleanup;
+    }
+    status = BCryptFinishHash(hash_handle, hash.data(), hash_len, 0);
+    if (!NT_SUCCESS(status))
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    if (hash_handle)
+    {
+        BCryptDestroyHash(hash_handle);
+    }
+    if (alg_handle)
+    {
+        BCryptCloseAlgorithmProvider(alg_handle, 0);
+    }
+
+    return hash;
+}
+#pragma warning(pop)
+
+#elif defined(_WIN32) && defined(__cplusplus_winrt) // Windows RT
+
+using namespace Windows::Security::Cryptography;
+using namespace Windows::Security::Cryptography::Core;
+using namespace Windows::Storage::Streams;
+
+std::vector<unsigned char> oauth1_config::_hmac_sha1(const utility::string_t& key, const utility::string_t& data)
+{
+    Platform::String ^ data_str = ref new Platform::String(data.c_str());
+    Platform::String ^ key_str = ref new Platform::String(key.c_str());
+
+    MacAlgorithmProvider ^ HMACSha1Provider = MacAlgorithmProvider::OpenAlgorithm(MacAlgorithmNames::HmacSha1);
+    IBuffer ^ content_buffer = CryptographicBuffer::ConvertStringToBinary(data_str, BinaryStringEncoding::Utf8);
+    IBuffer ^ key_buffer = CryptographicBuffer::ConvertStringToBinary(key_str, BinaryStringEncoding::Utf8);
+
+    auto signature_key = HMACSha1Provider->CreateKey(key_buffer);
+    auto signed_buffer = CryptographicEngine::Sign(signature_key, content_buffer);
+
+    Platform::Array<unsigned char, 1> ^ arr;
+    CryptographicBuffer::CopyToByteArray(signed_buffer, &arr);
+    return std::vector<unsigned char>(arr->Data, arr->Data + arr->Length);
+}
+
+#else // Linux, Mac OS X
+
+#include <openssl/hmac.h>
+
+std::vector<unsigned char> oauth1_config::_hmac_sha1(const utility::string_t& key, const utility::string_t& data)
+{
+    unsigned char digest[EVP_MAX_MD_SIZE];
+    unsigned int digest_len = 0;
+
+    HMAC(EVP_sha1(),
+         key.c_str(),
+         static_cast<int>(key.length()),
+         (const unsigned char*)data.c_str(),
+         data.length(),
+         digest,
+         &digest_len);
+
+    return std::vector<unsigned char>(digest, digest + digest_len);
+}
+
+#endif
+//
+// ...End of platform-dependent _hmac_sha1() block.
+//
+
+// Notes:
+// - Doesn't support URIs without scheme or host.
+// - If URI port is unspecified.
+utility::string_t oauth1_config::_build_base_string_uri(const uri& u)
+{
+    utility::string_t result(u.scheme());
+    result += _XPLATSTR("://");
+    result += u.host();
+    if (!u.is_port_default() && u.port() != 80 && u.port() != 443)
+    {
+        result += _XPLATSTR(':');
+        result += utility::conversions::details::to_string_t(u.port());
+    }
+
+    result += u.path();
+    return uri::encode_data_string(std::move(result));
+}
+
+utility::string_t oauth1_config::_build_normalized_parameters(web::http::uri u, const oauth1_state& state) const
+{
+    // While map sorts items by keys it doesn't take value into account.
+    // We need to sort the query parameters separately.
+    std::map<utility::string_t, utility::string_t> queries_map = http::uri::split_query(std::move(u).query());
+    std::vector<utility::string_t> queries;
+    for (const auto& query : queries_map)
+    {
+        queries.push_back(query.first + _XPLATSTR('=') + query.second);
+    }
+
+    for (const auto& query : parameters())
+    {
+        queries.push_back(query.first + _XPLATSTR('=') + query.second);
+    }
+
+    // Push oauth1 parameters.
+    queries.push_back(oauth1_strings::version + U("=1.0"));
+    queries.push_back(oauth1_strings::consumer_key + U("=") + web::uri::encode_data_string(consumer_key()));
+    if (!m_token.access_token().empty())
+    {
+        queries.push_back(oauth1_strings::token + U("=") + web::uri::encode_data_string(m_token.access_token()));
+    }
+    queries.push_back(oauth1_strings::signature_method + U("=") + method());
+    queries.push_back(oauth1_strings::timestamp + U("=") + state.timestamp());
+    queries.push_back(oauth1_strings::nonce + U("=") + state.nonce());
+    if (!state.extra_key().empty())
+    {
+        queries.push_back(state.extra_key() + U("=") + web::uri::encode_data_string(state.extra_value()));
+    }
+
+    // Sort parameters and build the string.
+    utility::string_t result;
+    if (!queries.empty())
+    {
+        auto i = queries.begin();
+        auto e = queries.end();
+        sort(i, e);
+        result = *i;
+        while (++i != e)
+        {
+            result += _XPLATSTR('&');
+            result += *i;
+        }
+    }
+
+    return uri::encode_data_string(result);
+}
+
+static bool is_application_x_www_form_urlencoded(http_request& request)
+{
+    const auto content_type(request.headers()[header_names::content_type]);
+    return 0 == content_type.find(web::http::details::mime_types::application_x_www_form_urlencoded);
+}
+
+utility::string_t oauth1_config::_build_signature_base_string(http_request request, oauth1_state state) const
+{
+    uri u(request.absolute_uri());
+    utility::string_t result(request.method());
+    result += _XPLATSTR('&');
+    result += _build_base_string_uri(u);
+
+    // http://oauth.net/core/1.0a/#signing_process
+    // 9.1.1.  Normalize Request Parameters
+    // The request parameters are collected, sorted and concatenated into a normalized string:
+    // - Parameters in the OAuth HTTP Authorization header excluding the realm parameter.
+    // - Parameters in the HTTP POST request body (with a content-type of application/x-www-form-urlencoded).
+    // - HTTP GET parameters added to the URLs in the query part (as defined by [RFC3986] section 3).
+    result += _XPLATSTR('&');
+    if (is_application_x_www_form_urlencoded(request))
+    {
+        // Note: this should be improved to not block and handle any potential exceptions.
+        utility::string_t str = request.extract_string(true).get();
+        request.set_body(str, web::http::details::mime_types::application_x_www_form_urlencoded);
+        uri v = http::uri_builder(request.absolute_uri()).append_query(std::move(str), false).to_uri();
+        result += _build_normalized_parameters(std::move(v), std::move(state));
+    }
+    else
+    {
+        result += _build_normalized_parameters(std::move(u), std::move(state));
+    }
+
+    return result;
+}
+
+utility::string_t oauth1_config::_build_signature(http_request request, oauth1_state state) const
+{
+    if (oauth1_methods::hmac_sha1 == method())
+    {
+        return _build_hmac_sha1_signature(std::move(request), std::move(state));
+    }
+    else if (oauth1_methods::plaintext == method())
+    {
+        return _build_plaintext_signature();
+    }
+    throw oauth1_exception(U("invalid signature method.")); // Should never happen.
+}
+
+pplx::task<void> oauth1_config::_request_token(oauth1_state state, bool is_temp_token_request)
+{
+    utility::string_t endpoint = is_temp_token_request ? temp_endpoint() : token_endpoint();
+    http_request req;
+    req.set_method(methods::POST);
+    req.set_request_uri(utility::string_t());
+    req._set_base_uri(endpoint);
+
+    _authenticate_request(req, std::move(state));
+
+    // configure proxy
+    http_client_config config;
+    config.set_proxy(m_proxy);
+
+    http_client client(endpoint, config);
+
+    return client.request(req)
+        .then([](http_response resp) { return resp.extract_string(); })
+        .then([this, is_temp_token_request](utility::string_t body) -> void {
+            auto query(uri::split_query(body));
+
+            if (is_temp_token_request)
+            {
+                auto callback_confirmed_param = query.find(oauth1_strings::callback_confirmed);
+                if (callback_confirmed_param == query.end())
+                {
+                    throw oauth1_exception(
+                        U("parameter 'oauth_callback_confirmed' is missing from response: ") + body +
+                        U(". the service may be using obsoleted and insecure OAuth Core 1.0 protocol."));
+                }
+            }
+
+            auto token_param = query.find(oauth1_strings::token);
+            if (token_param == query.end())
+            {
+                throw oauth1_exception(U("parameter 'oauth_token' missing from response: ") + body);
+            }
+
+            auto token_secret_param = query.find(oauth1_strings::token_secret);
+            if (token_secret_param == query.end())
+            {
+                throw oauth1_exception(U("parameter 'oauth_token_secret' missing from response: ") + body);
+            }
+
+            // Here the token can be either temporary or access token.
+            // The authorization is complete if it is access token.
+            m_is_authorization_completed = !is_temp_token_request;
+            m_token = oauth1_token(web::uri::decode(token_param->second), web::uri::decode(token_secret_param->second));
+
+            for (const auto& qa : query)
+            {
+                if (qa.first == oauth1_strings::token || qa.first == oauth1_strings::token_secret) continue;
+                m_token.set_additional_parameter(web::uri::decode(qa.first), web::uri::decode(qa.second));
+            }
+        });
+}
+
+void oauth1_config::_authenticate_request(http_request& request, oauth1_state state)
+{
+    utility::string_t authHeader(_XPLATSTR("OAuth "));
+    if (!realm().empty())
+    {
+        authHeader += oauth1_strings::realm;
+        authHeader += _XPLATSTR("=\"");
+        authHeader += web::uri::encode_data_string(realm());
+        authHeader += _XPLATSTR("\", ");
+    }
+
+    authHeader += oauth1_strings::version;
+    authHeader += _XPLATSTR("=\"1.0\", ");
+    authHeader += oauth1_strings::consumer_key;
+    authHeader += _XPLATSTR("=\"");
+    authHeader += web::uri::encode_data_string(consumer_key());
+
+    if (!m_token.access_token().empty())
+    {
+        authHeader += _XPLATSTR("\", ");
+        authHeader += oauth1_strings::token;
+        authHeader += _XPLATSTR("=\"");
+        authHeader += web::uri::encode_data_string(m_token.access_token());
+    }
+
+    authHeader += _XPLATSTR("\", ");
+    authHeader += oauth1_strings::signature_method;
+    authHeader += _XPLATSTR("=\"");
+    authHeader += method();
+    authHeader += _XPLATSTR("\", ");
+    authHeader += oauth1_strings::timestamp;
+    authHeader += _XPLATSTR("=\"");
+    authHeader += state.timestamp();
+    authHeader += _XPLATSTR("\", ");
+    authHeader += oauth1_strings::nonce;
+    authHeader += _XPLATSTR("=\"");
+    authHeader += state.nonce();
+    authHeader += _XPLATSTR("\", ");
+    authHeader += oauth1_strings::signature;
+    authHeader += _XPLATSTR("=\"");
+    authHeader += uri::encode_data_string(_build_signature(request, state));
+    authHeader += _XPLATSTR("\"");
+
+    if (!state.extra_key().empty())
+    {
+        authHeader += _XPLATSTR(", ");
+        authHeader += state.extra_key();
+        authHeader += _XPLATSTR("=\"");
+        authHeader += web::uri::encode_data_string(state.extra_value());
+        authHeader += _XPLATSTR("\"");
+    }
+
+    request.headers().add(header_names::authorization, std::move(authHeader));
+}
+
+pplx::task<utility::string_t> oauth1_config::build_authorization_uri()
+{
+    pplx::task<void> temp_token_req =
+        _request_token(_generate_auth_state(oauth1_strings::callback, callback_uri()), true);
+
+    return temp_token_req.then([this] {
+        uri_builder ub(auth_endpoint());
+        ub.append_query(oauth1_strings::token, m_token.access_token());
+        return ub.to_string();
+    });
+}
+
+pplx::task<void> oauth1_config::token_from_redirected_uri(const web::http::uri& redirected_uri)
+{
+    auto query = uri::split_query(redirected_uri.query());
+
+    auto token_param = query.find(oauth1_strings::token);
+    if (token_param == query.end())
+    {
+        return pplx::task_from_exception<void>(
+            oauth1_exception(U("parameter 'oauth_token' missing from redirected URI.")));
+    }
+    if (m_token.access_token() != token_param->second)
+    {
+        return pplx::task_from_exception<void>(oauth1_exception(
+            _XPLATSTR("redirected URI parameter 'oauth_token'='") + token_param->second +
+            _XPLATSTR("' does not match temporary token='") + m_token.access_token() + _XPLATSTR("'.")));
+    }
+
+    auto verifier_param = query.find(oauth1_strings::verifier);
+    if (verifier_param == query.end())
+    {
+        return pplx::task_from_exception<void>(
+            oauth1_exception(U("parameter 'oauth_verifier' missing from redirected URI.")));
+    }
+
+    return token_from_verifier(verifier_param->second);
+}
+
+// Remove once VS 2013 is no longer supported.
+#if defined(_WIN32) && _MSC_VER < 1900
+static const oauth1_token empty_token;
+#endif
+const oauth1_token& oauth1_config::token() const
+{
+    if (m_is_authorization_completed)
+    {
+        // Return the token object only if authorization has been completed.
+        // Otherwise the token object holds a temporary token which should not be
+        // returned to the user.
+        return m_token;
+    }
+    else
+    {
+#if !defined(_WIN32) || _MSC_VER >= 1900
+        static const oauth1_token empty_token;
+#endif
+        return empty_token;
+    }
+}
+
+#define _OAUTH1_METHODS
+#define DAT(a, b) const oauth1_method oauth1_methods::a = b;
+#include "cpprest/details/http_constants.dat"
+#undef _OAUTH1_METHODS
+#undef DAT
+
+} // namespace experimental
+} // namespace oauth1
+} // namespace http
+} // namespace web
+
+#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
diff --git a/Release/src/http/oauth/oauth2.cpp b/Release/src/http/oauth/oauth2.cpp
new file mode 100644 (file)
index 0000000..3e54a6e
--- /dev/null
@@ -0,0 +1,228 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: Oauth 2.0
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <sstream>
+
+using utility::conversions::to_utf8string;
+using web::http::client::http_client;
+using web::http::client::http_client_config;
+using web::http::details::mime_types;
+using web::http::oauth2::details::oauth2_strings;
+
+// Expose base64 conversion for arbitrary buffer.
+extern utility::string_t _to_base64(const unsigned char* ptr, size_t size);
+
+namespace web
+{
+namespace http
+{
+namespace oauth2
+{
+namespace details
+{
+#define _OAUTH2_STRINGS
+#define DAT(a_, b_) const oauth2_string oauth2_strings::a_(_XPLATSTR(b_));
+#include "cpprest/details/http_constants.dat"
+#undef _OAUTH2_STRINGS
+#undef DAT
+
+} // namespace details
+
+namespace experimental
+{
+utility::string_t oauth2_config::build_authorization_uri(bool generate_state)
+{
+    const utility::string_t response_type((implicit_grant()) ? oauth2_strings::token : oauth2_strings::code);
+    uri_builder ub(auth_endpoint());
+    ub.append_query(oauth2_strings::response_type, response_type);
+    ub.append_query(oauth2_strings::client_id, client_key());
+    ub.append_query(oauth2_strings::redirect_uri, redirect_uri());
+
+    if (generate_state)
+    {
+        m_state = m_state_generator.generate();
+    }
+    ub.append_query(oauth2_strings::state, state());
+
+    if (!scope().empty())
+    {
+        ub.append_query(oauth2_strings::scope, scope());
+    }
+    return ub.to_string();
+}
+
+pplx::task<void> oauth2_config::token_from_redirected_uri(const web::http::uri& redirected_uri)
+{
+    auto query = uri::split_query((implicit_grant()) ? redirected_uri.fragment() : redirected_uri.query());
+
+    auto state_param = query.find(oauth2_strings::state);
+    if (state_param == query.end())
+    {
+        return pplx::task_from_exception<void>(oauth2_exception(U("parameter 'state' missing from redirected URI.")));
+    }
+    if (state() != state_param->second)
+    {
+        utility::string_t err(_XPLATSTR("redirected URI parameter 'state'='"));
+        err += state_param->second;
+        err += _XPLATSTR("' does not match state='");
+        err += state();
+        err += _XPLATSTR("'.");
+        return pplx::task_from_exception<void>(oauth2_exception(std::move(err)));
+    }
+
+    auto code_param = query.find(oauth2_strings::code);
+    if (code_param != query.end())
+    {
+        return token_from_code(code_param->second);
+    }
+
+    // NOTE: The redirected URI contains access token only in the implicit grant.
+    // The implicit grant never passes a refresh token.
+    auto token_param = query.find(oauth2_strings::access_token);
+    if (token_param == query.end())
+    {
+        return pplx::task_from_exception<void>(
+            oauth2_exception(U("either 'code' or 'access_token' parameter must be in the redirected URI.")));
+    }
+
+    set_token(token_param->second);
+    return pplx::task_from_result();
+}
+
+pplx::task<void> oauth2_config::_request_token(uri_builder& request_body_ub)
+{
+    http_request request;
+    request.set_method(methods::POST);
+    request.set_request_uri(utility::string_t());
+
+    if (!user_agent().empty())
+    {
+        request.headers().add(web::http::header_names::user_agent, user_agent());
+    }
+
+    if (!scope().empty())
+    {
+        request_body_ub.append_query(oauth2_strings::scope, uri::encode_data_string(scope()), false);
+    }
+
+    if (http_basic_auth())
+    {
+        // Build HTTP Basic authorization header.
+        const std::string creds_utf8(
+            to_utf8string(uri::encode_data_string(client_key()) + U(":") + uri::encode_data_string(client_secret())));
+        request.headers().add(
+            header_names::authorization,
+            U("Basic ") + _to_base64(reinterpret_cast<const unsigned char*>(creds_utf8.data()), creds_utf8.size()));
+    }
+    else
+    {
+        // Add credentials to query as-is.
+        request_body_ub.append_query(oauth2_strings::client_id, uri::encode_data_string(client_key()), false);
+        request_body_ub.append_query(oauth2_strings::client_secret, uri::encode_data_string(client_secret()), false);
+    }
+    request.set_body(request_body_ub.query(), mime_types::application_x_www_form_urlencoded);
+
+    // configure proxy
+    http_client_config config;
+    config.set_proxy(m_proxy);
+
+    http_client token_client(token_endpoint(), config);
+
+    return token_client.request(request)
+        .then([](http_response resp) { return resp.extract_json(); })
+        .then([this](json::value json_resp) -> void { set_token(_parse_token_from_json(json_resp)); });
+}
+
+oauth2_token oauth2_config::_parse_token_from_json(const json::value& token_json)
+{
+    oauth2_token result;
+
+    if (token_json.has_string_field(oauth2_strings::access_token))
+    {
+        result.set_access_token(token_json.at(oauth2_strings::access_token).as_string());
+    }
+    else
+    {
+        throw oauth2_exception(U("response json contains no 'access_token': ") + token_json.serialize());
+    }
+
+    if (token_json.has_string_field(oauth2_strings::token_type))
+    {
+        result.set_token_type(token_json.at(oauth2_strings::token_type).as_string());
+    }
+    else
+    {
+        // Some services don't return 'token_type' while it's required by OAuth 2.0 spec:
+        // http://tools.ietf.org/html/rfc6749#section-5.1
+        // As workaround we act as if 'token_type=bearer' was received.
+        result.set_token_type(oauth2_strings::bearer);
+    }
+    if (!utility::details::str_iequal(result.token_type(), oauth2_strings::bearer))
+    {
+        throw oauth2_exception(U("only 'token_type=bearer' access tokens are currently supported: ") +
+                               token_json.serialize());
+    }
+
+    if (token_json.has_string_field(oauth2_strings::refresh_token))
+    {
+        result.set_refresh_token(token_json.at(oauth2_strings::refresh_token).as_string());
+    }
+    else
+    {
+        // Do nothing. Preserves the old refresh token.
+    }
+
+    if (token_json.has_field(oauth2_strings::expires_in))
+    {
+        const auto& json_expires_in_val = token_json.at(oauth2_strings::expires_in);
+
+        if (json_expires_in_val.is_number())
+            result.set_expires_in(json_expires_in_val.as_number().to_int64());
+        else
+        {
+            // Handle the case of a number as a JSON "string".
+            // Using streams because std::stoll isn't avaliable on Android.
+            int64_t expires;
+            utility::istringstream_t iss(json_expires_in_val.as_string());
+            iss.exceptions(std::ios::badbit | std::ios::failbit);
+            iss >> expires;
+            result.set_expires_in(expires);
+        }
+    }
+    else
+    {
+        result.set_expires_in(oauth2_token::undefined_expiration);
+    }
+
+    if (token_json.has_string_field(oauth2_strings::scope))
+    {
+        // The authorization server may return different scope from the one requested.
+        // This however doesn't necessarily mean the token authorization scope is different.
+        // See: http://tools.ietf.org/html/rfc6749#section-3.3
+        result.set_scope(token_json.at(oauth2_strings::scope).as_string());
+    }
+    else
+    {
+        // Use the requested scope() if no scope parameter was returned.
+        result.set_scope(scope());
+    }
+
+    return result;
+}
+
+} // namespace experimental
+} // namespace oauth2
+} // namespace http
+} // namespace web
diff --git a/Release/src/json/json.cpp b/Release/src/json/json.cpp
new file mode 100644 (file)
index 0000000..f1f0865
--- /dev/null
@@ -0,0 +1,476 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: JSON parser and writer
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+
+bool json::details::g_keep_json_object_unsorted = false;
+void json::keep_object_element_order(bool keep_order) { json::details::g_keep_json_object_unsorted = keep_order; }
+
+utility::ostream_t& web::json::operator<<(utility::ostream_t& os, const web::json::value& val)
+{
+    val.serialize(os);
+    return os;
+}
+
+utility::istream_t& web::json::operator>>(utility::istream_t& is, json::value& val)
+{
+    val = json::value::parse(is);
+    return is;
+}
+
+web::json::value::value()
+    : m_value(utility::details::make_unique<web::json::details::_Null>())
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::Null)
+#endif
+{
+}
+
+web::json::value::value(int32_t value)
+    : m_value(utility::details::make_unique<web::json::details::_Number>(value))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::Number)
+#endif
+{
+}
+
+web::json::value::value(uint32_t value)
+    : m_value(utility::details::make_unique<web::json::details::_Number>(value))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::Number)
+#endif
+{
+}
+
+web::json::value::value(int64_t value)
+    : m_value(utility::details::make_unique<web::json::details::_Number>(value))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::Number)
+#endif
+{
+}
+
+web::json::value::value(uint64_t value)
+    : m_value(utility::details::make_unique<web::json::details::_Number>(value))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::Number)
+#endif
+{
+}
+
+web::json::value::value(double value)
+    : m_value(utility::details::make_unique<web::json::details::_Number>(value))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::Number)
+#endif
+{
+}
+
+web::json::value::value(bool value)
+    : m_value(utility::details::make_unique<web::json::details::_Boolean>(value))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::Boolean)
+#endif
+{
+}
+
+web::json::value::value(utility::string_t value)
+    : m_value(utility::details::make_unique<web::json::details::_String>(std::move(value)))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::String)
+#endif
+{
+}
+
+web::json::value::value(utility::string_t value, bool has_escape_chars)
+    : m_value(utility::details::make_unique<web::json::details::_String>(std::move(value), has_escape_chars))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::String)
+#endif
+{
+}
+
+web::json::value::value(const utility::char_t* value)
+    : m_value(utility::details::make_unique<web::json::details::_String>(value))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::String)
+#endif
+{
+}
+
+web::json::value::value(const utility::char_t* value, bool has_escape_chars)
+    : m_value(utility::details::make_unique<web::json::details::_String>(utility::string_t(value), has_escape_chars))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(value::String)
+#endif
+{
+}
+
+web::json::value::value(const value& other)
+    : m_value(other.m_value->_copy_value())
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    , m_kind(other.m_kind)
+#endif
+{
+}
+
+web::json::value& web::json::value::operator=(const value& other)
+{
+    if (this != &other)
+    {
+        m_value = std::unique_ptr<details::_Value>(other.m_value->_copy_value());
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+        m_kind = other.m_kind;
+#endif
+    }
+    return *this;
+}
+
+web::json::value::value(value&& other) CPPREST_NOEXCEPT : m_value(std::move(other.m_value))
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+    ,
+                                                          m_kind(other.m_kind)
+#endif
+{
+}
+
+web::json::value& web::json::value::operator=(web::json::value&& other) CPPREST_NOEXCEPT
+{
+    if (this != &other)
+    {
+        m_value.swap(other.m_value);
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+        m_kind = other.m_kind;
+#endif
+    }
+    return *this;
+}
+
+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(uint32_t 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(uint64_t value) { return web::json::value(value); }
+
+web::json::value web::json::value::boolean(bool value) { return web::json::value(value); }
+
+web::json::value web::json::value::string(utility::string_t value)
+{
+    std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_String>(std::move(value));
+    return web::json::value(std::move(ptr)
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+                                ,
+                            value::String
+#endif
+    );
+}
+
+web::json::value web::json::value::string(utility::string_t value, bool has_escape_chars)
+{
+    std::unique_ptr<details::_Value> ptr =
+        utility::details::make_unique<details::_String>(std::move(value), has_escape_chars);
+    return web::json::value(std::move(ptr)
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+                                ,
+                            value::String
+#endif
+    );
+}
+
+#ifdef _WIN32
+web::json::value web::json::value::string(const std::string& value)
+{
+    std::unique_ptr<details::_Value> ptr =
+        utility::details::make_unique<details::_String>(utility::conversions::to_utf16string(value));
+    return web::json::value(std::move(ptr)
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+                                ,
+                            value::String
+#endif
+    );
+}
+#endif
+
+web::json::value web::json::value::object(bool keep_order)
+{
+    std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_Object>(keep_order);
+    return web::json::value(std::move(ptr)
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+                                ,
+                            value::Object
+#endif
+    );
+}
+
+web::json::value web::json::value::object(std::vector<std::pair<::utility::string_t, value>> fields, bool keep_order)
+{
+    std::unique_ptr<details::_Value> ptr =
+        utility::details::make_unique<details::_Object>(std::move(fields), keep_order);
+    return web::json::value(std::move(ptr)
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+                                ,
+                            value::Object
+#endif
+    );
+}
+
+web::json::value web::json::value::array()
+{
+    std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_Array>();
+    return web::json::value(std::move(ptr)
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+                                ,
+                            value::Array
+#endif
+    );
+}
+
+web::json::value web::json::value::array(size_t size)
+{
+    std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_Array>(size);
+    return web::json::value(std::move(ptr)
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+                                ,
+                            value::Array
+#endif
+    );
+}
+
+web::json::value web::json::value::array(std::vector<value> elements)
+{
+    std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_Array>(std::move(elements));
+    return web::json::value(std::move(ptr)
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+                                ,
+                            value::Array
+#endif
+    );
+}
+
+const web::json::number& web::json::value::as_number() const { return m_value->as_number(); }
+
+double web::json::value::as_double() const { return m_value->as_double(); }
+
+int web::json::value::as_integer() const { return m_value->as_integer(); }
+
+bool web::json::value::as_bool() const { return m_value->as_bool(); }
+
+json::array& web::json::value::as_array() { return m_value->as_array(); }
+
+const json::array& web::json::value::as_array() const { return m_value->as_array(); }
+
+json::object& web::json::value::as_object() { return m_value->as_object(); }
+
+const json::object& web::json::value::as_object() const { return m_value->as_object(); }
+
+bool web::json::number::is_int32() const
+{
+    switch (m_type)
+    {
+        case signed_type:
+            return m_intval >= (std::numeric_limits<int32_t>::min)() &&
+                   m_intval <= (std::numeric_limits<int32_t>::max)();
+        case unsigned_type: return m_uintval <= (uint32_t)(std::numeric_limits<int32_t>::max)();
+        case double_type:
+        default: return false;
+    }
+}
+
+bool web::json::number::is_uint32() const
+{
+    switch (m_type)
+    {
+        case signed_type: return m_intval >= 0 && m_intval <= (std::numeric_limits<uint32_t>::max)();
+        case unsigned_type: return m_uintval <= (std::numeric_limits<uint32_t>::max)();
+        case double_type:
+        default: return false;
+    }
+}
+
+bool web::json::number::is_int64() const
+{
+    switch (m_type)
+    {
+        case signed_type: return true;
+        case unsigned_type: return m_uintval <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)());
+        case double_type:
+        default: return false;
+    }
+}
+
+bool web::json::details::_String::has_escape_chars(const _String& str)
+{
+    return std::any_of(std::begin(str.m_string), std::end(str.m_string), [](utility::string_t::value_type const x) {
+        if (x <= 31)
+        {
+            return true;
+        }
+        if (x == '"')
+        {
+            return true;
+        }
+        if (x == '\\')
+        {
+            return true;
+        }
+        return false;
+    });
+}
+
+web::json::value::value_type json::value::type() const { return m_value->type(); }
+
+bool json::value::is_integer() const
+{
+    if (!is_number())
+    {
+        return false;
+    }
+    return m_value->is_integer();
+}
+
+bool json::value::is_double() const
+{
+    if (!is_number())
+    {
+        return false;
+    }
+    return m_value->is_double();
+}
+
+json::value& web::json::details::_Object::index(const utility::string_t& key) { return m_object[key]; }
+
+bool web::json::details::_Object::has_field(const utility::string_t& key) const
+{
+    return m_object.find(key) != m_object.end();
+}
+
+bool web::json::value::has_number_field(const utility::string_t& key) const
+{
+    return has_field(key) && at(key).is_number();
+}
+
+bool web::json::value::has_integer_field(const utility::string_t& key) const
+{
+    return has_field(key) && at(key).is_integer();
+}
+
+bool web::json::value::has_double_field(const utility::string_t& key) const
+{
+    return has_field(key) && at(key).is_double();
+}
+
+bool web::json::value::has_boolean_field(const utility::string_t& key) const
+{
+    return has_field(key) && at(key).is_boolean();
+}
+
+bool web::json::value::has_string_field(const utility::string_t& key) const
+{
+    return has_field(key) && at(key).is_string();
+}
+
+bool web::json::value::has_array_field(const utility::string_t& key) const
+{
+    return has_field(key) && at(key).is_array();
+}
+
+bool web::json::value::has_object_field(const utility::string_t& key) const
+{
+    return has_field(key) && at(key).is_object();
+}
+
+utility::string_t json::value::to_string() const
+{
+#ifndef _WIN32
+    utility::details::scoped_c_thread_locale locale;
+#endif
+    return m_value->to_string();
+}
+
+bool json::value::operator==(const json::value& other) const
+{
+    if (this->m_value.get() == other.m_value.get()) return true;
+    if (this->type() != other.type()) return false;
+
+    switch (this->type())
+    {
+        case Null: return true;
+        case Number: return this->as_number() == other.as_number();
+        case Boolean: return this->as_bool() == other.as_bool();
+        case String: return this->as_string() == other.as_string();
+        case Object:
+            return static_cast<const json::details::_Object*>(this->m_value.get())
+                ->is_equal(static_cast<const json::details::_Object*>(other.m_value.get()));
+        case Array:
+            return static_cast<const json::details::_Array*>(this->m_value.get())
+                ->is_equal(static_cast<const json::details::_Array*>(other.m_value.get()));
+    }
+    __assume(0);
+}
+
+void web::json::value::erase(size_t index) { return this->as_array().erase(index); }
+
+void web::json::value::erase(const utility::string_t& key) { return this->as_object().erase(key); }
+
+// at() overloads
+web::json::value& web::json::value::at(size_t index) { return this->as_array().at(index); }
+
+const web::json::value& web::json::value::at(size_t index) const { return this->as_array().at(index); }
+
+web::json::value& web::json::value::at(const utility::string_t& key) { return this->as_object().at(key); }
+
+const web::json::value& web::json::value::at(const utility::string_t& key) const { return this->as_object().at(key); }
+
+web::json::value& web::json::value::operator[](const utility::string_t& key)
+{
+    if (this->is_null())
+    {
+        m_value.reset(new web::json::details::_Object(details::g_keep_json_object_unsorted));
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+        m_kind = value::Object;
+#endif
+    }
+    return m_value->index(key);
+}
+
+web::json::value& web::json::value::operator[](size_t index)
+{
+    if (this->is_null())
+    {
+        m_value.reset(new web::json::details::_Array());
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+        m_kind = value::Array;
+#endif
+    }
+    return m_value->index(index);
+}
+
+// Remove once VS 2013 is no longer supported.
+#if defined(_WIN32) && _MSC_VER < 1900
+static web::json::details::json_error_category_impl instance;
+#endif
+const web::json::details::json_error_category_impl& web::json::details::json_error_category()
+{
+#if !defined(_WIN32) || _MSC_VER >= 1900
+    static web::json::details::json_error_category_impl instance;
+#endif
+    return instance;
+}
diff --git a/Release/src/json/json_parsing.cpp b/Release/src/json/json_parsing.cpp
new file mode 100644 (file)
index 0000000..cdef442
--- /dev/null
@@ -0,0 +1,1249 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: JSON parser
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <cstdlib>
+
+#if defined(_MSC_VER)
+#pragma warning(disable : 4127) // allow expressions like while(true) pass
+#endif
+using namespace web;
+using namespace web::json;
+using namespace utility;
+using namespace utility::conversions;
+
+std::array<signed char, 128> _hexval = {
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,  1,  2,  3,
+     4,  5,  6,  7,  8,  9,  -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
+
+namespace web
+{
+namespace json
+{
+namespace details
+{
+//
+// JSON Parsing
+//
+
+template<typename Token>
+#if defined(_WIN32)
+__declspec(noreturn)
+#else
+    __attribute__((noreturn))
+#endif
+    void CreateException(const Token& tk, const utility::string_t& message)
+{
+    std::string str("* Line ");
+    str += std::to_string(tk.start.m_line);
+    str += ", Column ";
+    str += std::to_string(tk.start.m_column);
+    str += " Syntax error: ";
+    str += utility::conversions::to_utf8string(message);
+    throw web::json::json_exception(std::move(str));
+}
+
+template<typename Token>
+void SetErrorCode(Token& tk, json_error jsonErrorCode)
+{
+    tk.m_error = std::error_code(jsonErrorCode, json_error_category());
+}
+
+template<typename CharType>
+class JSON_Parser
+{
+public:
+    JSON_Parser() : m_currentLine(1), m_currentColumn(1), m_currentParsingDepth(0) {}
+
+    struct Location
+    {
+        size_t m_line;
+        size_t m_column;
+    };
+
+    struct Token
+    {
+        enum Kind
+        {
+            TKN_EOF,
+
+            TKN_OpenBrace,
+            TKN_CloseBrace,
+            TKN_OpenBracket,
+            TKN_CloseBracket,
+            TKN_Comma,
+            TKN_Colon,
+            TKN_StringLiteral,
+            TKN_NumberLiteral,
+            TKN_IntegerLiteral,
+            TKN_BooleanLiteral,
+            TKN_NullLiteral,
+            TKN_Comment
+        };
+
+        Token() : kind(TKN_EOF) {}
+
+        Kind kind;
+        std::basic_string<CharType> string_val;
+
+        typename JSON_Parser<CharType>::Location start;
+
+        union {
+            double double_val;
+            int64_t int64_val;
+            uint64_t uint64_val;
+            bool boolean_val;
+            bool has_unescape_symbol;
+        };
+
+        bool signed_number;
+
+        std::error_code m_error;
+    };
+
+    void GetNextToken(Token&);
+
+    web::json::value ParseValue(typename JSON_Parser<CharType>::Token& first)
+    {
+#ifndef _WIN32
+        utility::details::scoped_c_thread_locale locale;
+#endif
+
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+        auto _value = _ParseValue(first);
+        auto type = _value->type();
+        return web::json::value(std::move(_value), type);
+#else
+        return web::json::value(_ParseValue(first));
+#endif
+    }
+
+protected:
+    typedef typename std::char_traits<CharType>::int_type int_type;
+    virtual int_type NextCharacter() = 0;
+    virtual int_type PeekCharacter() = 0;
+
+    virtual bool CompleteComment(Token& token);
+    virtual bool CompleteStringLiteral(Token& token);
+    int convert_unicode_to_code_point();
+    bool handle_unescape_char(Token& token);
+
+private:
+    bool CompleteNumberLiteral(CharType first, Token& token);
+    bool ParseInt64(CharType first, uint64_t& value);
+    bool CompleteKeywordTrue(Token& token);
+    bool CompleteKeywordFalse(Token& token);
+    bool CompleteKeywordNull(Token& token);
+    std::unique_ptr<web::json::details::_Value> _ParseValue(typename JSON_Parser<CharType>::Token& first);
+    std::unique_ptr<web::json::details::_Value> _ParseObject(typename JSON_Parser<CharType>::Token& tkn);
+    std::unique_ptr<web::json::details::_Value> _ParseArray(typename JSON_Parser<CharType>::Token& tkn);
+
+    JSON_Parser& operator=(const JSON_Parser&);
+
+    int_type EatWhitespace();
+
+    void CreateToken(typename JSON_Parser<CharType>::Token& tk, typename Token::Kind kind, Location& start)
+    {
+        tk.kind = kind;
+        tk.start = start;
+        tk.string_val.clear();
+    }
+
+    void CreateToken(typename JSON_Parser<CharType>::Token& tk, typename Token::Kind kind)
+    {
+        tk.kind = kind;
+        tk.start.m_line = m_currentLine;
+        tk.start.m_column = m_currentColumn;
+        tk.string_val.clear();
+    }
+
+protected:
+    size_t m_currentLine;
+    size_t m_currentColumn;
+    size_t m_currentParsingDepth;
+
+// The DEBUG macro is defined in XCode but we don't in our CMakeList
+// so for now we will keep the same on debug and release. In the future
+// this can be increase on release if necessary.
+#if defined(__APPLE__)
+    static const size_t maxParsingDepth = 32;
+#else
+    static const size_t maxParsingDepth = 128;
+#endif
+};
+
+// Replace with template alias once VS 2012 support is removed.
+template<typename CharType>
+typename std::char_traits<CharType>::int_type eof()
+{
+    return std::char_traits<CharType>::eof();
+}
+
+template<typename CharType>
+class JSON_StreamParser : public JSON_Parser<CharType>
+{
+public:
+    JSON_StreamParser(std::basic_istream<CharType>& stream) : m_streambuf(stream.rdbuf()) {}
+
+protected:
+    virtual typename JSON_Parser<CharType>::int_type NextCharacter();
+    virtual typename JSON_Parser<CharType>::int_type PeekCharacter();
+
+private:
+    typename std::basic_streambuf<CharType, std::char_traits<CharType>>* m_streambuf;
+};
+
+template<typename CharType>
+class JSON_StringParser : public JSON_Parser<CharType>
+{
+public:
+    JSON_StringParser(const std::basic_string<CharType>& string) : m_position(&string[0])
+    {
+        m_startpos = m_position;
+        m_endpos = m_position + string.size();
+    }
+
+protected:
+    virtual typename JSON_Parser<CharType>::int_type NextCharacter();
+    virtual typename JSON_Parser<CharType>::int_type PeekCharacter();
+
+    virtual bool CompleteComment(typename JSON_Parser<CharType>::Token& token);
+    virtual bool CompleteStringLiteral(typename JSON_Parser<CharType>::Token& token);
+
+private:
+    bool finish_parsing_string_with_unescape_char(typename JSON_Parser<CharType>::Token& token);
+    const CharType* m_position;
+    const CharType* m_startpos;
+    const CharType* m_endpos;
+};
+
+template<typename CharType>
+typename JSON_Parser<CharType>::int_type JSON_StreamParser<CharType>::NextCharacter()
+{
+    auto ch = m_streambuf->sbumpc();
+
+    if (ch == '\n')
+    {
+        this->m_currentLine += 1;
+        this->m_currentColumn = 0;
+    }
+    else
+    {
+        this->m_currentColumn += 1;
+    }
+
+    return ch;
+}
+
+template<typename CharType>
+typename JSON_Parser<CharType>::int_type JSON_StreamParser<CharType>::PeekCharacter()
+{
+    return m_streambuf->sgetc();
+}
+
+template<typename CharType>
+typename JSON_Parser<CharType>::int_type JSON_StringParser<CharType>::NextCharacter()
+{
+    if (m_position == m_endpos) return eof<CharType>();
+
+    CharType ch = *m_position;
+    m_position += 1;
+
+    if (ch == '\n')
+    {
+        this->m_currentLine += 1;
+        this->m_currentColumn = 0;
+    }
+    else
+    {
+        this->m_currentColumn += 1;
+    }
+
+    return ch;
+}
+
+template<typename CharType>
+typename JSON_Parser<CharType>::int_type JSON_StringParser<CharType>::PeekCharacter()
+{
+    if (m_position == m_endpos) return eof<CharType>();
+
+    return *m_position;
+}
+
+//
+// Consume whitespace characters and return the first non-space character or EOF
+//
+template<typename CharType>
+typename JSON_Parser<CharType>::int_type JSON_Parser<CharType>::EatWhitespace()
+{
+    auto ch = NextCharacter();
+
+    while (ch != eof<CharType>() && iswspace(static_cast<wint_t>(ch)))
+    {
+        ch = NextCharacter();
+    }
+
+    return ch;
+}
+
+template<typename CharType>
+bool JSON_Parser<CharType>::CompleteKeywordTrue(Token& token)
+{
+    if (NextCharacter() != 'r') return false;
+    if (NextCharacter() != 'u') return false;
+    if (NextCharacter() != 'e') return false;
+    token.kind = Token::TKN_BooleanLiteral;
+    token.boolean_val = true;
+    return true;
+}
+
+template<typename CharType>
+bool JSON_Parser<CharType>::CompleteKeywordFalse(Token& token)
+{
+    if (NextCharacter() != 'a') return false;
+    if (NextCharacter() != 'l') return false;
+    if (NextCharacter() != 's') return false;
+    if (NextCharacter() != 'e') return false;
+    token.kind = Token::TKN_BooleanLiteral;
+    token.boolean_val = false;
+    return true;
+}
+
+template<typename CharType>
+bool JSON_Parser<CharType>::CompleteKeywordNull(Token& token)
+{
+    if (NextCharacter() != 'u') return false;
+    if (NextCharacter() != 'l') return false;
+    if (NextCharacter() != 'l') return false;
+    token.kind = Token::TKN_NullLiteral;
+    return true;
+}
+
+// Returns false only on overflow
+template<typename CharType>
+inline bool JSON_Parser<CharType>::ParseInt64(CharType first, uint64_t& value)
+{
+    value = first - '0';
+    auto ch = PeekCharacter();
+    while (ch >= '0' && ch <= '9')
+    {
+        unsigned int next_digit = (unsigned int)(ch - '0');
+        if (value > (ULLONG_MAX / 10) || (value == ULLONG_MAX / 10 && next_digit > ULLONG_MAX % 10)) return false;
+
+        NextCharacter();
+
+        value *= 10;
+        value += next_digit;
+        ch = PeekCharacter();
+    }
+    return true;
+}
+
+// This namespace hides the x-plat helper functions
+namespace
+{
+#if defined(_WIN32)
+static int print_llu(char* ptr, size_t n, uint64_t val64)
+{
+    return _snprintf_s_l(ptr, n, _TRUNCATE, "%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64);
+}
+
+static int print_llu(wchar_t* ptr, size_t n, uint64_t val64)
+{
+    return _snwprintf_s_l(ptr, n, _TRUNCATE, L"%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64);
+}
+static double anystod(const char* str)
+{
+    return _strtod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale());
+}
+static double anystod(const wchar_t* str)
+{
+    return _wcstod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale());
+}
+#else
+static int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long long val64)
+{
+    return snprintf(ptr, n, "%llu", val64);
+}
+static int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long val64)
+{
+    return snprintf(ptr, n, "%lu", val64);
+}
+static double __attribute__((__unused__)) anystod(const char* str) { return strtod(str, nullptr); }
+static double __attribute__((__unused__)) anystod(const wchar_t* str) { return wcstod(str, nullptr); }
+#endif
+} // namespace
+
+template<typename CharType>
+bool JSON_Parser<CharType>::CompleteNumberLiteral(CharType first, Token& token)
+{
+    bool minus_sign;
+
+    if (first == '-')
+    {
+        minus_sign = true;
+
+        // Safe to cast because the check after this if/else statement will cover EOF.
+        first = static_cast<CharType>(NextCharacter());
+    }
+    else
+    {
+        minus_sign = false;
+    }
+
+    if (first < '0' || first > '9') return false;
+
+    auto ch = PeekCharacter();
+
+    // Check for two (or more) zeros at the beginning
+    if (first == '0' && ch == '0') return false;
+
+    // Parse the number assuming its integer
+    uint64_t val64;
+    bool complete = ParseInt64(first, val64);
+
+    ch = PeekCharacter();
+    if (complete && ch != '.' && ch != 'E' && ch != 'e')
+    {
+        if (minus_sign)
+        {
+            if (val64 > static_cast<uint64_t>(1) << 63)
+            {
+                // It is negative and cannot be represented in int64, so we resort to double
+                token.double_val = 0 - static_cast<double>(val64);
+                token.signed_number = true;
+                token.kind = JSON_Parser<CharType>::Token::TKN_NumberLiteral;
+                return true;
+            }
+
+            // It is negative, but fits into int64
+            token.int64_val = 0 - static_cast<int64_t>(val64);
+            token.kind = JSON_Parser<CharType>::Token::TKN_IntegerLiteral;
+            token.signed_number = true;
+            return true;
+        }
+
+        // It is positive so we use unsigned int64
+        token.uint64_val = val64;
+        token.kind = JSON_Parser<CharType>::Token::TKN_IntegerLiteral;
+        token.signed_number = false;
+        return true;
+    }
+
+    // Magic number 5 leaves room for decimal point, null terminator, etc (in most cases)
+    ::std::vector<CharType> buf(::std::numeric_limits<uint64_t>::digits10 + 5);
+    int count = print_llu(buf.data(), buf.size(), val64);
+    _ASSERTE(count >= 0);
+    _ASSERTE((size_t)count < buf.size());
+    // Resize to cut off the null terminator
+    buf.resize(count);
+
+    bool decimal = false;
+
+    while (ch != eof<CharType>())
+    {
+        // Digit encountered?
+        if (ch >= '0' && ch <= '9')
+        {
+            buf.push_back(static_cast<CharType>(ch));
+            NextCharacter();
+            ch = PeekCharacter();
+        }
+
+        // Decimal dot?
+        else if (ch == '.')
+        {
+            if (decimal) return false;
+
+            decimal = true;
+            buf.push_back(static_cast<CharType>(ch));
+
+            NextCharacter();
+            ch = PeekCharacter();
+
+            // Check that the following char is a digit
+            if (ch < '0' || ch > '9') return false;
+
+            buf.push_back(static_cast<CharType>(ch));
+            NextCharacter();
+            ch = PeekCharacter();
+        }
+
+        // Exponent?
+        else if (ch == 'E' || ch == 'e')
+        {
+            buf.push_back(static_cast<CharType>(ch));
+            NextCharacter();
+            ch = PeekCharacter();
+
+            // Check for the exponent sign
+            if (ch == '+')
+            {
+                buf.push_back(static_cast<CharType>(ch));
+                NextCharacter();
+                ch = PeekCharacter();
+            }
+            else if (ch == '-')
+            {
+                buf.push_back(static_cast<CharType>(ch));
+                NextCharacter();
+                ch = PeekCharacter();
+            }
+
+            // First number of the exponent
+            if (ch >= '0' && ch <= '9')
+            {
+                buf.push_back(static_cast<CharType>(ch));
+                NextCharacter();
+                ch = PeekCharacter();
+            }
+            else
+                return false;
+
+            // The rest of the exponent
+            while (ch >= '0' && ch <= '9')
+            {
+                buf.push_back(static_cast<CharType>(ch));
+                NextCharacter();
+                ch = PeekCharacter();
+            }
+
+            // The peeked character is not a number, so we can break from the loop and construct the number
+            break;
+        }
+        else
+        {
+            // Not expected number character?
+            break;
+        }
+    };
+
+    buf.push_back('\0');
+    token.double_val = anystod(buf.data());
+    if (minus_sign)
+    {
+        token.double_val = -token.double_val;
+    }
+    token.kind = (JSON_Parser<CharType>::Token::TKN_NumberLiteral);
+
+    return true;
+}
+
+template<typename CharType>
+bool JSON_Parser<CharType>::CompleteComment(Token& token)
+{
+    // We already found a '/' character as the first of a token -- what kind of comment is it?
+
+    auto ch = NextCharacter();
+
+    if (ch == eof<CharType>() || (ch != '/' && ch != '*')) return false;
+
+    if (ch == '/')
+    {
+        // Line comment -- look for a newline or EOF to terminate.
+
+        ch = NextCharacter();
+
+        while (ch != eof<CharType>() && ch != '\n')
+        {
+            ch = NextCharacter();
+        }
+    }
+    else
+    {
+        // Block comment -- look for a terminating "*/" sequence.
+
+        ch = NextCharacter();
+
+        while (true)
+        {
+            if (ch == eof<CharType>()) return false;
+
+            if (ch == '*')
+            {
+                auto ch1 = PeekCharacter();
+
+                if (ch1 == eof<CharType>()) return false;
+
+                if (ch1 == '/')
+                {
+                    // Consume the character
+                    NextCharacter();
+                    break;
+                }
+
+                ch = ch1;
+            }
+
+            ch = NextCharacter();
+        }
+    }
+
+    token.kind = Token::TKN_Comment;
+
+    return true;
+}
+
+template<typename CharType>
+bool JSON_StringParser<CharType>::CompleteComment(typename JSON_Parser<CharType>::Token& token)
+{
+    // This function is specialized for the string parser, since we can be slightly more
+    // efficient in copying data from the input to the token: do a memcpy() rather than
+    // one character at a time.
+
+    auto ch = JSON_StringParser<CharType>::NextCharacter();
+
+    if (ch == eof<CharType>() || (ch != '/' && ch != '*')) return false;
+
+    if (ch == '/')
+    {
+        // Line comment -- look for a newline or EOF to terminate.
+
+        ch = JSON_StringParser<CharType>::NextCharacter();
+
+        while (ch != eof<CharType>() && ch != '\n')
+        {
+            ch = JSON_StringParser<CharType>::NextCharacter();
+        }
+    }
+    else
+    {
+        // Block comment -- look for a terminating "*/" sequence.
+
+        ch = JSON_StringParser<CharType>::NextCharacter();
+
+        while (true)
+        {
+            if (ch == eof<CharType>()) return false;
+
+            if (ch == '*')
+            {
+                ch = JSON_StringParser<CharType>::PeekCharacter();
+
+                if (ch == eof<CharType>()) return false;
+
+                if (ch == '/')
+                {
+                    // Consume the character
+                    JSON_StringParser<CharType>::NextCharacter();
+                    break;
+                }
+            }
+
+            ch = JSON_StringParser<CharType>::NextCharacter();
+        }
+    }
+
+    token.kind = JSON_Parser<CharType>::Token::TKN_Comment;
+
+    return true;
+}
+
+void convert_append_unicode_code_unit(JSON_Parser<utf16char>::Token& token, utf16string value)
+{
+    token.string_val.append(value);
+}
+void convert_append_unicode_code_unit(JSON_Parser<char>::Token& token, utf16string value)
+{
+    token.string_val.append(::utility::conversions::utf16_to_utf8(value));
+}
+void convert_append_unicode_code_unit(JSON_Parser<utf16char>::Token& token, utf16char value)
+{
+    token.string_val.push_back(value);
+}
+void convert_append_unicode_code_unit(JSON_Parser<char>::Token& token, utf16char value)
+{
+    utf16string utf16(reinterpret_cast<utf16char*>(&value), 1);
+    token.string_val.append(::utility::conversions::utf16_to_utf8(utf16));
+}
+
+template<typename CharType>
+int JSON_Parser<CharType>::convert_unicode_to_code_point()
+{
+    // A four-hexdigit Unicode character.
+    // Transform into a 16 bit code point.
+    int decoded = 0;
+    for (int i = 0; i < 4; ++i)
+    {
+        auto ch = NextCharacter();
+        int ch_int = static_cast<int>(ch);
+        if (ch_int < 0 || ch_int > 127) return -1;
+#ifdef _WIN32
+        const int isxdigitResult = _isxdigit_l(ch_int, utility::details::scoped_c_thread_locale::c_locale());
+#else
+        const int isxdigitResult = isxdigit(ch_int);
+#endif
+        if (!isxdigitResult) return -1;
+
+        int val = _hexval[static_cast<size_t>(ch_int)];
+
+        _ASSERTE(val != -1);
+
+        // Add the input char to the decoded number
+        decoded |= (val << (4 * (3 - i)));
+    }
+    return decoded;
+}
+
+#define H_SURROGATE_START 0xD800
+#define H_SURROGATE_END 0xDBFF
+
+template<typename CharType>
+inline bool JSON_Parser<CharType>::handle_unescape_char(Token& token)
+{
+    token.has_unescape_symbol = true;
+
+    // This function converts unescaped character pairs (e.g. "\t") into their ASCII or Unicode representations (e.g.
+    // tab sign) Also it handles \u + 4 hexadecimal digits
+    auto ch = NextCharacter();
+    switch (ch)
+    {
+        case '\"': token.string_val.push_back('\"'); return true;
+        case '\\': token.string_val.push_back('\\'); return true;
+        case '/': token.string_val.push_back('/'); return true;
+        case 'b': token.string_val.push_back('\b'); return true;
+        case 'f': token.string_val.push_back('\f'); return true;
+        case 'r': token.string_val.push_back('\r'); return true;
+        case 'n': token.string_val.push_back('\n'); return true;
+        case 't': token.string_val.push_back('\t'); return true;
+        case 'u':
+        {
+            int decoded = convert_unicode_to_code_point();
+            if (decoded == -1)
+            {
+                return false;
+            }
+
+            // handle multi-block characters that start with a high-surrogate
+            if (decoded >= H_SURROGATE_START && decoded <= H_SURROGATE_END)
+            {
+                // skip escape character '\u'
+                if (NextCharacter() != '\\' || NextCharacter() != 'u')
+                {
+                    return false;
+                }
+                int decoded2 = convert_unicode_to_code_point();
+
+                if (decoded2 == -1)
+                {
+                    return false;
+                }
+
+                utf16string compoundUTF16 = {static_cast<utf16char>(decoded), static_cast<utf16char>(decoded2)};
+                convert_append_unicode_code_unit(token, compoundUTF16);
+
+                return true;
+            }
+
+            // Construct the character based on the decoded number
+            convert_append_unicode_code_unit(token, static_cast<utf16char>(decoded));
+
+            return true;
+        }
+        default: return false;
+    }
+}
+
+template<typename CharType>
+bool JSON_Parser<CharType>::CompleteStringLiteral(Token& token)
+{
+    token.has_unescape_symbol = false;
+    auto ch = NextCharacter();
+    while (ch != '"')
+    {
+        if (ch == '\\')
+        {
+            handle_unescape_char(token);
+        }
+        else if (ch >= CharType(0x0) && ch < CharType(0x20))
+        {
+            return false;
+        }
+        else
+        {
+            if (ch == eof<CharType>()) return false;
+
+            token.string_val.push_back(static_cast<CharType>(ch));
+        }
+        ch = NextCharacter();
+    }
+
+    if (ch == '"')
+    {
+        token.kind = Token::TKN_StringLiteral;
+    }
+    else
+    {
+        return false;
+    }
+
+    return true;
+}
+
+template<typename CharType>
+bool JSON_StringParser<CharType>::CompleteStringLiteral(typename JSON_Parser<CharType>::Token& token)
+{
+    // This function is specialized for the string parser, since we can be slightly more
+    // efficient in copying data from the input to the token: do a memcpy() rather than
+    // one character at a time.
+
+    auto start = m_position;
+    token.has_unescape_symbol = false;
+
+    auto ch = JSON_StringParser<CharType>::NextCharacter();
+
+    while (ch != '"')
+    {
+        if (ch == eof<CharType>()) return false;
+
+        if (ch == '\\')
+        {
+            const size_t numChars = m_position - start - 1;
+            const size_t prevSize = token.string_val.size();
+            token.string_val.resize(prevSize + numChars);
+            memcpy(const_cast<CharType*>(token.string_val.c_str() + prevSize), start, numChars * sizeof(CharType));
+
+            if (!JSON_StringParser<CharType>::handle_unescape_char(token))
+            {
+                return false;
+            }
+
+            // Reset start position and continue.
+            start = m_position;
+        }
+        else if (ch >= CharType(0x0) && ch < CharType(0x20))
+        {
+            return false;
+        }
+
+        ch = JSON_StringParser<CharType>::NextCharacter();
+    }
+
+    const size_t numChars = m_position - start - 1;
+    const size_t prevSize = token.string_val.size();
+    token.string_val.resize(prevSize + numChars);
+    memcpy(const_cast<CharType*>(token.string_val.c_str() + prevSize), start, numChars * sizeof(CharType));
+
+    token.kind = JSON_Parser<CharType>::Token::TKN_StringLiteral;
+
+    return true;
+}
+
+template<typename CharType>
+void JSON_Parser<CharType>::GetNextToken(typename JSON_Parser<CharType>::Token& result)
+{
+try_again:
+    auto ch = EatWhitespace();
+
+    CreateToken(result, Token::TKN_EOF);
+
+    if (ch == eof<CharType>()) return;
+
+    switch (ch)
+    {
+        case '{':
+        case '[':
+        {
+            if (++m_currentParsingDepth > JSON_Parser<CharType>::maxParsingDepth)
+            {
+                SetErrorCode(result, json_error::nesting);
+                break;
+            }
+
+            typename JSON_Parser<CharType>::Token::Kind tk = ch == '{' ? Token::TKN_OpenBrace : Token::TKN_OpenBracket;
+            CreateToken(result, tk, result.start);
+            break;
+        }
+        case '}':
+        case ']':
+        {
+            if ((signed int)(--m_currentParsingDepth) < 0)
+            {
+                SetErrorCode(result, json_error::mismatched_brances);
+                break;
+            }
+
+            typename JSON_Parser<CharType>::Token::Kind tk =
+                ch == '}' ? Token::TKN_CloseBrace : Token::TKN_CloseBracket;
+            CreateToken(result, tk, result.start);
+            break;
+        }
+        case ',': CreateToken(result, Token::TKN_Comma, result.start); break;
+
+        case ':': CreateToken(result, Token::TKN_Colon, result.start); break;
+
+        case 't':
+            if (!CompleteKeywordTrue(result))
+            {
+                SetErrorCode(result, json_error::malformed_literal);
+            }
+            break;
+        case 'f':
+            if (!CompleteKeywordFalse(result))
+            {
+                SetErrorCode(result, json_error::malformed_literal);
+            }
+            break;
+        case 'n':
+            if (!CompleteKeywordNull(result))
+            {
+                SetErrorCode(result, json_error::malformed_literal);
+            }
+            break;
+        case '/':
+            if (!CompleteComment(result))
+            {
+                SetErrorCode(result, json_error::malformed_comment);
+                break;
+            }
+            // For now, we're ignoring comments.
+            goto try_again;
+        case '"':
+            if (!CompleteStringLiteral(result))
+            {
+                SetErrorCode(result, json_error::malformed_string_literal);
+            }
+            break;
+
+        case '-':
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (!CompleteNumberLiteral(static_cast<CharType>(ch), result))
+            {
+                SetErrorCode(result, json_error::malformed_numeric_literal);
+            }
+            break;
+        default: SetErrorCode(result, json_error::malformed_token); break;
+    }
+}
+
+template<typename CharType>
+std::unique_ptr<web::json::details::_Value> JSON_Parser<CharType>::_ParseObject(
+    typename JSON_Parser<CharType>::Token& tkn)
+{
+    auto obj = utility::details::make_unique<web::json::details::_Object>(g_keep_json_object_unsorted);
+    auto& elems = obj->m_object.m_elements;
+
+    GetNextToken(tkn);
+    if (tkn.m_error) goto error;
+
+    if (tkn.kind != JSON_Parser<CharType>::Token::TKN_CloseBrace)
+    {
+        while (true)
+        {
+            // State 1: New field or end of object, looking for field name or closing brace
+            std::basic_string<CharType> fieldName;
+            switch (tkn.kind)
+            {
+                case JSON_Parser<CharType>::Token::TKN_StringLiteral: fieldName = std::move(tkn.string_val); break;
+                default: goto error;
+            }
+
+            GetNextToken(tkn);
+            if (tkn.m_error) goto error;
+
+            // State 2: Looking for a colon.
+            if (tkn.kind != JSON_Parser<CharType>::Token::TKN_Colon) goto error;
+
+            GetNextToken(tkn);
+            if (tkn.m_error) goto error;
+
+                // State 3: Looking for an expression.
+#ifdef ENABLE_JSON_VALUE_VISUALIZER
+            auto fieldValue = _ParseValue(tkn);
+            auto type = fieldValue->type();
+            elems.emplace_back(utility::conversions::to_string_t(std::move(fieldName)),
+                               json::value(std::move(fieldValue), type));
+#else
+            elems.emplace_back(utility::conversions::to_string_t(std::move(fieldName)), json::value(_ParseValue(tkn)));
+#endif
+            if (tkn.m_error) goto error;
+
+            // State 4: Looking for a comma or a closing brace
+            switch (tkn.kind)
+            {
+                case JSON_Parser<CharType>::Token::TKN_Comma:
+                    GetNextToken(tkn);
+                    if (tkn.m_error) goto error;
+                    break;
+                case JSON_Parser<CharType>::Token::TKN_CloseBrace: goto done;
+                default: goto error;
+            }
+        }
+    }
+
+done:
+    GetNextToken(tkn);
+    if (tkn.m_error) return utility::details::make_unique<web::json::details::_Null>();
+
+    if (!g_keep_json_object_unsorted)
+    {
+        ::std::sort(elems.begin(), elems.end(), json::object::compare_pairs);
+    }
+
+    return std::unique_ptr<web::json::details::_Value>(obj.release());
+
+error:
+    if (!tkn.m_error)
+    {
+        SetErrorCode(tkn, json_error::malformed_object_literal);
+    }
+    return utility::details::make_unique<web::json::details::_Null>();
+}
+
+template<typename CharType>
+std::unique_ptr<web::json::details::_Value> JSON_Parser<CharType>::_ParseArray(
+    typename JSON_Parser<CharType>::Token& tkn)
+{
+    GetNextToken(tkn);
+    if (tkn.m_error) return utility::details::make_unique<web::json::details::_Null>();
+
+    auto result = utility::details::make_unique<web::json::details::_Array>();
+
+    if (tkn.kind != JSON_Parser<CharType>::Token::TKN_CloseBracket)
+    {
+        while (true)
+        {
+            // State 1: Looking for an expression.
+            result->m_array.m_elements.emplace_back(ParseValue(tkn));
+            if (tkn.m_error) return utility::details::make_unique<web::json::details::_Null>();
+
+            // State 4: Looking for a comma or a closing bracket
+            switch (tkn.kind)
+            {
+                case JSON_Parser<CharType>::Token::TKN_Comma:
+                    GetNextToken(tkn);
+                    if (tkn.m_error) return utility::details::make_unique<web::json::details::_Null>();
+                    break;
+                case JSON_Parser<CharType>::Token::TKN_CloseBracket:
+                    GetNextToken(tkn);
+                    if (tkn.m_error) return utility::details::make_unique<web::json::details::_Null>();
+                    return std::unique_ptr<web::json::details::_Value>(result.release());
+                default:
+                    SetErrorCode(tkn, json_error::malformed_array_literal);
+                    return utility::details::make_unique<web::json::details::_Null>();
+            }
+        }
+    }
+
+    GetNextToken(tkn);
+    if (tkn.m_error) return utility::details::make_unique<web::json::details::_Null>();
+
+    return std::unique_ptr<web::json::details::_Value>(result.release());
+}
+
+template<typename CharType>
+std::unique_ptr<web::json::details::_Value> JSON_Parser<CharType>::_ParseValue(
+    typename JSON_Parser<CharType>::Token& tkn)
+{
+    typedef std::unique_ptr<web::json::details::_Value> Vptr;
+    switch (tkn.kind)
+    {
+        case JSON_Parser<CharType>::Token::TKN_OpenBrace:
+        {
+            return _ParseObject(tkn);
+        }
+        case JSON_Parser<CharType>::Token::TKN_OpenBracket:
+        {
+            return _ParseArray(tkn);
+        }
+        case JSON_Parser<CharType>::Token::TKN_StringLiteral:
+        {
+            Vptr value = utility::details::make_unique<web::json::details::_String>(std::move(tkn.string_val),
+                                                                                    tkn.has_unescape_symbol);
+            GetNextToken(tkn);
+            if (tkn.m_error) return utility::details::make_unique<web::json::details::_Null>();
+            return value;
+        }
+        case JSON_Parser<CharType>::Token::TKN_IntegerLiteral:
+        {
+            Vptr value;
+            if (tkn.signed_number)
+                value = utility::details::make_unique<web::json::details::_Number>(tkn.int64_val);
+            else
+                value = utility::details::make_unique<web::json::details::_Number>(tkn.uint64_val);
+
+            GetNextToken(tkn);
+            if (tkn.m_error) return utility::details::make_unique<web::json::details::_Null>();
+            return value;
+        }
+        case JSON_Parser<CharType>::Token::TKN_NumberLiteral:
+        {
+            Vptr value = utility::details::make_unique<web::json::details::_Number>(tkn.double_val);
+            GetNextToken(tkn);
+            if (tkn.m_error) return utility::details::make_unique<web::json::details::_Null>();
+            return value;
+        }
+        case JSON_Parser<CharType>::Token::TKN_BooleanLiteral:
+        {
+            Vptr value = utility::details::make_unique<web::json::details::_Boolean>(tkn.boolean_val);
+            GetNextToken(tkn);
+            if (tkn.m_error) return utility::details::make_unique<web::json::details::_Null>();
+            return value;
+        }
+        case JSON_Parser<CharType>::Token::TKN_NullLiteral:
+        {
+            GetNextToken(tkn);
+            // Returning a null value whether or not an error occurred.
+            return utility::details::make_unique<web::json::details::_Null>();
+        }
+        default:
+        {
+            SetErrorCode(tkn, json_error::malformed_token);
+            return utility::details::make_unique<web::json::details::_Null>();
+        }
+    }
+}
+
+} // namespace details
+} // namespace json
+} // namespace web
+
+template<typename CharType>
+static web::json::value _parse_stream(std::basic_istream<CharType>& stream)
+{
+    web::json::details::JSON_StreamParser<CharType> parser(stream);
+    typename web::json::details::JSON_Parser<CharType>::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<CharType>::Token::TKN_EOF)
+    {
+        web::json::details::CreateException(tkn,
+                                            _XPLATSTR("Left-over characters in stream after parsing a JSON value"));
+    }
+    return value;
+}
+
+template<typename CharType>
+static web::json::value _parse_stream(std::basic_istream<CharType>& stream, std::error_code& error)
+{
+    web::json::details::JSON_StreamParser<CharType> parser(stream);
+    typename web::json::details::JSON_Parser<CharType>::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<CharType>::Token::TKN_EOF)
+    {
+        web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream);
+    }
+
+    error = std::move(tkn.m_error);
+    return returnObject;
+}
+
+template<typename CharType>
+static web::json::value _parse_string(const std::basic_string<CharType>& str)
+{
+    web::json::details::JSON_StringParser<CharType> parser(str);
+    typename web::json::details::JSON_Parser<CharType>::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<CharType>::Token::TKN_EOF)
+    {
+        web::json::details::CreateException(tkn,
+                                            _XPLATSTR("Left-over characters in stream after parsing a JSON value"));
+    }
+    return value;
+}
+
+template<typename CharType>
+static web::json::value _parse_string(const std::basic_string<CharType>& str, std::error_code& error)
+{
+    web::json::details::JSON_StringParser<CharType> parser(str);
+    typename web::json::details::JSON_Parser<CharType>::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<CharType>::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;
+}
+
+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)
+{
+    return _parse_string(str, error);
+}
+
+web::json::value web::json::value::parse(utility::istream_t& stream) { return _parse_stream(stream); }
+
+web::json::value web::json::value::parse(utility::istream_t& stream, std::error_code& error)
+{
+    return _parse_stream(stream, error);
+}
+
+#ifdef _WIN32
+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_stream(stream, error);
+}
+#endif
diff --git a/Release/src/json/json_serialization.cpp b/Release/src/json/json_serialization.cpp
new file mode 100644 (file)
index 0000000..0191b34
--- /dev/null
@@ -0,0 +1,256 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * HTTP Library: JSON parser and writer
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <stdio.h>
+
+#ifndef _WIN32
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+#include <inttypes.h>
+#endif
+
+using namespace web;
+using namespace web::json;
+using namespace utility;
+using namespace utility::conversions;
+
+//
+// JSON Serialization
+//
+
+#ifdef _WIN32
+void web::json::value::serialize(std::ostream& stream) const
+{
+    // This has better performance than writing directly to stream.
+    std::string str;
+    m_value->serialize_impl(str);
+    stream << str;
+}
+void web::json::value::format(std::basic_string<wchar_t>& string) const { m_value->format(string); }
+#endif
+
+void web::json::value::serialize(utility::ostream_t& stream) const
+{
+#ifndef _WIN32
+    utility::details::scoped_c_thread_locale locale;
+#endif
+
+    // This has better performance than writing directly to stream.
+    utility::string_t str;
+    m_value->serialize_impl(str);
+    stream << str;
+}
+
+void web::json::value::format(std::basic_string<char>& string) const { m_value->format(string); }
+
+template<typename CharType>
+void web::json::details::append_escape_string(std::basic_string<CharType>& str,
+                                              const std::basic_string<CharType>& escaped)
+{
+    for (const auto& ch : escaped)
+    {
+        switch (ch)
+        {
+            case '\"':
+                str += '\\';
+                str += '\"';
+                break;
+            case '\\':
+                str += '\\';
+                str += '\\';
+                break;
+            case '\b':
+                str += '\\';
+                str += 'b';
+                break;
+            case '\f':
+                str += '\\';
+                str += 'f';
+                break;
+            case '\r':
+                str += '\\';
+                str += 'r';
+                break;
+            case '\n':
+                str += '\\';
+                str += 'n';
+                break;
+            case '\t':
+                str += '\\';
+                str += 't';
+                break;
+            default:
+
+                // If a control character then must unicode escaped.
+                if (ch >= 0 && ch <= 0x1F)
+                {
+                    static const std::array<CharType, 16> intToHex = {
+                        {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}};
+                    str += '\\';
+                    str += 'u';
+                    str += '0';
+                    str += '0';
+                    str += intToHex[(ch & 0xF0) >> 4];
+                    str += intToHex[ch & 0x0F];
+                }
+                else
+                {
+                    str += ch;
+                }
+        }
+    }
+}
+
+void web::json::details::format_string(const utility::string_t& key, utility::string_t& str)
+{
+    str.push_back('"');
+    append_escape_string(str, key);
+    str.push_back('"');
+}
+
+#ifdef _WIN32
+void web::json::details::format_string(const utility::string_t& key, std::string& str)
+{
+    str.push_back('"');
+    append_escape_string(str, utility::conversions::to_utf8string(key));
+    str.push_back('"');
+}
+#endif
+
+void web::json::details::_String::format(std::basic_string<char>& str) const
+{
+    str.push_back('"');
+
+    if (m_has_escape_char)
+    {
+        append_escape_string(str, utility::conversions::to_utf8string(m_string));
+    }
+    else
+    {
+        str.append(utility::conversions::to_utf8string(m_string));
+    }
+
+    str.push_back('"');
+}
+
+void web::json::details::_Number::format(std::basic_string<char>& stream) const
+{
+    if (m_number.m_type != number::type::double_type)
+    {
+        // #digits + 1 to avoid loss + 1 for the sign + 1 for null terminator.
+        const size_t tempSize = std::numeric_limits<uint64_t>::digits10 + 3;
+        char tempBuffer[tempSize];
+
+#ifdef _WIN32
+        // This can be improved performance-wise if we implement our own routine
+        if (m_number.m_type == number::type::signed_type)
+            _i64toa_s(m_number.m_intval, tempBuffer, tempSize, 10);
+        else
+            _ui64toa_s(m_number.m_uintval, tempBuffer, tempSize, 10);
+
+        const auto numChars = strnlen_s(tempBuffer, tempSize);
+#else
+        int numChars;
+        if (m_number.m_type == number::type::signed_type)
+            numChars = snprintf(tempBuffer, tempSize, "%" PRId64, m_number.m_intval);
+        else
+            numChars = snprintf(tempBuffer, tempSize, "%" PRIu64, m_number.m_uintval);
+#endif
+        stream.append(tempBuffer, numChars);
+    }
+    else
+    {
+        // #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null
+        // terminator
+        const size_t tempSize = std::numeric_limits<double>::digits10 + 10;
+        char tempBuffer[tempSize];
+#ifdef _WIN32
+        const auto numChars = _sprintf_s_l(tempBuffer,
+                                           tempSize,
+                                           "%.*g",
+                                           utility::details::scoped_c_thread_locale::c_locale(),
+                                           std::numeric_limits<double>::digits10 + 2,
+                                           m_number.m_value);
+#else
+        const auto numChars =
+            snprintf(tempBuffer, tempSize, "%.*g", std::numeric_limits<double>::digits10 + 2, m_number.m_value);
+#endif
+        stream.append(tempBuffer, numChars);
+    }
+}
+
+#ifdef _WIN32
+
+void web::json::details::_String::format(std::basic_string<wchar_t>& str) const
+{
+    str.push_back(L'"');
+
+    if (m_has_escape_char)
+    {
+        append_escape_string(str, m_string);
+    }
+    else
+    {
+        str.append(m_string);
+    }
+
+    str.push_back(L'"');
+}
+
+void web::json::details::_Number::format(std::basic_string<wchar_t>& stream) const
+{
+    if (m_number.m_type != number::type::double_type)
+    {
+        // #digits + 1 to avoid loss + 1 for the sign + 1 for null terminator.
+        const size_t tempSize = std::numeric_limits<uint64_t>::digits10 + 3;
+        wchar_t tempBuffer[tempSize];
+
+        if (m_number.m_type == number::type::signed_type)
+            _i64tow_s(m_number.m_intval, tempBuffer, tempSize, 10);
+        else
+            _ui64tow_s(m_number.m_uintval, tempBuffer, tempSize, 10);
+
+        stream.append(tempBuffer, wcsnlen_s(tempBuffer, tempSize));
+    }
+    else
+    {
+        // #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null
+        // terminator
+        const size_t tempSize = std::numeric_limits<double>::digits10 + 10;
+        wchar_t tempBuffer[tempSize];
+        const int numChars = _swprintf_s_l(tempBuffer,
+                                           tempSize,
+                                           L"%.*g",
+                                           utility::details::scoped_c_thread_locale::c_locale(),
+                                           std::numeric_limits<double>::digits10 + 2,
+                                           m_number.m_value);
+        stream.append(tempBuffer, numChars);
+    }
+}
+
+#endif
+
+const utility::string_t& web::json::details::_String::as_string() const { return m_string; }
+
+const utility::string_t& web::json::value::as_string() const { return m_value->as_string(); }
+
+utility::string_t json::value::serialize() const
+{
+#ifndef _WIN32
+    utility::details::scoped_c_thread_locale locale;
+#endif
+    return m_value->to_string();
+}
diff --git a/Release/src/pch/stdafx.cpp b/Release/src/pch/stdafx.cpp
new file mode 100644 (file)
index 0000000..e4c61a1
--- /dev/null
@@ -0,0 +1,13 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ **/
+// stdafx.cpp :
+// Include the standard header and generate the precompiled header.
+
+#include "stdafx.h"
diff --git a/Release/src/pch/stdafx.h b/Release/src/pch/stdafx.h
new file mode 100644 (file)
index 0000000..5c398e1
--- /dev/null
@@ -0,0 +1,119 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Pre-compiled headers
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+#endif
+
+#ifdef _WIN32
+// use the debug version of the CRT if _DEBUG is defined
+#ifdef _DEBUG
+#define _CRTDBG_MAP_ALLOC
+#include <crtdbg.h>
+#endif // _DEBUG
+
+#include <SDKDDKVer.h>
+#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 <objbase.h>
+
+#include <windows.h>
+
+// Windows Header Files:
+#ifndef __cplusplus_winrt
+#include <winhttp.h>
+#endif // !__cplusplus_winrt
+
+#else // LINUX or APPLE
+#define __STDC_LIMIT_MACROS
+#include "pthread.h"
+#include <atomic>
+#include <cstdint>
+#include <signal.h>
+#include <stdint.h>
+#include <string>
+#if (defined(ANDROID) || defined(__ANDROID__)) && !defined(_LIBCPP_VERSION)
+// Boost doesn't recognize libstdcpp on top of clang correctly
+#include "boost/config.hpp"
+#include "boost/config/stdlib/libstdcpp3.hpp"
+#undef BOOST_NO_CXX11_SMART_PTR
+#undef BOOST_NO_CXX11_NULLPTR
+#endif
+#include "boost/bind/bind.hpp"
+#include "boost/thread/condition_variable.hpp"
+#include "boost/thread/mutex.hpp"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#endif // _WIN32
+
+// Macro indicating the C++ Rest SDK product itself is being built.
+// This is to help track how many developers are directly building from source themselves.
+#define _CASA_BUILD_FROM_SRC
+
+#include "cpprest/details/basic_types.h"
+#include "cpprest/details/cpprest_compat.h"
+#include "cpprest/version.h"
+#include "pplx/pplxtasks.h"
+#include <algorithm>
+#include <array>
+#include <assert.h>
+#include <atomic>
+#include <exception>
+#include <memory>
+#include <mutex>
+#include <stdlib.h>
+#include <vector>
+
+// json
+#include "cpprest/json.h"
+
+// uri
+#include "cpprest/base_uri.h"
+
+// utilities
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/details/web_utilities.h"
+
+// http
+#include "cpprest/details/http_helpers.h"
+#include "cpprest/http_client.h"
+#include "cpprest/http_headers.h"
+#include "cpprest/http_msg.h"
+
+// oauth
+#if !defined(_WIN32) || _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#include "cpprest/oauth1.h"
+#endif
+#include "cpprest/oauth2.h"
+
+#if !defined(__cplusplus_winrt)
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#include "cpprest/details/http_server.h"
+#include "cpprest/details/http_server_api.h"
+#include "cpprest/http_listener.h"
+#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
diff --git a/Release/src/pplx/pplx.cpp b/Release/src/pplx/pplx.cpp
new file mode 100644 (file)
index 0000000..cdb28b3
--- /dev/null
@@ -0,0 +1,128 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Parallel Patterns Library implementation (common code across platforms)
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if !defined(_WIN32) || CPPREST_FORCE_PPLX
+#include "pplx/pplx.h"
+#include <atomic>
+
+namespace pplx
+{
+namespace details
+{
+/// <summary>
+/// Spin lock to allow for locks to be used in global scope
+/// </summary>
+class _Spin_lock
+{
+public:
+    _Spin_lock() : _M_lock() {}
+
+    void lock()
+    {
+        while (_M_lock.test_and_set())
+        {
+            pplx::details::platform::YieldExecution();
+        }
+    }
+
+    void unlock() { _M_lock.clear(); }
+
+private:
+    std::atomic_flag _M_lock;
+};
+
+typedef ::pplx::scoped_lock<_Spin_lock> _Scoped_spin_lock;
+} // namespace details
+
+static struct _pplx_g_sched_t
+{
+    typedef std::shared_ptr<pplx::scheduler_interface> sched_ptr;
+
+    _pplx_g_sched_t() { m_state.store(post_ctor, std::memory_order_relaxed); }
+
+    ~_pplx_g_sched_t() { m_state.store(post_dtor, std::memory_order_relaxed); }
+
+    sched_ptr get_scheduler()
+    {
+        sched_ptr result;
+        switch (m_state.load(std::memory_order_relaxed))
+        {
+            case post_ctor:
+                // This is the 99.9% case.
+                {
+                    ::pplx::details::_Scoped_spin_lock lock(m_spinlock);
+                    if (!m_scheduler)
+                    {
+                        m_scheduler = std::make_shared<::pplx::default_scheduler_t>();
+                    }
+
+                    result = m_scheduler;
+                } // unlock
+
+                break;
+            default:
+                // This case means the global m_scheduler is not available.
+                // We spin off an individual scheduler instead.
+                result = std::make_shared<::pplx::default_scheduler_t>();
+                break;
+        }
+
+        return result;
+    }
+
+    void set_scheduler(sched_ptr scheduler)
+    {
+        const auto localState = m_state.load(std::memory_order_relaxed);
+        if (localState == pre_ctor || localState == post_dtor)
+        {
+            throw invalid_operation("Scheduler cannot be initialized now");
+        }
+
+        ::pplx::details::_Scoped_spin_lock lock(m_spinlock);
+
+        if (m_scheduler)
+        {
+            throw invalid_operation("Scheduler is already initialized");
+        }
+
+        m_scheduler = std::move(scheduler);
+    }
+
+    enum m_state_values
+    {
+        pre_ctor,
+        post_ctor,
+        post_dtor
+    };
+
+private:
+    std::atomic<m_state_values> m_state;
+    pplx::details::_Spin_lock m_spinlock;
+    sched_ptr m_scheduler;
+} _pplx_g_sched;
+
+_PPLXIMP std::shared_ptr<pplx::scheduler_interface> _pplx_cdecl get_ambient_scheduler()
+{
+    return _pplx_g_sched.get_scheduler();
+}
+
+_PPLXIMP void _pplx_cdecl set_ambient_scheduler(std::shared_ptr<pplx::scheduler_interface> _Scheduler)
+{
+    _pplx_g_sched.set_scheduler(std::move(_Scheduler));
+}
+
+} // namespace pplx
+
+#endif
diff --git a/Release/src/pplx/pplxapple.cpp b/Release/src/pplx/pplxapple.cpp
new file mode 100644 (file)
index 0000000..6eff5e7
--- /dev/null
@@ -0,0 +1,50 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Apple-specific pplx implementations
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "pplx/pplx.h"
+#include <dispatch/dispatch.h>
+#include <errno.h>
+#include <libkern/OSAtomic.h>
+#include <pthread.h>
+#include <sys/time.h>
+
+// DEVNOTE:
+// The use of mutexes is suboptimal for synchronization of task execution.
+// Given that scheduler implementations should use GCD queues, there are potentially better mechanisms available to
+// coordinate tasks (such as dispatch groups).
+
+namespace pplx
+{
+namespace details
+{
+namespace platform
+{
+_PPLXIMP long GetCurrentThreadId()
+{
+    pthread_t threadId = pthread_self();
+    return (long)threadId;
+}
+
+void YieldExecution() { sleep(0); }
+
+} // namespace platform
+
+void apple_scheduler::schedule(TaskProc_t proc, void* param)
+{
+    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+    dispatch_async_f(queue, param, proc);
+}
+
+} // namespace details
+
+} // namespace pplx
diff --git a/Release/src/pplx/pplxlinux.cpp b/Release/src/pplx/pplxlinux.cpp
new file mode 100644 (file)
index 0000000..630a9e4
--- /dev/null
@@ -0,0 +1,43 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Parallel Patterns Library - Linux version
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "pplx/pplx.h"
+#include "pplx/threadpool.h"
+#include "sys/syscall.h"
+#include <thread>
+
+#ifdef _WIN32
+#error "ERROR: This file should only be included in non-windows Build"
+#endif
+
+namespace pplx
+{
+namespace details
+{
+namespace platform
+{
+_PPLXIMP long GetCurrentThreadId() { return reinterpret_cast<long>(reinterpret_cast<void*>(pthread_self())); }
+
+_PPLXIMP void YieldExecution() { std::this_thread::yield(); }
+} // namespace platform
+
+_PPLXIMP void linux_scheduler::schedule(TaskProc_t proc, void* param)
+{
+    crossplat::threadpool::shared_instance().service().post(boost::bind(proc, param));
+}
+
+} // namespace details
+
+} // namespace pplx
diff --git a/Release/src/pplx/pplxwin.cpp b/Release/src/pplx/pplxwin.cpp
new file mode 100644 (file)
index 0000000..e25d9ac
--- /dev/null
@@ -0,0 +1,260 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Windows specific implementation of PPL constructs
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if !defined(_WIN32) || CPPREST_FORCE_PPLX
+
+#include "pplx/pplxwin.h"
+
+// Disable false alarm code analysis warning
+#pragma warning(disable : 26165 26110)
+namespace pplx
+{
+namespace details
+{
+namespace platform
+{
+_PPLXIMP long __cdecl GetCurrentThreadId() { return (long)(::GetCurrentThreadId()); }
+
+_PPLXIMP void __cdecl YieldExecution() { YieldProcessor(); }
+
+_PPLXIMP size_t __cdecl CaptureCallstack(void** stackData, size_t skipFrames, size_t captureFrames)
+{
+    (stackData);
+    (skipFrames);
+    (captureFrames);
+
+    size_t capturedFrames = 0;
+    // RtlCaptureSTackBackTrace is not available in MSDK, so we only call it under Desktop or _DEBUG MSDK.
+    //  For MSDK unsupported version, we will return zero frame number.
+#if !defined(__cplusplus_winrt)
+    capturedFrames = RtlCaptureStackBackTrace(
+        static_cast<DWORD>(skipFrames + 1), static_cast<DWORD>(captureFrames), stackData, nullptr);
+#endif // !__cplusplus_winrt
+    return capturedFrames;
+}
+
+#if defined(__cplusplus_winrt)
+volatile long s_asyncId = 0;
+
+_PPLXIMP unsigned int __cdecl GetNextAsyncId() { return static_cast<unsigned int>(_InterlockedIncrement(&s_asyncId)); }
+
+#endif // defined(__cplusplus_winrt)
+
+void InitializeCriticalSection(LPCRITICAL_SECTION _cs)
+{
+#ifndef __cplusplus_winrt
+    // InitializeCriticalSection can cause STATUS_NO_MEMORY see C28125
+    __try
+    {
+        ::InitializeCriticalSection(_cs);
+    }
+    __except (GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+    {
+        throw ::std::bad_alloc();
+    }
+#else  // ^^^ __cplusplus_winrt ^^^ // vvv !__cplusplus_winrt vvv
+    InitializeCriticalSectionEx(_cs, 0, 0);
+#endif // __cplusplus_winrt
+}
+
+} // namespace platform
+
+//
+// Event implementation
+//
+_PPLXIMP event_impl::event_impl()
+{
+    static_assert(sizeof(HANDLE) <= sizeof(_M_impl), "HANDLE version mismatch");
+
+#ifndef __cplusplus_winrt
+    _M_impl = CreateEvent(NULL, true, false, NULL);
+#else
+    _M_impl = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
+#endif // !__cplusplus_winrt
+
+    if (_M_impl != NULL)
+    {
+        ResetEvent(static_cast<HANDLE>(_M_impl));
+    }
+}
+
+_PPLXIMP event_impl::~event_impl() { CloseHandle(static_cast<HANDLE>(_M_impl)); }
+
+_PPLXIMP void event_impl::set() { SetEvent(static_cast<HANDLE>(_M_impl)); }
+
+_PPLXIMP void event_impl::reset() { ResetEvent(static_cast<HANDLE>(_M_impl)); }
+
+_PPLXIMP unsigned int event_impl::wait(unsigned int timeout)
+{
+    DWORD waitTime = (timeout == event_impl::timeout_infinite) ? INFINITE : (DWORD)timeout;
+    DWORD status = WaitForSingleObjectEx(static_cast<HANDLE>(_M_impl), waitTime, 0);
+    _ASSERTE((status == WAIT_OBJECT_0) || (waitTime != INFINITE));
+
+    return (status == WAIT_OBJECT_0) ? 0 : event_impl::timeout_infinite;
+}
+
+//
+// critical_section implementation
+//
+// TFS# 612702 -- this implementation is unnecessarily recursive. See bug for details.
+_PPLXIMP critical_section_impl::critical_section_impl()
+{
+    static_assert(sizeof(CRITICAL_SECTION) <= sizeof(_M_impl), "CRITICAL_SECTION version mismatch");
+
+    platform::InitializeCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&_M_impl));
+}
+
+_PPLXIMP critical_section_impl::~critical_section_impl()
+{
+    DeleteCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&_M_impl));
+}
+
+_PPLXIMP void critical_section_impl::lock() { EnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&_M_impl)); }
+
+_PPLXIMP void critical_section_impl::unlock() { LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&_M_impl)); }
+
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+//
+// reader_writer_lock implementation
+//
+_PPLXIMP reader_writer_lock_impl::reader_writer_lock_impl() : m_locked_exclusive(false)
+{
+    static_assert(sizeof(SRWLOCK) <= sizeof(_M_impl), "SRWLOCK version mismatch");
+    InitializeSRWLock(reinterpret_cast<PSRWLOCK>(&_M_impl));
+}
+
+_PPLXIMP void reader_writer_lock_impl::lock()
+{
+    AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&_M_impl));
+    m_locked_exclusive = true;
+}
+
+_PPLXIMP void reader_writer_lock_impl::lock_read() { AcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(&_M_impl)); }
+
+_PPLXIMP void reader_writer_lock_impl::unlock()
+{
+    if (m_locked_exclusive)
+    {
+        m_locked_exclusive = false;
+        ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&_M_impl));
+    }
+    else
+    {
+        ReleaseSRWLockShared(reinterpret_cast<PSRWLOCK>(&_M_impl));
+    }
+}
+#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
+
+//
+// scheduler implementation
+//
+#if defined(__cplusplus_winrt)
+
+_PPLXIMP void windows_scheduler::schedule(TaskProc_t proc, _In_ void* param)
+{
+    auto workItemHandler = ref new Windows::System::Threading::WorkItemHandler(
+        [proc, param](Windows::Foundation::IAsyncAction ^) { proc(param); });
+
+    Windows::System::Threading::ThreadPool::RunAsync(workItemHandler);
+}
+#else // ^^^ __cplusplus_winrt ^^^ // vvv !__cplusplus_winrt vvv
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+struct _Scheduler_Param
+{
+    TaskProc_t m_proc;
+    void* m_param;
+
+    _Scheduler_Param(TaskProc_t proc, _In_ void* param) : m_proc(proc), m_param(param) {}
+
+    static DWORD CALLBACK DefaultWorkCallback(LPVOID lpParameter)
+    {
+        auto schedulerParam = (_Scheduler_Param*)(lpParameter);
+
+        schedulerParam->m_proc(schedulerParam->m_param);
+
+        delete schedulerParam;
+
+        return 1;
+    }
+};
+
+_PPLXIMP void windows_scheduler::schedule(TaskProc_t proc, _In_ void* param)
+{
+    auto schedulerParam = new _Scheduler_Param(proc, param);
+    auto work = QueueUserWorkItem(_Scheduler_Param::DefaultWorkCallback, schedulerParam, WT_EXECUTELONGFUNCTION);
+
+    if (!work)
+    {
+        delete schedulerParam;
+        throw utility::details::create_system_error(GetLastError());
+    }
+}
+#else  // ^^^ _WIN32_WINNT < _WIN32_WINNT_VISTA ^^^ // vvv _WIN32_WINNT >= _WIN32_WINNT_VISTA vvv
+struct _Scheduler_Param
+{
+    TaskProc_t m_proc;
+    void* m_param;
+
+    _Scheduler_Param(TaskProc_t proc, _In_ void* param) : m_proc(proc), m_param(param) {}
+
+    static void CALLBACK DefaultWorkCallback(PTP_CALLBACK_INSTANCE, PVOID pContext, PTP_WORK)
+    {
+        auto schedulerParam = (_Scheduler_Param*)(pContext);
+
+        schedulerParam->m_proc(schedulerParam->m_param);
+
+        delete schedulerParam;
+    }
+};
+
+_PPLXIMP void windows_scheduler::schedule(TaskProc_t proc, _In_ void* param)
+{
+    auto schedulerParam = new _Scheduler_Param(proc, param);
+    auto work = CreateThreadpoolWork(_Scheduler_Param::DefaultWorkCallback, schedulerParam, NULL);
+
+    if (work == nullptr)
+    {
+        delete schedulerParam;
+        throw utility::details::create_system_error(GetLastError());
+    }
+
+    SubmitThreadpoolWork(work);
+    CloseThreadpoolWork(work);
+}
+#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
+
+#endif // __cplusplus_winrt
+} // namespace details
+
+} // namespace pplx
+
+#else // ^^^ !defined(_WIN32) || CPPREST_FORCE_PPLX ^^^ // vvv defined(_WIN32) && !CPPREST_FORCE_PPLX vvv
+namespace Concurrency
+{
+void __cdecl set_cpprestsdk_ambient_scheduler(const std::shared_ptr<scheduler_interface>& _Scheduler)
+{
+    pplx::set_ambient_scheduler(_Scheduler);
+}
+
+const std::shared_ptr<scheduler_interface>& __cdecl get_cpprestsdk_ambient_scheduler()
+{
+    const auto& tmp = pplx::get_ambient_scheduler(); // putting this in a temporary reference variable to workaround
+                                                     // VS2013 compiler bugs
+    return tmp;
+}
+
+} // namespace Concurrency
+#endif
diff --git a/Release/src/pplx/threadpool.cpp b/Release/src/pplx/threadpool.cpp
new file mode 100644 (file)
index 0000000..ba38a1a
--- /dev/null
@@ -0,0 +1,235 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ **/
+#include "stdafx.h"
+
+#if !defined(CPPREST_EXCLUDE_WEBSOCKETS) || !defined(_WIN32)
+#include "pplx/threadpool.h"
+#include <boost/asio/detail/thread.hpp>
+#include <new>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#if defined(__ANDROID__)
+#include <android/log.h>
+#include <jni.h>
+#endif
+
+namespace
+{
+#if defined(__ANDROID__)
+// This pointer will be 0-initialized by default (at load time).
+static void abort_if_no_jvm()
+{
+    if (crossplat::JVM == nullptr)
+    {
+        __android_log_print(ANDROID_LOG_ERROR,
+                            "CPPRESTSDK",
+                            "%s",
+                            "The CppREST SDK must be initialized before first use on android: "
+                            "https://github.com/Microsoft/cpprestsdk/wiki/How-to-build-for-Android");
+        std::abort();
+    }
+}
+#endif // __ANDROID__
+
+struct threadpool_impl final : crossplat::threadpool
+{
+    threadpool_impl(size_t n) : crossplat::threadpool(n), m_work(m_service)
+    {
+        for (size_t i = 0; i < n; i++)
+            add_thread();
+    }
+
+    threadpool_impl(const threadpool_impl&) = delete;
+    threadpool_impl& operator=(const threadpool_impl&) = delete;
+
+    ~threadpool_impl()
+    {
+        m_service.stop();
+        for (auto iter = m_threads.begin(); iter != m_threads.end(); ++iter)
+        {
+            (*iter)->join();
+        }
+    }
+
+    threadpool_impl& get_shared() { return *this; }
+
+private:
+    void add_thread()
+    {
+        m_threads.push_back(
+            std::unique_ptr<boost::asio::detail::thread>(new boost::asio::detail::thread([&] { thread_start(this); })));
+    }
+
+#if defined(__ANDROID__)
+    static void detach_from_java(void*) { crossplat::JVM.load()->DetachCurrentThread(); }
+#endif // __ANDROID__
+
+    static void* thread_start(void* arg) CPPREST_NOEXCEPT
+    {
+#if defined(__ANDROID__)
+        // Calling get_jvm_env() here forces the thread to be attached.
+        crossplat::get_jvm_env();
+        pthread_cleanup_push(detach_from_java, nullptr);
+#endif // __ANDROID__
+        threadpool_impl* _this = reinterpret_cast<threadpool_impl*>(arg);
+        _this->m_service.run();
+#if defined(__ANDROID__)
+        pthread_cleanup_pop(true);
+#endif // __ANDROID__
+        return arg;
+    }
+
+    std::vector<std::unique_ptr<boost::asio::detail::thread>> m_threads;
+    boost::asio::io_service::work m_work;
+};
+
+#if defined(_WIN32)
+struct shared_threadpool
+{
+#if defined(_MSC_VER) && _MSC_VER < 1900
+    std::aligned_storage<sizeof(threadpool_impl)>::type shared_storage;
+
+    threadpool_impl& get_shared() { return reinterpret_cast<threadpool_impl&>(shared_storage); }
+
+    shared_threadpool(size_t n) { ::new (static_cast<void*>(&shared_storage)) threadpool_impl(n); }
+#else  // ^^^ VS2013 ^^^ // vvv everything else vvv
+    union {
+        threadpool_impl shared_storage;
+    };
+
+    threadpool_impl& get_shared() { return shared_storage; }
+
+    shared_threadpool(size_t n) : shared_storage(n) {}
+#endif // defined(_MSC_VER) && _MSC_VER < 1900
+
+    ~shared_threadpool()
+    {
+        // if linked into a DLL, the threadpool shared instance will be
+        // destroyed at DLL_PROCESS_DETACH, at which stage joining threads
+        // causes deadlock, hence this dance
+        bool terminate_threads = boost::asio::detail::thread::terminate_threads();
+        boost::asio::detail::thread::set_terminate_threads(true);
+        get_shared().~threadpool_impl();
+        boost::asio::detail::thread::set_terminate_threads(terminate_threads);
+    }
+};
+
+typedef shared_threadpool platform_shared_threadpool;
+#else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv //
+typedef threadpool_impl platform_shared_threadpool;
+#endif
+
+namespace
+{
+template<class T>
+struct uninitialized
+{
+#if defined(_MSC_VER) && _MSC_VER < 1900
+    typename std::aligned_storage<sizeof(T)>::type storage;
+
+    ~uninitialized()
+    {
+        if (initialized)
+        {
+            reinterpret_cast<T&>(storage).~T();
+        }
+    }
+#else  // ^^^ VS2013 ^^^ // vvv everything else vvv
+    union {
+        T storage;
+    };
+
+    ~uninitialized()
+    {
+        if (initialized)
+        {
+            storage.~T();
+        }
+    }
+#endif // defined(_MSC_VER) && _MSC_VER < 1900
+
+    bool initialized;
+    uninitialized() CPPREST_NOEXCEPT : initialized(false) {}
+    uninitialized(const uninitialized&) = delete;
+    uninitialized& operator=(const uninitialized&) = delete;
+
+    template<class... Args>
+    void construct(Args&&... vals)
+    {
+        ::new (static_cast<void*>(&storage)) T(std::forward<Args>(vals)...);
+        initialized = true;
+    }
+};
+} // unnamed namespace
+
+std::pair<bool, platform_shared_threadpool*> initialize_shared_threadpool(size_t num_threads)
+{
+    static uninitialized<platform_shared_threadpool> uninit_threadpool;
+    bool initialized_this_time = false;
+    static std::once_flag of;
+
+#if defined(__ANDROID__)
+    abort_if_no_jvm();
+#endif // __ANDROID__
+
+    std::call_once(of, [num_threads, &initialized_this_time] {
+        uninit_threadpool.construct(num_threads);
+        initialized_this_time = true;
+    });
+
+    return
+    {
+        initialized_this_time,
+#if defined(_MSC_VER) && _MSC_VER < 1900
+            reinterpret_cast<platform_shared_threadpool*>(&uninit_threadpool.storage)
+#else  // ^^^ VS2013 ^^^ // vvv everything else vvv
+            &uninit_threadpool.storage
+#endif // defined(_MSC_VER) && _MSC_VER < 1900
+    };
+}
+} // namespace
+
+namespace crossplat
+{
+threadpool& threadpool::shared_instance() { return initialize_shared_threadpool(40).second->get_shared(); }
+
+void threadpool::initialize_with_threads(size_t num_threads)
+{
+    const auto result = initialize_shared_threadpool(num_threads);
+    if (!result.first)
+    {
+        throw std::runtime_error("the cpprestsdk threadpool has already been initialized");
+    }
+}
+
+#if defined(__ANDROID__)
+std::atomic<JavaVM*> JVM;
+
+JNIEnv* get_jvm_env()
+{
+    abort_if_no_jvm();
+    JNIEnv* env = nullptr;
+    auto result = crossplat::JVM.load()->AttachCurrentThread(&env, nullptr);
+    if (result != JNI_OK)
+    {
+        throw std::runtime_error("Could not attach to JVM");
+    }
+
+    return env;
+}
+#endif // defined(__ANDROID__)
+} // namespace crossplat
+
+#if defined(__ANDROID__)
+void cpprest_init(JavaVM* vm) { crossplat::JVM = vm; }
+#endif // defined(__ANDROID__)
+
+std::unique_ptr<crossplat::threadpool> crossplat::threadpool::construct(size_t num_threads)
+{
+    return std::unique_ptr<crossplat::threadpool>(new threadpool_impl(num_threads));
+}
+#endif //  !defined(CPPREST_EXCLUDE_WEBSOCKETS) || !defined(_WIN32)
diff --git a/Release/src/streams/fileio_posix.cpp b/Release/src/streams/fileio_posix.cpp
new file mode 100644 (file)
index 0000000..2404196
--- /dev/null
@@ -0,0 +1,653 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Asynchronous I/O: stream buffer implementation details
+ *
+ * We're going to some lengths to avoid exporting C++ class member functions and implementation details across
+ * module boundaries, and the factoring requires that we keep the implementation details away from the main header
+ * files. The supporting functions, which are in this file, use C-like signatures to avoid as many issues as
+ * possible.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "cpprest/details/fileio.h"
+
+using namespace boost::asio;
+using namespace Concurrency::streams::details;
+
+namespace Concurrency
+{
+namespace streams
+{
+namespace details
+{
+/***
+ * ==++==
+ *
+ * Implementation details of the file stream buffer
+ *
+ * =-=-=-
+ ****/
+
+/// <summary>
+/// The public parts of the file information record contain only what is implementation-
+/// independent. The actual allocated record is larger and has details that the implementation
+/// require in order to function.
+/// </summary>
+struct _file_info_impl : _file_info
+{
+    _file_info_impl(int handle, std::ios_base::openmode mode, bool buffer_reads)
+        : _file_info(mode, 512), m_handle(handle), m_buffer_reads(buffer_reads), m_outstanding_writes(0)
+    {
+    }
+
+    /// <summary>
+    /// The file handle of the file
+    /// </summary>
+    int m_handle;
+
+    bool m_buffer_reads;
+
+    /// <summary>
+    /// A list of callback waiting to be signaled that there are no outstanding writes.
+    /// </summary>
+    std::vector<_filestream_callback*> m_sync_waiters;
+
+    std::atomic<long> m_outstanding_writes;
+};
+
+} // namespace details
+} // namespace streams
+} // namespace Concurrency
+
+/// <summary>
+/// Perform post-CreateFile processing.
+/// </summary>
+/// <param name="fh">The Win32 file handle</param>
+/// <param name="callback">The callback interface pointer</param>
+/// <param name="mode">The C++ file open mode</param>
+/// <returns>The error code if there was an error in file creation.</returns>
+bool _finish_create(int fh, _filestream_callback* callback, std::ios_base::openmode mode, int /* prot */)
+{
+    if (fh != -1)
+    {
+        // Buffer reads internally if and only if we're just reading (not also writing) and
+        // if the file is opened exclusively. If either is false, we're better off just
+        // letting the OS do its buffering, even if it means that prompt reads won't
+        // happen.
+        bool buffer = (mode == std::ios_base::in);
+
+        // seek to end if requested
+        if (mode & std::ios_base::ate)
+        {
+            lseek(fh, 0, SEEK_END);
+        }
+
+        auto info = new _file_info_impl(fh, mode, buffer);
+
+        if (mode & std::ios_base::app || mode & std::ios_base::ate)
+        {
+            info->m_wrpos = static_cast<size_t>(-1); // Start at the end of the file.
+        }
+
+        callback->on_opened(info);
+        return true;
+    }
+    else
+    {
+        callback->on_error(std::make_exception_ptr(utility::details::create_system_error(errno)));
+        return false;
+    }
+}
+
+int get_open_flags(std::ios_base::openmode mode)
+{
+    int result = 0;
+    if (mode & std::ios_base::in)
+    {
+        if (mode & std::ios_base::out)
+        {
+            result = O_RDWR;
+        }
+        else
+        {
+            result = O_RDONLY;
+        }
+    }
+    else if (mode & std::ios_base::out)
+    {
+        result = O_WRONLY | O_CREAT;
+    }
+
+    if (mode & std::ios_base::app)
+    {
+        result |= O_APPEND;
+    }
+
+    if (mode & std::ios_base::trunc)
+    {
+        result |= O_TRUNC | O_CREAT;
+    }
+
+    return result;
+}
+
+/// <summary>
+/// Open a file and create a streambuf instance to represent it.
+/// </summary>
+/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
+/// <param name="filename">The name of the file to open</param>
+/// <param name="mode">A creation mode for the stream buffer</param>
+/// <param name="prot">A file protection mode to use for the file stream</param>
+/// <returns>True if the opening operation could be initiated, false otherwise.</returns>
+/// <remarks>
+/// True does not signal that the file will eventually be successfully opened, just that the process was started.
+/// </remarks>
+bool _open_fsb_str(_filestream_callback* callback, const char* filename, std::ios_base::openmode mode, int prot)
+{
+    if (callback == nullptr || filename == nullptr) return false;
+
+    std::string name(filename);
+
+    pplx::create_task([=]() -> void {
+        int cmode = get_open_flags(mode);
+        if (cmode == O_RDWR)
+        {
+            cmode |= O_CREAT;
+        }
+
+        int f = open(name.c_str(), cmode, 0666);
+
+        _finish_create(f, callback, mode, prot);
+    });
+
+    return true;
+}
+
+/// <summary>
+/// Close a file stream buffer.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
+/// <returns>True if the closing operation could be initiated, false otherwise.</returns>
+/// <remarks>
+/// True does not signal that the file will eventually be successfully closed, just that the process was started.
+/// </remarks>
+bool _close_fsb_nolock(_file_info** info, Concurrency::streams::details::_filestream_callback* callback)
+{
+    if (callback == nullptr) return false;
+    if (info == nullptr || *info == nullptr) return false;
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(*info);
+
+    if (fInfo->m_handle == -1) return false;
+
+    // Since closing a file may involve waiting for outstanding writes which can take some time
+    // if the file is on a network share, the close action is done in a separate task, as
+    // CloseHandle doesn't have I/O completion events.
+    pplx::create_task([=]() -> void {
+        bool result = false;
+
+        {
+            pplx::extensibility::scoped_recursive_lock_t lock(fInfo->m_lock);
+
+            if (fInfo->m_handle != -1)
+            {
+                result = close(fInfo->m_handle) != -1;
+            }
+
+            if (fInfo->m_buffer != nullptr)
+            {
+                delete[] fInfo->m_buffer;
+            }
+        }
+
+        delete fInfo;
+        if (result)
+        {
+            callback->on_closed();
+        }
+        else
+        {
+            callback->on_error(std::make_exception_ptr(utility::details::create_system_error(errno)));
+        }
+    });
+
+    *info = nullptr;
+
+    return true;
+}
+
+bool _close_fsb(_file_info** info, Concurrency::streams::details::_filestream_callback* callback)
+{
+    if (callback == nullptr) return false;
+    if (info == nullptr || *info == nullptr) return false;
+
+    pplx::extensibility::scoped_recursive_lock_t lock((*info)->m_lock);
+
+    return _close_fsb_nolock(info, callback);
+}
+
+/// <summary>
+/// Initiate an asynchronous (overlapped) write to the file stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to the data to write</param>
+/// <param name="count">The size (in bytes) of the data</param>
+/// <returns>0 if the write request is still outstanding, -1 if the request failed, otherwise the size of the data
+/// written</returns>
+size_t _write_file_async(Concurrency::streams::details::_file_info_impl* fInfo,
+                         Concurrency::streams::details::_filestream_callback* callback,
+                         const void* ptr,
+                         size_t count,
+                         size_t position)
+{
+    ++fInfo->m_outstanding_writes;
+
+    pplx::create_task([=]() -> void {
+        off_t abs_position;
+        bool must_restore_pos;
+        off_t orig_pos;
+        if (position == static_cast<size_t>(-1))
+        {
+            orig_pos = lseek(fInfo->m_handle, 0, SEEK_CUR);
+            abs_position = lseek(fInfo->m_handle, 0, SEEK_END);
+            must_restore_pos = true;
+        }
+        else
+        {
+            abs_position = position;
+            orig_pos = 0;
+            must_restore_pos = false;
+        }
+
+        auto bytes_written = pwrite(fInfo->m_handle, ptr, count, abs_position);
+        if (bytes_written == -1)
+        {
+            callback->on_error(std::make_exception_ptr(utility::details::create_system_error(errno)));
+        }
+
+        if (must_restore_pos)
+        {
+            lseek(fInfo->m_handle, orig_pos, SEEK_SET);
+        }
+
+        callback->on_completed(bytes_written);
+
+        {
+            pplx::extensibility::scoped_recursive_lock_t lock(fInfo->m_lock);
+
+            // Decrement the counter of outstanding write events.
+            if (--fInfo->m_outstanding_writes == 0)
+            {
+                // If this was the last one, signal all objects waiting for it to complete.
+
+                for (auto iter = fInfo->m_sync_waiters.begin(); iter != fInfo->m_sync_waiters.end(); iter++)
+                {
+                    (*iter)->on_completed(0);
+                }
+                fInfo->m_sync_waiters.clear();
+            }
+        }
+    });
+
+    return 0;
+}
+
+/// <summary>
+/// Initiate an asynchronous (overlapped) read from the file stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
+/// <param name="count">The size (in bytes) of the buffer</param>
+/// <param name="offset">The offset in the file to read from</param>
+/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read
+/// into the buffer</returns>
+size_t _read_file_async(Concurrency::streams::details::_file_info_impl* fInfo,
+                        Concurrency::streams::details::_filestream_callback* callback,
+                        void* ptr,
+                        size_t count,
+                        size_t offset)
+{
+    pplx::create_task([=]() -> void {
+        auto bytes_read = pread(fInfo->m_handle, ptr, count, offset);
+        if (bytes_read < 0)
+        {
+            callback->on_error(std::make_exception_ptr(utility::details::create_system_error(errno)));
+        }
+        else
+        {
+            callback->on_completed(bytes_read);
+        }
+    });
+
+    return 0;
+}
+
+template<typename Func>
+class _filestream_callback_fill_buffer : public _filestream_callback
+{
+public:
+    _filestream_callback_fill_buffer(_file_info* info, _filestream_callback* callback, const Func& func)
+        : m_info(info), m_func(func), m_callback(callback)
+    {
+    }
+
+    virtual void on_completed(size_t result) override
+    {
+        m_func(result);
+        delete this;
+    }
+    virtual void on_error(const std::exception_ptr& e) override
+    {
+        auto exptr = std::make_exception_ptr(e);
+        m_callback->on_error(exptr);
+        delete this;
+    }
+
+private:
+    _file_info* m_info;
+    Func m_func;
+    _filestream_callback* m_callback;
+};
+
+template<typename Func>
+_filestream_callback_fill_buffer<Func>* create_callback(_file_info* info,
+                                                        _filestream_callback* callback,
+                                                        const Func& func)
+{
+    return new _filestream_callback_fill_buffer<Func>(info, callback, func);
+}
+
+static const size_t PageSize = 512;
+
+size_t _fill_buffer_fsb(_file_info_impl* fInfo, _filestream_callback* callback, size_t count, size_t charSize)
+{
+    size_t byteCount = count * charSize;
+    if (fInfo->m_buffer == nullptr)
+    {
+        fInfo->m_bufsize = (std::max)(PageSize, byteCount);
+        fInfo->m_buffer = new char[static_cast<size_t>(fInfo->m_bufsize)];
+        fInfo->m_bufoff = fInfo->m_rdpos;
+
+        auto cb = create_callback(fInfo, callback, [=](size_t result) {
+            pplx::extensibility::scoped_recursive_lock_t lock(fInfo->m_lock);
+            fInfo->m_buffill = result / charSize;
+            callback->on_completed(result);
+        });
+
+        return _read_file_async(fInfo, cb, (uint8_t*)fInfo->m_buffer, fInfo->m_bufsize, fInfo->m_rdpos * charSize);
+    }
+
+    // First, we need to understand how far into the buffer we have already read
+    // and how much remains.
+
+    size_t bufpos = fInfo->m_rdpos - fInfo->m_bufoff;
+    size_t bufrem = fInfo->m_buffill - bufpos;
+
+    if (bufrem < count)
+    {
+        fInfo->m_bufsize = (std::max)(PageSize, byteCount);
+
+        // Then, we allocate a new buffer.
+
+        char* newbuf = new char[static_cast<size_t>(fInfo->m_bufsize)];
+
+        // Then, we copy the unread part to the new buffer and delete the old buffer
+
+        if (bufrem > 0) memcpy(newbuf, fInfo->m_buffer + bufpos * charSize, bufrem * charSize);
+
+        delete[] fInfo->m_buffer;
+        fInfo->m_buffer = newbuf;
+
+        // Then, we read the remainder of the count into the new buffer
+        fInfo->m_bufoff = fInfo->m_rdpos;
+
+        auto cb = create_callback(fInfo, callback, [=](size_t result) {
+            pplx::extensibility::scoped_recursive_lock_t lock(fInfo->m_lock);
+            fInfo->m_buffill = result / charSize;
+            callback->on_completed(result + bufrem * charSize);
+        });
+
+        return _read_file_async(fInfo,
+                                cb,
+                                (uint8_t*)fInfo->m_buffer + bufrem * charSize,
+                                fInfo->m_bufsize - bufrem * charSize,
+                                (fInfo->m_rdpos + bufrem) * charSize);
+    }
+    else
+        return byteCount;
+}
+
+/// <summary>
+/// Read data from a file stream into a buffer
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
+/// <param name="count">The size (in bytes) of the buffer</param>
+/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read
+/// into the buffer</returns>
+size_t _getn_fsb(Concurrency::streams::details::_file_info* info,
+                 Concurrency::streams::details::_filestream_callback* callback,
+                 void* ptr,
+                 size_t count,
+                 size_t charSize)
+{
+    if (callback == nullptr || info == nullptr) return static_cast<size_t>(-1);
+
+    _file_info_impl* fInfo = (_file_info_impl*)info;
+
+    pplx::extensibility::scoped_recursive_lock_t lock(info->m_lock);
+
+    if (fInfo->m_handle == -1) return static_cast<size_t>(-1);
+
+    size_t byteCount = count * charSize;
+
+    if (fInfo->m_buffer_reads)
+    {
+        auto cb = create_callback(fInfo, callback, [=](size_t read) {
+            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;
+            callback->on_completed(copy);
+        });
+
+        size_t read = _fill_buffer_fsb(fInfo, cb, count, charSize);
+
+        if (static_cast<int>(read) > 0)
+        {
+            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;
+            return copy;
+        }
+
+        return read;
+    }
+    else
+    {
+        return _read_file_async(fInfo, callback, ptr, count, fInfo->m_rdpos * charSize);
+    }
+}
+
+/// <summary>
+/// Write data from a buffer into the file stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
+/// <param name="count">The size (in bytes) of the buffer</param>
+/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read
+/// into the buffer</returns>
+size_t _putn_fsb(Concurrency::streams::details::_file_info* info,
+                 Concurrency::streams::details::_filestream_callback* callback,
+                 const void* ptr,
+                 size_t count,
+                 size_t charSize)
+{
+    if (callback == nullptr || info == nullptr) return static_cast<size_t>(-1);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lock(fInfo->m_lock);
+
+    if (fInfo->m_handle == -1) return static_cast<size_t>(-1);
+
+    size_t byteSize = count * charSize;
+
+    // To preserve the async write order, we have to move the write head before read.
+    auto lastPos = fInfo->m_wrpos;
+    if (fInfo->m_wrpos != static_cast<size_t>(-1))
+    {
+        fInfo->m_wrpos += count;
+        lastPos *= charSize;
+    }
+
+    return _write_file_async(fInfo, callback, ptr, byteSize, lastPos);
+}
+
+/// <summary>
+/// Flush all buffered data to the underlying file.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <returns>True if the request was initiated</returns>
+bool _sync_fsb(Concurrency::streams::details::_file_info* info,
+               Concurrency::streams::details::_filestream_callback* callback)
+{
+    if (callback == nullptr) return false;
+    if (info == nullptr) return false;
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lock(fInfo->m_lock);
+
+    if (fInfo->m_handle == -1) return false;
+
+    if (fInfo->m_outstanding_writes > 0)
+        fInfo->m_sync_waiters.push_back(callback);
+    else
+        callback->on_completed(0);
+
+    return true;
+}
+
+/// <summary>
+/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="pos">The new position (offset from the start) in the file stream</param>
+/// <returns>New file position or -1 if error</returns>
+size_t _seekrdtoend_fsb(Concurrency::streams::details::_file_info* info, int64_t offset, size_t char_size)
+{
+    if (info == nullptr) return static_cast<size_t>(-1);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lock(info->m_lock);
+
+    if (fInfo->m_handle == -1) return static_cast<size_t>(-1);
+
+    if (fInfo->m_buffer != nullptr)
+    {
+        delete[] fInfo->m_buffer;
+        fInfo->m_buffer = nullptr;
+        fInfo->m_bufoff = fInfo->m_buffill = fInfo->m_bufsize = 0;
+    }
+
+    auto newpos = lseek(fInfo->m_handle, static_cast<off_t>(offset * char_size), SEEK_END);
+
+    if (newpos == -1) return static_cast<size_t>(-1);
+
+    fInfo->m_rdpos = static_cast<size_t>(newpos) / char_size;
+    return fInfo->m_rdpos;
+}
+
+utility::size64_t _get_size(_In_ concurrency::streams::details::_file_info* info, size_t char_size)
+{
+    if (info == nullptr) return static_cast<size_t>(-1);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lock(info->m_lock);
+
+    if (fInfo->m_handle == -1) return static_cast<size_t>(-1);
+
+    if (fInfo->m_buffer != nullptr)
+    {
+        delete[] fInfo->m_buffer;
+        fInfo->m_buffer = nullptr;
+        fInfo->m_bufoff = fInfo->m_buffill = fInfo->m_bufsize = 0;
+    }
+
+    auto oldpos = lseek(fInfo->m_handle, 0, SEEK_CUR);
+
+    if (oldpos == -1) return utility::size64_t(-1);
+
+    auto newpos = lseek(fInfo->m_handle, 0, SEEK_END);
+
+    if (newpos == -1) return utility::size64_t(-1);
+
+    lseek(fInfo->m_handle, oldpos, SEEK_SET);
+
+    return utility::size64_t(newpos / char_size);
+}
+
+/// <summary>
+/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="pos">The new position (offset from the start) in the file stream</param>
+/// <returns>New file position or -1 if error</returns>
+size_t _seekrdpos_fsb(Concurrency::streams::details::_file_info* info, size_t pos, size_t)
+{
+    if (info == nullptr) return static_cast<size_t>(-1);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lock(info->m_lock);
+
+    if (fInfo->m_handle == -1) return static_cast<size_t>(-1);
+
+    if (pos < fInfo->m_bufoff || pos > (fInfo->m_bufoff + fInfo->m_buffill))
+    {
+        delete[] fInfo->m_buffer;
+        fInfo->m_buffer = nullptr;
+        fInfo->m_bufoff = fInfo->m_buffill = fInfo->m_bufsize = 0;
+    }
+
+    fInfo->m_rdpos = pos;
+    return fInfo->m_rdpos;
+}
+
+/// <summary>
+/// Adjust the internal buffers and pointers when the application seeks to a new write location in the stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="pos">The new position (offset from the start) in the file stream</param>
+/// <returns>New file position or -1 if error</returns>
+size_t _seekwrpos_fsb(Concurrency::streams::details::_file_info* info, size_t pos, size_t)
+{
+    if (info == nullptr) return static_cast<size_t>(-1);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lock(info->m_lock);
+
+    if (fInfo->m_handle == -1) return static_cast<size_t>(-1);
+
+    fInfo->m_wrpos = pos;
+    return fInfo->m_wrpos;
+}
diff --git a/Release/src/streams/fileio_win32.cpp b/Release/src/streams/fileio_win32.cpp
new file mode 100644 (file)
index 0000000..057dd9b
--- /dev/null
@@ -0,0 +1,968 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Asynchronous I/O: stream buffer implementation details
+ *
+ * We're going to some lengths to avoid exporting C++ class member functions and implementation details across
+ * module boundaries, and the factoring requires that we keep the implementation details away from the main header
+ * files. The supporting functions, which are in this file, use C-like signatures to avoid as many issues as
+ * possible.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "cpprest/details/fileio.h"
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace utility::conversions;
+
+namespace Concurrency
+{
+namespace streams
+{
+namespace details
+{
+/***
+ * ==++==
+ *
+ * Implementation details of the file stream buffer
+ *
+ * =-=-=-
+ ****/
+
+/// <summary>
+/// The public parts of the file information record contain only what is implementation-
+/// independent. The actual allocated record is larger and has details that the implementation
+/// require in order to function.
+/// </summary>
+struct _file_info_impl : _file_info
+{
+    _file_info_impl(HANDLE handle, _In_ void* io_ctxt, std::ios_base::openmode mode, size_t buffer_size)
+        : _file_info(mode, buffer_size), m_io_context(io_ctxt), m_handle(handle)
+    {
+    }
+
+    /// <summary>
+    /// The Win32 file handle of the file
+    /// </summary>
+    HANDLE m_handle;
+
+    /// <summary>
+    /// A Win32 I/O context, used by the thread pool to scheduler work.
+    /// </summary>
+    void* m_io_context;
+};
+
+} // namespace details
+} // namespace streams
+} // namespace Concurrency
+
+using namespace streams::details;
+
+/// <summary>
+/// Our extended OVERLAPPED record.
+/// </summary>
+/// <remarks>
+/// The standard OVERLAPPED structure doesn't have any fields for application-specific
+/// data, so we must extend it.
+/// </remarks>
+struct EXTENDED_OVERLAPPED : OVERLAPPED
+{
+    EXTENDED_OVERLAPPED(LPOVERLAPPED_COMPLETION_ROUTINE func, streams::details::_filestream_callback* cb)
+        : callback(cb), func(func)
+    {
+        ZeroMemory(this, sizeof(OVERLAPPED));
+    }
+
+    streams::details::_filestream_callback* callback;
+    LPOVERLAPPED_COMPLETION_ROUTINE func;
+};
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+void CALLBACK IoCompletionCallback(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED pOverlapped)
+{
+    EXTENDED_OVERLAPPED* pExtOverlapped = static_cast<EXTENDED_OVERLAPPED*>(pOverlapped);
+
+    ////If dwErrorCode is 0xc0000011, it means STATUS_END_OF_FILE.
+    ////Map this error code to system error code:ERROR_HANDLE_EOF
+    if (dwErrorCode == 0xc0000011) dwErrorCode = ERROR_HANDLE_EOF;
+    pExtOverlapped->func(dwErrorCode, dwNumberOfBytesTransfered, pOverlapped);
+    delete pOverlapped;
+}
+#else
+void CALLBACK IoCompletionCallback(PTP_CALLBACK_INSTANCE instance,
+                                   PVOID ctxt,
+                                   PVOID pOverlapped,
+                                   ULONG result,
+                                   ULONG_PTR numberOfBytesTransferred,
+                                   PTP_IO io)
+{
+    (void)io;
+    (void)ctxt;
+    (void)instance;
+
+    EXTENDED_OVERLAPPED* pExtOverlapped = static_cast<EXTENDED_OVERLAPPED*>(pOverlapped);
+    pExtOverlapped->func(result, static_cast<DWORD>(numberOfBytesTransferred), static_cast<LPOVERLAPPED>(pOverlapped));
+    delete pExtOverlapped;
+}
+#endif
+
+/// <summary>
+/// Translate from C++ STL file open modes to Win32 flags.
+/// </summary>
+/// <param name="mode">The C++ file open mode</param>
+/// <param name="prot">The C++ file open protection</param>
+/// <param name="dwDesiredAccess">A pointer to a DWORD that will hold the desired access flags</param>
+/// <param name="dwCreationDisposition">A pointer to a DWORD that will hold the creation disposition</param>
+/// <param name="dwShareMode">A pointer to a DWORD that will hold the share mode</param>
+void _get_create_flags(
+    std::ios_base::openmode mode, int prot, DWORD& dwDesiredAccess, DWORD& dwCreationDisposition, DWORD& dwShareMode)
+{
+    dwDesiredAccess = 0x0;
+    if (mode & std::ios_base::in) dwDesiredAccess |= GENERIC_READ;
+    if (mode & std::ios_base::out) dwDesiredAccess |= GENERIC_WRITE;
+
+    if (mode & std::ios_base::in)
+    {
+        if (mode & std::ios_base::out)
+            dwCreationDisposition = OPEN_ALWAYS;
+        else
+            dwCreationDisposition = OPEN_EXISTING;
+    }
+    else if (mode & std::ios_base::trunc)
+    {
+        dwCreationDisposition = CREATE_ALWAYS;
+    }
+    else
+    {
+        dwCreationDisposition = OPEN_ALWAYS;
+    }
+
+    // C++ specifies what permissions to deny, Windows which permissions to give,
+    dwShareMode = 0x3;
+    switch (prot)
+    {
+        case _SH_DENYRW: dwShareMode = 0x0; break;
+        case _SH_DENYWR: dwShareMode = 0x1; break;
+        case _SH_DENYRD: dwShareMode = 0x2; break;
+    }
+}
+
+/// <summary>
+/// Perform post-CreateFile processing.
+/// </summary>
+/// <param name="fh">The Win32 file handle</param>
+/// <param name="callback">The callback interface pointer</param>
+/// <param name="mode">The C++ file open mode</param>
+void _finish_create(HANDLE fh, _In_ _filestream_callback* callback, std::ios_base::openmode mode, int prot)
+{
+    if (fh == INVALID_HANDLE_VALUE)
+    {
+        callback->on_error(std::make_exception_ptr(utility::details::create_system_error(GetLastError())));
+        return;
+    }
+
+    void* io_ctxt = nullptr;
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+    if (!BindIoCompletionCallback(fh, IoCompletionCallback, 0))
+    {
+        callback->on_error(std::make_exception_ptr(utility::details::create_system_error(GetLastError())));
+        return;
+    }
+#else
+    io_ctxt = CreateThreadpoolIo(fh, IoCompletionCallback, nullptr, nullptr);
+    if (io_ctxt == nullptr)
+    {
+        callback->on_error(std::make_exception_ptr(utility::details::create_system_error(GetLastError())));
+        return;
+    }
+
+    if (!SetFileCompletionNotificationModes(fh, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS))
+    {
+        CloseThreadpoolIo(static_cast<PTP_IO>(io_ctxt));
+        callback->on_error(std::make_exception_ptr(utility::details::create_system_error(GetLastError())));
+        return;
+    }
+#endif
+
+    // Buffer reads internally if and only if we're just reading (not also writing) and
+    // if the file is opened exclusively. If either is false, we're better off just
+    // letting the OS do its buffering, even if it means that prompt reads won't
+    // happen.
+    bool buffer = (mode == std::ios_base::in) && (prot == _SH_DENYRW);
+
+    auto info = new _file_info_impl(fh, io_ctxt, mode, buffer ? 512 : 0);
+
+    if (mode & std::ios_base::app || mode & std::ios_base::ate)
+    {
+        info->m_wrpos = static_cast<size_t>(-1); // Start at the end of the file.
+    }
+
+    callback->on_opened(info);
+}
+
+/// <summary>
+/// Open a file and create a streambuf instance to represent it.
+/// </summary>
+/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
+/// <param name="filename">The name of the file to open</param>
+/// <param name="mode">A creation mode for the stream buffer</param>
+/// <param name="prot">A file protection mode to use for the file stream</param>
+/// <returns><c>true</c> if the opening operation could be initiated, <c>false</c> otherwise.</returns>
+/// <remarks>
+/// True does not signal that the file will eventually be successfully opened, just that the process was started.
+/// </remarks>
+bool __cdecl _open_fsb_str(_In_ _filestream_callback* callback,
+                           const utility::char_t* filename,
+                           std::ios_base::openmode mode,
+                           int prot)
+{
+    _ASSERTE(callback != nullptr);
+    _ASSERTE(filename != nullptr);
+
+    std::wstring name(filename);
+
+    pplx::create_task([=]() {
+        DWORD dwDesiredAccess, dwCreationDisposition, dwShareMode;
+        _get_create_flags(mode, prot, dwDesiredAccess, dwCreationDisposition, dwShareMode);
+
+        HANDLE fh = ::CreateFileW(
+            name.c_str(), dwDesiredAccess, dwShareMode, nullptr, dwCreationDisposition, FILE_FLAG_OVERLAPPED, 0);
+
+        _finish_create(fh, callback, mode, prot);
+    });
+
+    return true;
+}
+
+/// <summary>
+/// Close a file stream buffer.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
+/// <returns><c>true</c> if the closing operation could be initiated, <c>false</c> otherwise.</returns>
+/// <remarks>
+/// True does not signal that the file will eventually be successfully closed, just that the process was started.
+/// </remarks>
+bool __cdecl _close_fsb_nolock(_In_ _file_info** info, _In_ streams::details::_filestream_callback* callback)
+{
+    _ASSERTE(callback != nullptr);
+    _ASSERTE(info != nullptr);
+    _ASSERTE(*info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(*info);
+
+    if (fInfo->m_handle == INVALID_HANDLE_VALUE) return false;
+
+    // Since closing a file may involve waiting for outstanding writes which can take some time
+    // if the file is on a network share, the close action is done in a separate task, as
+    // CloseHandle doesn't have I/O completion events.
+    pplx::create_task([=]() {
+        bool result = false;
+
+        {
+            pplx::extensibility::scoped_recursive_lock_t lck(fInfo->m_lock);
+
+            if (fInfo->m_handle != INVALID_HANDLE_VALUE)
+            {
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+                CloseThreadpoolIo(static_cast<PTP_IO>(fInfo->m_io_context));
+#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
+
+                result = CloseHandle(fInfo->m_handle) != FALSE;
+            }
+
+            delete fInfo->m_buffer;
+        }
+
+        delete fInfo;
+
+        if (result)
+            callback->on_closed();
+        else
+            callback->on_error(std::make_exception_ptr(utility::details::create_system_error(GetLastError())));
+    });
+
+    *info = nullptr;
+
+    return true;
+}
+
+bool __cdecl _close_fsb(_In_ _file_info** info, _In_ streams::details::_filestream_callback* callback)
+{
+    _ASSERTE(callback != nullptr);
+    _ASSERTE(info != nullptr);
+    _ASSERTE(*info != nullptr);
+
+    return _close_fsb_nolock(info, callback);
+}
+
+/// <summary>
+/// The completion routine used when a write request finishes.
+/// </summary>
+/// <remarks>
+/// The signature is the standard IO completion signature, documented on MSDN
+/// </remarks>
+template<typename InfoType>
+VOID CALLBACK _WriteFileCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
+{
+    EXTENDED_OVERLAPPED* pOverlapped = static_cast<EXTENDED_OVERLAPPED*>(lpOverlapped);
+
+    if (dwErrorCode != ERROR_SUCCESS && dwErrorCode != ERROR_HANDLE_EOF)
+    {
+        pOverlapped->callback->on_error(std::make_exception_ptr(utility::details::create_system_error(dwErrorCode)));
+    }
+    else
+    {
+        pOverlapped->callback->on_completed(static_cast<size_t>(dwNumberOfBytesTransfered));
+    }
+}
+
+/// <summary>
+/// The completion routine used when a read request finishes.
+/// </summary>
+/// <remarks>
+/// The signature is the standard IO completion signature, documented on MSDN
+/// </remarks>
+template<typename InfoType>
+VOID CALLBACK _ReadFileCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
+{
+    EXTENDED_OVERLAPPED* pOverlapped = static_cast<EXTENDED_OVERLAPPED*>(lpOverlapped);
+
+    if (dwErrorCode != ERROR_SUCCESS && dwErrorCode != ERROR_HANDLE_EOF)
+    {
+        pOverlapped->callback->on_error(std::make_exception_ptr(utility::details::create_system_error(dwErrorCode)));
+    }
+    else
+    {
+        pOverlapped->callback->on_completed(static_cast<size_t>(dwNumberOfBytesTransfered));
+    }
+}
+
+/// <summary>
+/// Initiate an asynchronous (overlapped) write to the file stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to the data to write</param>
+/// <param name="count">The size (in bytes) of the data</param>
+/// <returns>0 if the write request is still outstanding, -1 if the request failed, otherwise the size of the data
+/// written</returns>
+size_t _write_file_async(_In_ streams::details::_file_info_impl* fInfo,
+                         _In_ streams::details::_filestream_callback* callback,
+                         const void* ptr,
+                         size_t count,
+                         size_t position)
+{
+    auto pOverlapped = std::unique_ptr<EXTENDED_OVERLAPPED>(
+        new EXTENDED_OVERLAPPED(_WriteFileCompletionRoutine<streams::details::_file_info_impl>, callback));
+
+    if (position == static_cast<size_t>(-1))
+    {
+        pOverlapped->Offset = 0xFFFFFFFF;
+        pOverlapped->OffsetHigh = 0xFFFFFFFF;
+    }
+    else
+    {
+        pOverlapped->Offset = static_cast<DWORD>(position);
+#ifdef _WIN64
+        pOverlapped->OffsetHigh = static_cast<DWORD>(position >> 32);
+#else
+        pOverlapped->OffsetHigh = 0;
+#endif
+    }
+
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+    StartThreadpoolIo(static_cast<PTP_IO>(fInfo->m_io_context));
+
+    BOOL wrResult = WriteFile(fInfo->m_handle, ptr, static_cast<DWORD>(count), nullptr, pOverlapped.get());
+    DWORD error = GetLastError();
+
+    // WriteFile will return false when a) the operation failed, or b) when the request is still
+    // pending. The error code will tell us which is which.
+    if (wrResult == FALSE && error == ERROR_IO_PENDING)
+    {
+        // Overlapped is deleted in the threadpool callback.
+        pOverlapped.release();
+        return 0;
+    }
+
+    CancelThreadpoolIo(static_cast<PTP_IO>(fInfo->m_io_context));
+
+    size_t result = static_cast<size_t>(-1);
+
+    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
+        // 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<size_t>(written)
+                                                                                          : static_cast<size_t>(-1);
+    }
+
+    if (result == static_cast<size_t>(-1))
+        callback->on_error(std::make_exception_ptr(utility::details::create_system_error(error)));
+
+    return result;
+#else
+    BOOL wrResult = WriteFile(fInfo->m_handle, ptr, (DWORD)count, nullptr, pOverlapped.get());
+    DWORD error = GetLastError();
+
+    // 1. If WriteFile returned true, it must be because the operation completed immediately.
+    // The xp threadpool immediately creates a workerthread to run "_WriteFileCompletionRoutine".
+    // If this function return value > 0, the condition "if (written == sizeof(_CharType))" in the filestreams.h
+    // "_getcImpl()" function will be satisfied. The main thread will delete the input "callback", while the threadpool
+    // workerthread is accessing this "callback"; there will be a race condition and AV error. We directly return 0 and
+    // leave all the completion callbacks working on the workerthread. We do not need to call GetOverlappedResult, the
+    // workerthread will call the "on_error()" if the WriteFaile failed. "req" is deleted in
+    // "_WriteFileCompletionRoutine, "pOverlapped" is deleted in io_scheduler::FileIOCompletionRoutine.
+    if (wrResult == TRUE)
+    {
+        pOverlapped.release();
+        return 0;
+    }
+
+    // 2. If WriteFile returned false and GetLastError is ERROR_IO_PENDING, return 0,
+    //    The xp threadpool will create a workerthread to run "_WriteFileCompletionRoutine" after the operation
+    //    completed.
+    if (wrResult == FALSE && error == ERROR_IO_PENDING)
+    {
+        // Overlapped is deleted in the threadpool callback.
+        pOverlapped.release();
+        return 0;
+    }
+
+    // 3. If ReadFile returned false and GetLastError is not ERROR_IO_PENDING, we must call "callback->on_error()".
+    //    The threadpools will not start the workerthread.
+    callback->on_error(std::make_exception_ptr(utility::details::create_system_error(error)));
+
+    return static_cast<size_t>(-1);
+#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
+}
+
+/// <summary>
+/// Initiate an asynchronous (overlapped) read from the file stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
+/// <param name="count">The size (in bytes) of the buffer</param>
+/// <param name="offset">The offset in the file to read from</param>
+/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read
+/// into the buffer</returns>
+size_t _read_file_async(_In_ streams::details::_file_info_impl* fInfo,
+                        _In_ streams::details::_filestream_callback* callback,
+                        _Out_writes_(count) void* ptr,
+                        _In_ size_t count,
+                        size_t offset)
+{
+    auto pOverlapped = std::unique_ptr<EXTENDED_OVERLAPPED>(
+        new EXTENDED_OVERLAPPED(_ReadFileCompletionRoutine<streams::details::_file_info_impl>, callback));
+    pOverlapped->Offset = static_cast<DWORD>(offset);
+#ifdef _WIN64
+    pOverlapped->OffsetHigh = static_cast<DWORD>(offset >> 32);
+#else
+    pOverlapped->OffsetHigh = 0;
+#endif
+
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+    StartThreadpoolIo((PTP_IO)fInfo->m_io_context);
+
+    BOOL wrResult = ReadFile(fInfo->m_handle, ptr, static_cast<DWORD>(count), nullptr, pOverlapped.get());
+    DWORD error = GetLastError();
+
+    // ReadFile will return false when a) the operation failed, or b) when the request is still
+    // pending. The error code will tell us which is which.
+    if (wrResult == FALSE && error == ERROR_IO_PENDING)
+    {
+        // Overlapped is deleted in the threadpool callback.
+        pOverlapped.release();
+        return 0;
+    }
+
+    // We find ourselves here because there was a synchronous completion, either with an error or
+    // success. Either way, we don't need the thread pool I/O request here, or the request and
+    // overlapped structures.
+    CancelThreadpoolIo(static_cast<PTP_IO>(fInfo->m_io_context));
+
+    size_t result = static_cast<size_t>(-1);
+
+    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
+        // we have to retrieve it using 'GetOverlappedResult,' which may, in turn, fail.
+        DWORD read = 0;
+        result = GetOverlappedResult(fInfo->m_handle, pOverlapped.get(), &read, FALSE) ? static_cast<size_t>(read)
+                                                                                       : static_cast<size_t>(-1);
+    }
+
+    if (wrResult == FALSE && error == ERROR_HANDLE_EOF)
+    {
+        callback->on_completed(0);
+        return 0;
+    }
+
+    if (result == static_cast<size_t>(-1))
+        callback->on_error(std::make_exception_ptr(utility::details::create_system_error(error)));
+
+    return result;
+#else
+    BOOL wrResult = ReadFile(fInfo->m_handle, ptr, static_cast<DWORD>(count), nullptr, pOverlapped.get());
+    DWORD error = GetLastError();
+
+    // 1. If ReadFile returned true, it must be because the operation completed immediately.
+    // The xp threadpool immediately creates a workerthread to run "_WriteFileCompletionRoutine".
+    // If this function return value > 0, the condition "if ( ch == sizeof(_CharType) )" in the filestreams.h
+    // "_getcImpl()" function will be satisfied. The main thread will delete the input "callback", while the threadpool
+    // workerthread is accessing this "callback"; there will be a race condition and AV error. We can directly return 0
+    // and leave all the completion callbacks working on the workerthread. We do not need to call GetOverlappedResult,
+    // the workerthread will call the "on_error()" if the ReadFile failed. "req" is deleted in
+    // "_ReadFileCompletionRoutine, "pOverlapped" is deleted in io_scheduler::FileIOCompletionRoutine.
+    if (wrResult == TRUE)
+    {
+        pOverlapped.release();
+        return 0;
+    }
+
+    // 2. If ReadFile returned false and GetLastError is ERROR_IO_PENDING, return 0.
+    //    The xp threadpool will create a workerthread to run "_WriteFileCompletionRoutine" after the operation
+    //    completed.
+    if (wrResult == FALSE && error == ERROR_IO_PENDING)
+    {
+        // Overlapped is deleted in the threadpool callback.
+        pOverlapped.release();
+        return 0;
+    }
+
+    // 3. If ReadFile returned false and GetLastError is ERROR_HANDLE_EOF, we must call "callback->on_completed(0)".
+    //    The threadpool will not start the workerthread.
+    if (wrResult == FALSE && error == ERROR_HANDLE_EOF)
+    {
+        callback->on_completed(0);
+        return 0;
+    }
+
+    // 4. If ReadFile returned false and GetLastError is not a valid error code, we must call "callback->on_error()".
+    //    The threadpool will not start the workerthread.
+    callback->on_error(std::make_exception_ptr(utility::details::create_system_error(error)));
+
+    return static_cast<size_t>(-1);
+#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
+}
+
+template<typename Func>
+class _filestream_callback_fill_buffer : public _filestream_callback
+{
+public:
+    _filestream_callback_fill_buffer(_In_ _file_info* info, const Func& func) : m_func(func), m_info(info) {}
+
+    virtual void on_completed(size_t result)
+    {
+        m_func(result);
+        delete this;
+    }
+
+private:
+    _file_info* m_info;
+    Func m_func;
+};
+
+template<typename Func>
+_filestream_callback_fill_buffer<Func>* create_callback(_In_ _file_info* info, const Func& func)
+{
+    return new _filestream_callback_fill_buffer<Func>(info, func);
+}
+
+size_t _fill_buffer_fsb(_In_ _file_info_impl* fInfo,
+                        _In_ _filestream_callback* callback,
+                        size_t count,
+                        size_t char_size)
+{
+    msl::safeint3::SafeInt<size_t> safeCount = count;
+
+    if (fInfo->m_buffer == nullptr || safeCount > fInfo->m_bufsize)
+    {
+        if (fInfo->m_buffer != nullptr) delete fInfo->m_buffer;
+
+        fInfo->m_bufsize = safeCount.Max(fInfo->m_buffer_size);
+        fInfo->m_buffer = new char[fInfo->m_bufsize * char_size];
+        fInfo->m_bufoff = fInfo->m_rdpos;
+
+        auto cb = create_callback(fInfo, [=](size_t result) {
+            pplx::extensibility::scoped_recursive_lock_t lck(fInfo->m_lock);
+            fInfo->m_buffill = result / char_size;
+            callback->on_completed(result);
+        });
+
+        auto read = _read_file_async(fInfo,
+                                     cb,
+                                     reinterpret_cast<uint8_t*>(fInfo->m_buffer),
+                                     fInfo->m_bufsize * char_size,
+                                     fInfo->m_rdpos * char_size);
+
+        switch (read)
+        {
+            case 0:
+                // pending
+                return read;
+
+            case (-1):
+                // error
+                delete cb;
+                return read;
+
+            default:
+                // operation is complete. The pattern of returning synchronously
+                // has the expectation that we duplicate the callback code here...
+                // Do the expedient thing for now.
+                cb->on_completed(read);
+
+                // return pending
+                return 0;
+        };
+    }
+
+    // First, we need to understand how far into the buffer we have already read
+    // and how much remains.
+
+    size_t bufpos = fInfo->m_rdpos - fInfo->m_bufoff;
+    size_t bufrem = fInfo->m_buffill - bufpos;
+
+    // We have four different scenarios:
+    //  1. The read position is before the start of the buffer, in which case we will just reuse the buffer.
+    //  2. The read position is in the middle of the buffer, and we need to read some more.
+    //  3. The read position is beyond the end of the buffer. Do as in #1.
+    //  4. We have everything we need.
+
+    if ((fInfo->m_rdpos < fInfo->m_bufoff) || (fInfo->m_rdpos >= (fInfo->m_bufoff + fInfo->m_buffill)))
+    {
+        // Reuse the existing buffer.
+
+        fInfo->m_bufoff = fInfo->m_rdpos;
+
+        auto cb = create_callback(fInfo, [=](size_t result) {
+            pplx::extensibility::scoped_recursive_lock_t lck(fInfo->m_lock);
+            fInfo->m_buffill = result / char_size;
+            callback->on_completed(bufrem * char_size + result);
+        });
+
+        auto read = _read_file_async(fInfo,
+                                     cb,
+                                     reinterpret_cast<uint8_t*>(fInfo->m_buffer),
+                                     fInfo->m_bufsize * char_size,
+                                     fInfo->m_rdpos * char_size);
+
+        switch (read)
+        {
+            case 0:
+                // pending
+                return read;
+
+            case (-1):
+                // error
+                delete cb;
+                return read;
+
+            default:
+                // operation is complete. The pattern of returning synchronously
+                // has the expectation that we duplicate the callback code here...
+                // Do the expedient thing for now.
+                cb->on_completed(read);
+
+                // return pending
+                return 0;
+        };
+    }
+    else if (bufrem < count)
+    {
+        fInfo->m_bufsize = safeCount.Max(fInfo->m_buffer_size);
+
+        // Then, we allocate a new buffer.
+
+        char* newbuf = new char[fInfo->m_bufsize * char_size];
+
+        // Then, we copy the unread part to the new buffer and delete the old buffer
+
+        if (bufrem > 0) memcpy(newbuf, fInfo->m_buffer + bufpos * char_size, bufrem * char_size);
+
+        delete fInfo->m_buffer;
+        fInfo->m_buffer = newbuf;
+
+        // Then, we read the remainder of the count into the new buffer
+        fInfo->m_bufoff = fInfo->m_rdpos;
+
+        auto cb = create_callback(fInfo, [=](size_t result) {
+            pplx::extensibility::scoped_recursive_lock_t lck(fInfo->m_lock);
+            fInfo->m_buffill = result / char_size;
+            callback->on_completed(bufrem * char_size + result);
+        });
+
+        auto read = _read_file_async(fInfo,
+                                     cb,
+                                     reinterpret_cast<uint8_t*>(fInfo->m_buffer) + bufrem * char_size,
+                                     (fInfo->m_bufsize - bufrem) * char_size,
+                                     (fInfo->m_rdpos + bufrem) * char_size);
+
+        switch (read)
+        {
+            case 0:
+                // pending
+                return read;
+
+            case (-1):
+                // error
+                delete cb;
+                return read;
+
+            default:
+                // operation is complete. The pattern of returning synchronously
+                // has the expectation that we duplicate the callback code here...
+                // Do the expedient thing for now.
+                cb->on_completed(read);
+
+                // return pending
+                return 0;
+        };
+    }
+    else
+    {
+        // If we are here, it means that we didn't need to read, we already have enough data in the buffer
+        return count * char_size;
+    }
+}
+
+/// <summary>
+/// Read data from a file stream into a buffer
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
+/// <param name="count">The size (in characters) of the buffer</param>
+/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read
+/// into the buffer</returns>
+size_t __cdecl _getn_fsb(_In_ streams::details::_file_info* info,
+                         _In_ streams::details::_filestream_callback* callback,
+                         _Out_writes_(count) void* ptr,
+                         _In_ size_t count,
+                         size_t char_size)
+{
+    _ASSERTE(callback != nullptr);
+    _ASSERTE(info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(info->m_lock);
+
+    if (fInfo->m_handle == INVALID_HANDLE_VALUE)
+    {
+        callback->on_error(std::make_exception_ptr(utility::details::create_system_error(ERROR_INVALID_HANDLE)));
+        return (size_t)-1;
+    }
+
+    if (fInfo->m_buffer_size > 0)
+    {
+        auto cb = create_callback(fInfo, [=](size_t read) {
+            auto sz = count * char_size;
+            auto copy = (read < sz) ? read : sz;
+            auto bufoff = fInfo->m_rdpos - fInfo->m_bufoff;
+            memcpy(ptr, fInfo->m_buffer + bufoff * char_size, copy);
+            fInfo->m_atend = copy < sz;
+            callback->on_completed(copy);
+        });
+
+        size_t read = _fill_buffer_fsb(fInfo, cb, count, char_size);
+
+        if (read > 0)
+        {
+            auto sz = count * char_size;
+            auto copy = (read < sz) ? read : sz;
+            auto bufoff = fInfo->m_rdpos - fInfo->m_bufoff;
+            memcpy(ptr, fInfo->m_buffer + bufoff * char_size, copy);
+            fInfo->m_atend = copy < sz;
+            return copy;
+        }
+
+        return read;
+    }
+    else
+    {
+        return _read_file_async(fInfo, callback, ptr, count * char_size, fInfo->m_rdpos * char_size);
+    }
+}
+
+/// <summary>
+/// Write data from a buffer into the file stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
+/// <param name="count">The size (in characters) of the buffer</param>
+/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read
+/// into the buffer</returns>
+size_t __cdecl _putn_fsb(_In_ streams::details::_file_info* info,
+                         _In_ streams::details::_filestream_callback* callback,
+                         const void* ptr,
+                         size_t count,
+                         size_t char_size)
+{
+    _ASSERTE(info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(fInfo->m_lock);
+
+    if (fInfo->m_handle == INVALID_HANDLE_VALUE)
+    {
+        callback->on_error(std::make_exception_ptr(utility::details::create_system_error(ERROR_INVALID_HANDLE)));
+        return static_cast<size_t>(-1);
+    }
+
+    // To preserve the async write order, we have to move the write head before read.
+    auto lastPos = fInfo->m_wrpos;
+    if (fInfo->m_wrpos != static_cast<size_t>(-1))
+    {
+        fInfo->m_wrpos += count;
+        lastPos *= char_size;
+    }
+    return _write_file_async(fInfo, callback, ptr, count * char_size, lastPos);
+}
+
+/// <summary>
+/// Flush all buffered data to the underlying file.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <returns><c>true</c> if the request was initiated</returns>
+bool __cdecl _sync_fsb(_In_ streams::details::_file_info*, _In_ streams::details::_filestream_callback* callback)
+{
+    _ASSERTE(callback != nullptr);
+
+    // Writes are not cached
+    callback->on_completed(0);
+
+    return true;
+}
+
+/// <summary>
+/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="pos">The new position (offset from the start) in the file stream</param>
+/// <returns>New file position or -1 if error</returns>
+size_t __cdecl _seekrdpos_fsb(_In_ streams::details::_file_info* info, size_t pos, size_t)
+{
+    _ASSERTE(info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(info->m_lock);
+
+    if (fInfo->m_handle == INVALID_HANDLE_VALUE) return static_cast<size_t>(-1);
+
+    if (pos < fInfo->m_bufoff || pos > (fInfo->m_bufoff + fInfo->m_buffill))
+    {
+        delete fInfo->m_buffer;
+        fInfo->m_buffer = nullptr;
+        fInfo->m_bufoff = fInfo->m_buffill = fInfo->m_bufsize = 0;
+    }
+
+    fInfo->m_rdpos = pos;
+    return fInfo->m_rdpos;
+}
+
+/// <summary>
+/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="offset">The new position (offset from the end of the stream) in the file stream</param>
+/// <param name="char_size">The size of the character type used for this stream</param>
+/// <returns>New file position or -1 if error</returns>
+size_t __cdecl _seekrdtoend_fsb(_In_ streams::details::_file_info* info, int64_t offset, size_t char_size)
+{
+    _ASSERTE(info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(info->m_lock);
+
+    if (fInfo->m_handle == INVALID_HANDLE_VALUE) return static_cast<size_t>(-1);
+
+    if (fInfo->m_buffer != nullptr)
+    {
+        // Clear the internal buffer.
+        delete fInfo->m_buffer;
+        fInfo->m_buffer = nullptr;
+        fInfo->m_bufoff = fInfo->m_buffill = fInfo->m_bufsize = 0;
+    }
+
+#ifdef _WIN64
+    LARGE_INTEGER filesize;
+    filesize.QuadPart = 0;
+
+    BOOL result = GetFileSizeEx(fInfo->m_handle, &filesize);
+    if (FALSE == result)
+    {
+        return static_cast<size_t>(-1);
+    }
+    else
+    {
+        fInfo->m_rdpos = static_cast<size_t>(filesize.QuadPart) / char_size;
+    }
+#else
+    auto newpos = SetFilePointer(fInfo->m_handle, (LONG)(offset * char_size), nullptr, FILE_END);
+
+    if (newpos == INVALID_SET_FILE_POINTER) return static_cast<size_t>(-1);
+
+    fInfo->m_rdpos = static_cast<size_t>(newpos) / char_size;
+#endif
+
+    return fInfo->m_rdpos;
+}
+
+utility::size64_t __cdecl _get_size(_In_ concurrency::streams::details::_file_info* info, size_t char_size)
+{
+    _ASSERTE(info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(info->m_lock);
+
+    if (fInfo->m_handle == INVALID_HANDLE_VALUE) return (utility::size64_t)-1;
+
+    LARGE_INTEGER size;
+
+    if (GetFileSizeEx(fInfo->m_handle, &size))
+        return utility::size64_t(size.QuadPart / char_size);
+    else
+        return 0;
+}
+
+/// <summary>
+/// Adjust the internal buffers and pointers when the application seeks to a new write location in the stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="pos">The new position (offset from the start) in the file stream</param>
+/// <returns>New file position or -1 if error</returns>
+size_t __cdecl _seekwrpos_fsb(_In_ streams::details::_file_info* info, size_t pos, size_t)
+{
+    _ASSERTE(info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(info->m_lock);
+
+    if (fInfo->m_handle == INVALID_HANDLE_VALUE) return static_cast<size_t>(-1);
+
+    fInfo->m_wrpos = pos;
+    return fInfo->m_wrpos;
+}
diff --git a/Release/src/streams/fileio_winrt.cpp b/Release/src/streams/fileio_winrt.cpp
new file mode 100644 (file)
index 0000000..ef6a588
--- /dev/null
@@ -0,0 +1,1042 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Asynchronous I/O: stream buffer implementation details
+ *
+ * We're going to some lengths to avoid exporting C++ class member functions and implementation details across
+ * module boundaries, and the factoring requires that we keep the implementation details away from the main header
+ * files. The supporting functions, which are in this file, use C-like signatures to avoid as many issues as
+ * possible.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "cpprest/details/fileio.h"
+#include "cpprest/interopstream.h"
+#include "robuffer.h"
+
+using namespace ::Windows::Foundation;
+using namespace ::Windows::Storage;
+using namespace ::Windows::Storage::Streams;
+using namespace ::Windows::Networking;
+using namespace ::Windows::Networking::Sockets;
+
+namespace Concurrency
+{
+namespace streams
+{
+namespace details
+{
+/// <summary>
+/// The public parts of the file information record contain only what is implementation-
+/// independent. The actual allocated record is larger and has details that the implementation
+/// require in order to function.
+/// </summary>
+struct _file_info_impl : _file_info
+{
+    _file_info_impl(Streams::IRandomAccessStream ^ stream, std::ios_base::openmode mode)
+        : m_stream(stream), m_writer(nullptr), _file_info(mode, 0)
+    {
+        m_pendingWrites = pplx::task_from_result();
+    }
+
+    _file_info_impl(Streams::IRandomAccessStream ^ stream, std::ios_base::openmode mode, size_t buffer_size)
+        : m_stream(stream), m_writer(nullptr), _file_info(mode, buffer_size)
+    {
+        m_pendingWrites = pplx::task_from_result();
+    }
+
+    Streams::IRandomAccessStream ^ m_stream;
+    Streams::IDataWriter ^ m_writer;
+    pplx::task<void> m_pendingWrites;
+};
+
+} // namespace details
+} // namespace streams
+} // namespace Concurrency
+
+using namespace Concurrency::streams::details;
+
+#pragma warning(push)
+#pragma warning(disable : 4100)
+
+/// <summary>
+/// Translate from C++ STL file open modes to Win32 flags.
+/// </summary>
+/// <param name="mode">The C++ file open mode</param>
+/// <param name="prot">The C++ file open protection</param>
+/// <param name="dwDesiredAccess">A pointer to a DWORD that will hold the desired access flags</param>
+/// <param name="dwCreationDisposition">A pointer to a DWORD that will hold the creation disposition</param>
+/// <param name="dwShareMode">A pointer to a DWORD that will hold the share mode</param>
+void _get_create_flags(std::ios_base::openmode mode,
+                       int prot,
+                       FileAccessMode& acc_mode,
+                       CreationCollisionOption& options)
+{
+    options = CreationCollisionOption::OpenIfExists;
+    acc_mode = FileAccessMode::ReadWrite;
+
+    if ((mode & std::ios_base::in) && !(mode & std::ios_base::out)) acc_mode = FileAccessMode::Read;
+    if (mode & std::ios_base::trunc) options = CreationCollisionOption::ReplaceExisting;
+}
+
+/// <summary>
+/// Perform post-CreateFile processing.
+/// </summary>
+/// <param name="fh">The Win32 file handle</param>
+/// <param name="callback">The callback interface pointer</param>
+/// <param name="mode">The C++ file open mode</param>
+/// <returns>The error code if there was an error in file creation.</returns>
+void _finish_create(Streams::IRandomAccessStream ^ stream,
+                    _In_ _filestream_callback* callback,
+                    std::ios_base::openmode mode,
+                    int prot)
+{
+    _file_info_impl* info = nullptr;
+
+    info = new _file_info_impl(stream, mode, 512);
+
+    // Seek to end if it's in appending write mode
+    if ((mode & std::ios_base::out) && (mode & std::ios_base::app || mode & std::ios_base::ate))
+    {
+        _seekwrpos_fsb(info, static_cast<size_t>(stream->Size), 1);
+    }
+
+    callback->on_opened(info);
+}
+
+/// <summary>
+/// Create a streambuf instance to represent a WinRT file.
+/// </summary>
+/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
+/// <param name="file">The file object</param>
+/// <param name="mode">A creation mode for the stream buffer</param>
+/// <returns>True if the opening operation could be initiated, false otherwise.</returns>
+/// <remarks>
+/// True does not signal that the file will eventually be successfully opened, just that the process was started.
+/// This is only available for WinRT.
+/// </remarks>
+bool __cdecl _open_fsb_stf_str(_In_ Concurrency::streams::details::_filestream_callback* callback,
+                               ::Windows::Storage::StorageFile ^ file,
+                               std::ios_base::openmode mode,
+                               int prot)
+{
+    _ASSERTE(callback != nullptr);
+    _ASSERTE(file != nullptr);
+
+    CreationCollisionOption options;
+    FileAccessMode acc_mode;
+
+    _get_create_flags(mode, prot, acc_mode, options);
+
+    pplx::create_task(file->OpenAsync(acc_mode)).then([=](pplx::task<Streams::IRandomAccessStream ^> sop) {
+        try
+        {
+            _finish_create(sop.get(), callback, mode, prot);
+        }
+        catch (Platform::Exception ^ exc)
+        {
+            callback->on_error(std::make_exception_ptr(utility::details::create_system_error(exc->HResult)));
+        }
+    });
+
+    return true;
+}
+
+bool __cdecl _sync_fsb_winrt(_In_ Concurrency::streams::details::_file_info* info,
+                             _In_opt_ Concurrency::streams::details::_filestream_callback* callback)
+{
+    _ASSERTE(info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(fInfo->m_lock);
+
+    if (fInfo->m_stream == nullptr || fInfo->m_writer == nullptr || !fInfo->m_stream->CanWrite) return false;
+
+    // take a snapshot of current writer, since writer can be replaced during flush
+    auto writer = fInfo->m_writer;
+    // Flush operation will not begin until all previous writes (StoreAsync) finished, thus it could avoid race.
+    fInfo->m_pendingWrites = fInfo->m_pendingWrites.then([=] { return writer->StoreAsync(); })
+                                 .then([=](unsigned int) {
+                                     fInfo->m_buffill = 0;
+                                     return writer->FlushAsync();
+                                 })
+                                 .then([=](pplx::task<bool> result) {
+                                     // Rethrow exception if no callback attached.
+                                     if (callback == nullptr)
+                                         result.wait();
+                                     else
+                                     {
+                                         try
+                                         {
+                                             result.wait();
+                                             callback->on_completed(0);
+                                         }
+                                         catch (Platform::Exception ^ exc)
+                                         {
+                                             callback->on_error(std::make_exception_ptr(
+                                                 utility::details::create_system_error(exc->HResult)));
+                                         }
+                                     }
+                                 });
+    return true;
+}
+
+/// <summary>
+/// Close a file stream buffer.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
+/// <returns>True if the closing operation could be initiated, false otherwise.</returns>
+/// <remarks>
+/// True does not signal that the file will eventually be successfully closed, just that the process was started.
+/// </remarks>
+bool __cdecl _close_fsb_nolock(_In_ _file_info** info,
+                               _In_ Concurrency::streams::details::_filestream_callback* callback)
+{
+    _ASSERTE(callback != nullptr);
+    _ASSERTE(info != nullptr);
+    _ASSERTE(*info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(*info);
+
+    *info = nullptr;
+
+    auto stream = fInfo->m_stream;
+
+    if (fInfo->m_stream->CanWrite)
+    {
+        _sync_fsb_winrt(fInfo, nullptr);
+        fInfo->m_pendingWrites.then([=](pplx::task<void> t) {
+            try
+            {
+                // The lock fInfo->m_lock must not be held at this point
+                delete fInfo;
+                t.wait();
+                callback->on_closed();
+            }
+            catch (Platform::Exception ^ exc)
+            {
+                callback->on_error(std::make_exception_ptr(utility::details::create_system_error(exc->HResult)));
+            }
+        });
+    }
+    else
+    {
+        // The lock fInfo->m_lock must not be held at this point
+        delete fInfo;
+        callback->on_closed();
+    }
+    return true;
+}
+
+bool __cdecl _close_fsb(_In_ _file_info** info, _In_ Concurrency::streams::details::_filestream_callback* callback)
+{
+    return _close_fsb_nolock(info, callback);
+}
+
+/// <summary>
+/// Initiate an asynchronous (overlapped) read from the file stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
+/// <param name="count">The size (in bytes) of the buffer</param>
+/// <param name="offset">The offset in the file to read from</param>
+/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read
+/// into the buffer</returns>
+size_t __cdecl _read_file_async(_In_ Concurrency::streams::details::_file_info_impl* fInfo,
+                                _In_ Concurrency::streams::details::_filestream_callback* callback,
+                                _Out_writes_(count) void* ptr,
+                                _In_ size_t count,
+                                size_t offset)
+{
+    if (fInfo->m_stream == nullptr)
+    {
+        if (callback != nullptr)
+        {
+            // I don't know of a better error code, so this will have to do.
+            callback->on_error(std::make_exception_ptr(utility::details::create_system_error(ERROR_INVALID_ADDRESS)));
+        }
+        return 0;
+    }
+
+    auto reader = ref new Streams::DataReader(fInfo->m_stream->GetInputStreamAt(offset));
+
+    pplx::create_task(reader->LoadAsync(static_cast<unsigned int>(count))).then([=](pplx::task<unsigned int> result) {
+        try
+        {
+            auto read = result.get();
+
+            if (read > 0)
+            {
+                reader->ReadBytes(Platform::ArrayReference<unsigned char>(static_cast<unsigned char*>(ptr), read));
+            }
+
+            callback->on_completed(read);
+        }
+        catch (Platform::Exception ^ exc)
+        {
+            callback->on_error(std::make_exception_ptr(utility::details::create_system_error(exc->HResult)));
+        }
+    });
+
+    return 0;
+}
+
+template<typename Func>
+class _filestream_callback_fill_buffer : public _filestream_callback
+{
+public:
+    _filestream_callback_fill_buffer(_In_ _file_info* info, const Func& func) : m_func(func), m_info(info) {}
+
+    virtual void on_completed(size_t result)
+    {
+        m_func(result);
+        delete this;
+    }
+
+private:
+    _file_info* m_info;
+    Func m_func;
+};
+
+template<typename Func>
+_filestream_callback_fill_buffer<Func>* create_callback(_In_ _file_info* info, const Func& func)
+{
+    return new _filestream_callback_fill_buffer<Func>(info, func);
+}
+
+size_t _fill_buffer_fsb(_In_ _file_info_impl* fInfo,
+                        _In_ _filestream_callback* callback,
+                        size_t count,
+                        size_t char_size)
+{
+    msl::safeint3::SafeInt<size_t> safeCount = count;
+
+    if (fInfo->m_buffer == nullptr || safeCount > fInfo->m_bufsize)
+    {
+        if (fInfo->m_buffer != nullptr) delete fInfo->m_buffer;
+
+        fInfo->m_bufsize = safeCount.Max(fInfo->m_buffer_size);
+        fInfo->m_buffer = new char[fInfo->m_bufsize * char_size];
+        fInfo->m_bufoff = fInfo->m_rdpos;
+
+        auto cb = create_callback(fInfo, [=](size_t result) {
+            {
+                pplx::extensibility::scoped_recursive_lock_t lck(fInfo->m_lock);
+                fInfo->m_buffill = result / char_size;
+            }
+            callback->on_completed(result);
+        });
+
+        auto read = _read_file_async(
+            fInfo, cb, (uint8_t*)fInfo->m_buffer, fInfo->m_bufsize * char_size, fInfo->m_rdpos * char_size);
+
+        switch (read)
+        {
+            case 0:
+                // pending
+                return read;
+
+            case (-1):
+                // error
+                delete cb;
+                return read;
+
+            default:
+                // operation is complete. The pattern of returning synchronously
+                // has the expectation that we duplicate the callback code here...
+                // Do the expedient thing for now.
+                cb->on_completed(read);
+
+                // return pending
+                return 0;
+        };
+    }
+
+    // First, we need to understand how far into the buffer we have already read
+    // and how much remains.
+
+    size_t bufpos = fInfo->m_rdpos - fInfo->m_bufoff;
+    size_t bufrem = fInfo->m_buffill - bufpos;
+
+    // We have four different scenarios:
+    //  1. The read position is before the start of the buffer, in which case we will just reuse the buffer.
+    //  2. The read position is in the middle of the buffer, and we need to read some more.
+    //  3. The read position is beyond the end of the buffer. Do as in #1.
+    //  4. We have everything we need.
+
+    if ((fInfo->m_rdpos < fInfo->m_bufoff) || (fInfo->m_rdpos >= (fInfo->m_bufoff + fInfo->m_buffill)))
+    {
+        // Reuse the existing buffer.
+
+        fInfo->m_bufoff = fInfo->m_rdpos;
+
+        auto cb = create_callback(fInfo, [=](size_t result) {
+            {
+                pplx::extensibility::scoped_recursive_lock_t lck(fInfo->m_lock);
+                fInfo->m_buffill = result / char_size;
+            }
+            callback->on_completed(bufrem * char_size + result);
+        });
+
+        auto read = _read_file_async(
+            fInfo, cb, (uint8_t*)fInfo->m_buffer, fInfo->m_bufsize * char_size, fInfo->m_rdpos * char_size);
+
+        switch (read)
+        {
+            case 0:
+                // pending
+                return read;
+
+            case (-1):
+                // error
+                delete cb;
+                return read;
+
+            default:
+                // operation is complete. The pattern of returning synchronously
+                // has the expectation that we duplicate the callback code here...
+                // Do the expedient thing for now.
+                cb->on_completed(read);
+
+                // return pending
+                return 0;
+        };
+    }
+    else if (bufrem < count)
+    {
+        fInfo->m_bufsize = safeCount.Max(fInfo->m_buffer_size);
+
+        // Then, we allocate a new buffer.
+
+        char* newbuf = new char[fInfo->m_bufsize * char_size];
+
+        // Then, we copy the unread part to the new buffer and delete the old buffer
+
+        if (bufrem > 0) memcpy(newbuf, fInfo->m_buffer + bufpos * char_size, bufrem * char_size);
+
+        delete fInfo->m_buffer;
+        fInfo->m_buffer = newbuf;
+
+        // Then, we read the remainder of the count into the new buffer
+        fInfo->m_bufoff = fInfo->m_rdpos;
+
+        auto cb = create_callback(fInfo, [=](size_t result) {
+            {
+                pplx::extensibility::scoped_recursive_lock_t lck(fInfo->m_lock);
+                fInfo->m_buffill = result / char_size;
+            }
+            callback->on_completed(bufrem * char_size + result);
+        });
+
+        auto read = _read_file_async(fInfo,
+                                     cb,
+                                     (uint8_t*)fInfo->m_buffer + bufrem * char_size,
+                                     (fInfo->m_bufsize - bufrem) * char_size,
+                                     (fInfo->m_rdpos + bufrem) * char_size);
+
+        switch (read)
+        {
+            case 0:
+                // pending
+                return read;
+
+            case (-1):
+                // error
+                delete cb;
+                return read;
+
+            default:
+                // operation is complete. The pattern of returning synchronously
+                // has the expectation that we duplicate the callback code here...
+                // Do the expedient thing for now.
+                cb->on_completed(read);
+
+                // return pending
+                return 0;
+        };
+    }
+    else
+    {
+        // If we are here, it means that we didn't need to read, we already have enough data in the buffer
+        return count * char_size;
+    }
+}
+
+/// <summary>
+/// Read data from a file stream into a buffer
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
+/// <param name="count">The size (in characters) of the buffer</param>
+/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read
+/// into the buffer</returns>
+size_t __cdecl _getn_fsb(_In_ Concurrency::streams::details::_file_info* info,
+                         _In_ Concurrency::streams::details::_filestream_callback* callback,
+                         _Out_writes_(count) void* ptr,
+                         _In_ size_t count,
+                         size_t char_size)
+{
+    _ASSERTE(callback != nullptr);
+    _ASSERTE(info != nullptr);
+    _ASSERTE(count > 0);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(info->m_lock);
+
+    if (fInfo->m_buffer_size > 0)
+    {
+        auto cb = create_callback(fInfo, [=](size_t read) {
+            auto sz = count * char_size;
+            auto copy = (read < sz) ? read : sz;
+            auto bufoff = fInfo->m_rdpos - fInfo->m_bufoff;
+            memcpy(ptr, fInfo->m_buffer + bufoff * char_size, copy);
+            fInfo->m_atend = copy < sz;
+            callback->on_completed(copy);
+        });
+
+        size_t read = _fill_buffer_fsb(fInfo, cb, count, char_size);
+
+        if (read > 0)
+        {
+            auto sz = count * char_size;
+            auto copy = (read < sz) ? read : sz;
+            auto bufoff = fInfo->m_rdpos - fInfo->m_bufoff;
+            memcpy(ptr, fInfo->m_buffer + bufoff * char_size, copy);
+            fInfo->m_atend = copy < sz;
+            return copy;
+        }
+
+        return (size_t)read;
+    }
+    else
+    {
+        return _read_file_async(fInfo, callback, ptr, count * char_size, fInfo->m_rdpos * char_size);
+    }
+}
+
+/// <summary>
+/// Write data from a buffer into the file stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
+/// <param name="count">The size (in characters) of the buffer</param>
+/// <returns>0 if the write request is still outstanding, -1 if the request failed, otherwise the size of the data read
+/// into the buffer</returns>
+size_t __cdecl _putn_fsb(_In_ Concurrency::streams::details::_file_info* info,
+                         _In_ Concurrency::streams::details::_filestream_callback* callback,
+                         const void* ptr,
+                         size_t count,
+                         size_t char_size)
+{
+    _ASSERTE(callback != nullptr);
+    _ASSERTE(info != nullptr);
+    _ASSERTE(count > 0);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(fInfo->m_lock);
+
+    if (fInfo->m_stream == nullptr) return static_cast<size_t>(-1);
+
+    // To preserve the async write order, we have to move the write head before read.
+    if (fInfo->m_wrpos != static_cast<size_t>(-1)) fInfo->m_wrpos += count;
+
+    msl::safeint3::SafeInt<unsigned int> safeWriteSize = count;
+    safeWriteSize *= char_size;
+
+    // In most of the time, we preserve the writer so that it would have better performance.
+    // However, after user call seek, we will dispose old writer. By doing so, users could
+    // write to new writer in new position while the old writer is still flushing data into stream.
+    if (fInfo->m_writer == nullptr)
+    {
+        fInfo->m_writer = ref new Streams::DataWriter(fInfo->m_stream);
+        fInfo->m_buffill = 0;
+    }
+
+    // It keeps tracking the number of bytes written into m_writer buffer.
+    fInfo->m_buffill += count;
+
+    // ArrayReference here is for avoiding data copy.
+    fInfo->m_writer->WriteBytes(Platform::ArrayReference<unsigned char>(
+        const_cast<unsigned char*>(static_cast<const unsigned char*>(ptr)), safeWriteSize));
+
+    // Flush data from m_writer buffer into stream , if the buffer is full
+    if (fInfo->m_buffill >= fInfo->m_buffer_size)
+    {
+        fInfo->m_buffill = 0;
+        fInfo->m_pendingWrites = fInfo->m_pendingWrites.then([=] { return fInfo->m_writer->StoreAsync(); })
+                                     .then([=](pplx::task<unsigned int> result) {
+                                         try
+                                         {
+                                             result.wait();
+                                             callback->on_completed(safeWriteSize);
+                                         }
+                                         catch (Platform::Exception ^ exc)
+                                         {
+                                             callback->on_error(std::make_exception_ptr(
+                                                 utility::details::create_system_error(exc->HResult)));
+                                         }
+                                     });
+        return 0;
+    }
+    else
+        return safeWriteSize;
+}
+
+/// <summary>
+/// Flush all buffered data to the underlying file.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
+/// <returns>True if the request was initiated</returns>
+bool __cdecl _sync_fsb(_In_ Concurrency::streams::details::_file_info* info,
+                       _In_ Concurrency::streams::details::_filestream_callback* callback)
+{
+    return _sync_fsb_winrt(info, callback);
+}
+
+/// <summary>
+/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="pos">The new position (offset from the start) in the file stream</param>
+/// <returns>New file position or -1 if error</returns>
+size_t __cdecl _seekrdpos_fsb(_In_ Concurrency::streams::details::_file_info* info, size_t pos, size_t char_size)
+{
+    _ASSERTE(info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(info->m_lock);
+
+    if (fInfo->m_stream == nullptr) return static_cast<size_t>(-1);
+    ;
+
+    if (pos < fInfo->m_bufoff || pos > (fInfo->m_bufoff + fInfo->m_buffill))
+    {
+        delete fInfo->m_buffer;
+        fInfo->m_buffer = nullptr;
+        fInfo->m_bufoff = fInfo->m_buffill = fInfo->m_bufsize = 0;
+    }
+
+    fInfo->m_rdpos = pos;
+
+    return fInfo->m_rdpos;
+}
+
+/// <summary>
+/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="offset">The new position (offset from the end of the stream) in the file stream</param>
+/// <param name="char_size">The size of the character type used for this stream</param>
+/// <returns>New file position or -1 if error</returns>
+_ASYNCRTIMP size_t __cdecl _seekrdtoend_fsb(_In_ Concurrency::streams::details::_file_info* info,
+                                            int64_t offset,
+                                            size_t char_size)
+{
+    _ASSERTE(info != nullptr);
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    return _seekrdpos_fsb(info, static_cast<size_t>(fInfo->m_stream->Size / char_size + offset), char_size);
+}
+
+utility::size64_t __cdecl _get_size(_In_ concurrency::streams::details::_file_info* info, size_t char_size)
+{
+    _ASSERTE(info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(info->m_lock);
+
+    if (fInfo->m_stream == nullptr) return 0;
+
+    return utility::size64_t(fInfo->m_stream->Size / char_size);
+}
+
+/// <summary>
+/// Adjust the internal buffers and pointers when the application seeks to a new write location in the stream.
+/// </summary>
+/// <param name="info">The file info record of the file</param>
+/// <param name="pos">The new position (offset from the start) in the file stream</param>
+/// <returns>New file position or -1 if error</returns>
+size_t __cdecl _seekwrpos_fsb(_In_ Concurrency::streams::details::_file_info* info, size_t pos, size_t char_size)
+{
+    _ASSERTE(info != nullptr);
+
+    _file_info_impl* fInfo = static_cast<_file_info_impl*>(info);
+
+    pplx::extensibility::scoped_recursive_lock_t lck(info->m_lock);
+
+    if (fInfo->m_stream == nullptr) return static_cast<size_t>(-1);
+
+    fInfo->m_wrpos = pos;
+
+    // m_buffill keeps number of chars written into the m_writer buffer.
+    // We need to flush it into stream before seek the write head of the stream
+    if (fInfo->m_buffill > 0) _sync_fsb_winrt(fInfo, nullptr);
+
+    // Moving write head should follow the flush operation. is_done test is for perf optimization.
+    if (fInfo->m_pendingWrites.is_done())
+        fInfo->m_stream->Seek(static_cast<long long>(pos) * char_size);
+    else
+    {
+        auto lastWriter = fInfo->m_writer;
+        fInfo->m_writer = nullptr;
+
+        fInfo->m_pendingWrites = fInfo->m_pendingWrites.then([=] {
+            // Detach stream could avoid stream destruction after writer get destructed.
+            lastWriter->DetachStream();
+            fInfo->m_stream->Seek(static_cast<long long>(pos) * char_size);
+        });
+    }
+
+    return fInfo->m_wrpos;
+}
+
+namespace Concurrency
+{
+namespace streams
+{
+namespace details
+{
+/// <summary>
+/// This class acts as a bridge between WinRT input streams and Casablanca asynchronous streams.
+/// </summary>
+ref class IRandomAccessStream_bridge sealed : public Windows::Storage::Streams::IRandomAccessStream
+{
+public:
+    virtual property bool CanRead
+    {
+        bool get() { return m_buffer.can_read(); }
+    }
+
+    virtual property bool CanWrite
+    {
+        bool get() { return m_buffer.can_write(); }
+    }
+
+    virtual property uint64_t Position
+    {
+        uint64_t get() { return m_position; }
+    }
+
+    virtual property uint64_t Size
+    {
+        uint64_t get()
+        {
+            if (!m_buffer.has_size()) return m_remembered_size;
+            return m_buffer.size();
+        }
+
+        void set(uint64_t sz)
+        {
+            if (!m_buffer.has_size() || !m_buffer.can_write())
+                m_remembered_size = sz;
+            else
+                m_buffer.seekoff(basic_streambuf<uint8_t>::pos_type(sz), std::ios_base::beg, std::ios_base::out);
+        }
+    }
+
+    virtual Windows::Storage::Streams::IRandomAccessStream ^
+        CloneStream() { return ref new IRandomAccessStream_bridge(m_buffer); }
+
+        virtual Windows::Storage::Streams::IInputStream
+        ^
+        GetInputStreamAt(uint64_t position) {
+            if (!m_buffer.can_read()) return nullptr;
+
+            concurrency::streams::streambuf<uint8_t>::pos_type pos = position;
+
+            if (m_buffer.can_seek() || pos == m_buffer.getpos(std::ios_base::in))
+            {
+                return ref new IRandomAccessStream_bridge(m_buffer, position);
+            }
+            return nullptr;
+        }
+
+        virtual Windows::Storage::Streams::IOutputStream
+        ^ GetOutputStreamAt(uint64_t position) {
+              if (!m_buffer.can_write()) return nullptr;
+
+              concurrency::streams::streambuf<uint8_t>::pos_type pos = position;
+
+              if (m_buffer.can_seek() || pos == m_buffer.getpos(std::ios_base::out))
+              {
+                  return ref new IRandomAccessStream_bridge(m_buffer, position);
+              }
+              return nullptr;
+          };
+
+    virtual void Seek(uint64_t position)
+    {
+        if (!m_buffer.can_seek()) throw ref new Platform::InvalidArgumentException(L"underlying buffer cannot seek");
+
+        m_position = position;
+
+        m_buffer.seekpos(concurrency::streams::streambuf<uint8_t>::pos_type(m_position), std::ios_base::in);
+        m_buffer.seekpos(concurrency::streams::streambuf<uint8_t>::pos_type(m_position), std::ios_base::out);
+    }
+
+    virtual Windows::Foundation::IAsyncOperationWithProgress<unsigned int, unsigned int> ^
+        WriteAsync(Windows::Storage::Streams::IBuffer ^ buffer);
+    virtual Windows::Foundation::IAsyncOperationWithProgress<::Windows::Storage::Streams::IBuffer ^, unsigned int> ^
+        ReadAsync(::Windows::Storage::Streams::IBuffer ^ buffer,
+                  unsigned int count,
+                  Windows::Storage::Streams::InputStreamOptions options);
+    virtual Windows::Foundation::IAsyncOperation<bool> ^ FlushAsync();
+
+    virtual ~IRandomAccessStream_bridge() {}
+
+    internal :
+
+        IRandomAccessStream_bridge(const concurrency::streams::streambuf<uint8_t>& buffer)
+        : m_buffer(buffer), m_remembered_size(0), m_position(0)
+    {
+    }
+
+    IRandomAccessStream_bridge(const concurrency::streams::streambuf<uint8_t>& buffer,
+                               concurrency::streams::streambuf<uint8_t>::pos_type position)
+        : m_buffer(buffer), m_remembered_size(0), m_position(position)
+    {
+    }
+
+private:
+    uint64_t m_remembered_size;
+    concurrency::streams::streambuf<uint8_t>::pos_type m_position;
+    concurrency::streams::streambuf<uint8_t> m_buffer;
+};
+
+struct _alloc_protector
+{
+    _alloc_protector(concurrency::streams::streambuf<uint8_t>& buffer) : m_buffer(buffer), m_size(0) {}
+
+    ~_alloc_protector() { m_buffer.commit(m_size); }
+
+    size_t m_size;
+
+private:
+    _alloc_protector& operator=(const _alloc_protector&);
+
+    concurrency::streams::streambuf<uint8_t>& m_buffer;
+};
+
+struct _acquire_protector
+{
+    _acquire_protector(concurrency::streams::streambuf<uint8_t>& buffer, uint8_t* ptr)
+        : m_buffer(buffer), m_ptr(ptr), m_size(0)
+    {
+    }
+
+    ~_acquire_protector() { m_buffer.release(m_ptr, m_size); }
+
+    size_t m_size;
+
+private:
+    _acquire_protector& operator=(const _acquire_protector&);
+
+    uint8_t* m_ptr;
+    concurrency::streams::streambuf<uint8_t>& m_buffer;
+};
+
+// Rather than using ComPtr, which is somewhat complex, a simple RAII class
+// to make sure that Release() is called is useful here.
+struct _IUnknown_protector
+{
+    _IUnknown_protector(IUnknown* unk_ptr) : m_unknown(unk_ptr) {}
+    ~_IUnknown_protector()
+    {
+        if (m_unknown != nullptr) m_unknown->Release();
+    }
+
+private:
+    IUnknown* m_unknown;
+};
+
+Windows::Foundation::IAsyncOperationWithProgress<::Windows::Storage::Streams::IBuffer ^, unsigned int> ^
+    IRandomAccessStream_bridge::ReadAsync(::Windows::Storage::Streams::IBuffer ^ buffer,
+                                          unsigned int count,
+                                          Windows::Storage::Streams::InputStreamOptions options)
+{
+    if (!m_buffer.can_read())
+    {
+        return pplx::create_async([buffer](pplx::progress_reporter<uint32> reporter) { return buffer; });
+    }
+
+    if (buffer->Capacity < count)
+        return pplx::create_async([buffer](pplx::progress_reporter<uint32> reporter) { return buffer; });
+
+    m_buffer.seekpos(concurrency::streams::streambuf<uint8_t>::pos_type(m_position), std::ios_base::in);
+
+    concurrency::streams::streambuf<uint8_t> streambuf = m_buffer;
+
+    return pplx::create_async([streambuf, buffer, options, count](pplx::progress_reporter<uint32> reporter) {
+        auto sbuf = streambuf;
+        auto local_buf = ref new ::Platform::Array<unsigned char, 1>(count);
+
+        uint8_t* ptr = nullptr;
+        size_t acquired_size = 0;
+
+        if (sbuf.acquire(ptr, acquired_size) && acquired_size >= count)
+        {
+            _acquire_protector prot(sbuf, ptr);
+
+            IUnknown* pUnk = reinterpret_cast<IUnknown*>(buffer);
+            ::Windows::Storage::Streams::IBufferByteAccess* pBufferByteAccess = nullptr;
+            HRESULT hr = pUnk->QueryInterface(IID_PPV_ARGS(&pBufferByteAccess));
+            __abi_ThrowIfFailed(hr);
+
+            _IUnknown_protector unkprot(pBufferByteAccess);
+
+            byte* buffer_data = nullptr;
+            hr = pBufferByteAccess->Buffer(&buffer_data);
+            __abi_ThrowIfFailed(hr);
+
+            memcpy(buffer_data, ptr, count);
+
+            prot.m_size = count;
+            buffer->Length = count;
+
+            return pplx::task_from_result(buffer);
+        }
+        else
+        {
+            if (acquired_size > 0)
+            {
+                sbuf.release(ptr, 0);
+            }
+
+            IUnknown* pUnk = reinterpret_cast<IUnknown*>(buffer);
+            ::Windows::Storage::Streams::IBufferByteAccess* pBufferByteAccess = nullptr;
+            HRESULT hr = pUnk->QueryInterface(IID_PPV_ARGS(&pBufferByteAccess));
+            __abi_ThrowIfFailed(hr);
+
+            _IUnknown_protector unkprot(pBufferByteAccess);
+
+            byte* buffer_data = nullptr;
+            hr = pBufferByteAccess->Buffer(&buffer_data);
+            __abi_ThrowIfFailed(hr);
+
+            pBufferByteAccess->AddRef();
+
+            return sbuf.getn(buffer_data, count).then([buffer, pBufferByteAccess, count](pplx::task<size_t> written) {
+                _IUnknown_protector unkprot(pBufferByteAccess);
+                buffer->Length = (unsigned int)written.get();
+                return pplx::task_from_result(buffer);
+            });
+        }
+    });
+}
+
+Windows::Foundation::IAsyncOperationWithProgress<unsigned int, unsigned int> ^
+    IRandomAccessStream_bridge::WriteAsync(Windows::Storage::Streams::IBuffer ^ buffer)
+{
+    if (!m_buffer.can_write())
+    {
+        return pplx::create_async([](pplx::progress_reporter<uint32> reporter) { return 0U; });
+    }
+
+    m_buffer.seekpos(concurrency::streams::streambuf<uint8_t>::pos_type(m_position), std::ios_base::out);
+
+    concurrency::streams::streambuf<uint8_t> streambuf = m_buffer;
+
+    return pplx::create_async([buffer, streambuf](pplx::progress_reporter<uint32> reporter) {
+        auto size = buffer->Length;
+        auto sbuf = streambuf;
+        uint8_t* ptr = sbuf.alloc(size);
+
+        if (ptr != nullptr)
+        {
+            {
+                _alloc_protector prot(sbuf);
+
+                IUnknown* pUnk = reinterpret_cast<IUnknown*>(buffer);
+                ::Windows::Storage::Streams::IBufferByteAccess* pBufferByteAccess = nullptr;
+                HRESULT hr = pUnk->QueryInterface(IID_PPV_ARGS(&pBufferByteAccess));
+                __abi_ThrowIfFailed(hr);
+
+                _IUnknown_protector unkprot(pBufferByteAccess);
+
+                byte* buffer_data = nullptr;
+                hr = pBufferByteAccess->Buffer(&buffer_data);
+                __abi_ThrowIfFailed(hr);
+
+                memcpy(ptr, buffer_data, size);
+
+                prot.m_size = size;
+            }
+            return pplx::task_from_result((unsigned int)size);
+        }
+        else
+        {
+            IUnknown* pUnk = reinterpret_cast<IUnknown*>(buffer);
+            ::Windows::Storage::Streams::IBufferByteAccess* pBufferByteAccess = nullptr;
+            HRESULT hr = pUnk->QueryInterface(IID_PPV_ARGS(&pBufferByteAccess));
+            __abi_ThrowIfFailed(hr);
+
+            _IUnknown_protector unkprot(pBufferByteAccess);
+
+            byte* buffer_data = nullptr;
+            hr = pBufferByteAccess->Buffer(&buffer_data);
+            __abi_ThrowIfFailed(hr);
+
+            pBufferByteAccess->AddRef();
+
+            return sbuf.putn_nocopy(buffer_data, size).then([pBufferByteAccess](pplx::task<size_t> size) {
+                pBufferByteAccess->Release();
+                return (unsigned int)size.get();
+            });
+        }
+    });
+}
+
+Windows::Foundation::IAsyncOperation<bool> ^ IRandomAccessStream_bridge::FlushAsync()
+{
+    concurrency::streams::streambuf<uint8_t> streambuf = m_buffer;
+    return pplx::create_async([streambuf]() {
+        if (!streambuf.can_write())
+        {
+            return pplx::task_from_result(false);
+        }
+
+        auto sbuf = streambuf;
+        return sbuf.sync().then([] { return pplx::task_from_result(true); });
+    });
+}
+
+} // namespace details
+} // namespace streams
+} // namespace Concurrency
+
+Windows::Storage::Streams::IInputStream ^
+    Concurrency::streams::winrt_stream::create_input_stream(const concurrency::streams::streambuf<uint8_t>& buffer)
+{
+    return ref new ::Concurrency::streams::details::IRandomAccessStream_bridge(buffer, 0);
+}
+
+Windows::Storage::Streams::IOutputStream ^
+    Concurrency::streams::winrt_stream::create_output_stream(const concurrency::streams::streambuf<uint8_t>& buffer)
+{
+    return ref new Concurrency::streams::details::IRandomAccessStream_bridge(buffer, 0);
+}
+
+Windows::Storage::Streams::IRandomAccessStream ^ Concurrency::streams::winrt_stream::create_random_access_stream(
+                                                     const concurrency::streams::streambuf<uint8_t>& buffer)
+{
+    return ref new Concurrency::streams::details::IRandomAccessStream_bridge(buffer);
+}
+
+#pragma warning(pop)
diff --git a/Release/src/uri/uri.cpp b/Release/src/uri/uri.cpp
new file mode 100644 (file)
index 0000000..3f2414a
--- /dev/null
@@ -0,0 +1,867 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Protocol independent support for URIs.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <sstream>
+
+using namespace utility::conversions;
+
+namespace web
+{
+namespace details
+{
+namespace
+{
+/// <summary>
+/// Unreserved characters are those that are allowed in a URI but do not have a reserved purpose. They include:
+/// - A-Z
+/// - a-z
+/// - 0-9
+/// - '-' (hyphen)
+/// - '.' (period)
+/// - '_' (underscore)
+/// - '~' (tilde)
+/// </summary>
+inline bool is_unreserved(int c)
+{
+    return ::utility::details::is_alnum((char)c) || c == '-' || c == '.' || c == '_' || c == '~';
+}
+
+/// <summary>
+/// General delimiters serve as the delimiters between different uri components.
+/// General delimiters include:
+/// - All of these :/?#[]@
+/// </summary>
+inline bool is_gen_delim(int c)
+{
+    return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@';
+}
+
+/// <summary>
+/// Subdelimiters are those characters that may have a defined meaning within component
+/// of a uri for a particular scheme. They do not serve as delimiters in any case between
+/// uri segments. sub_delimiters include:
+/// - All of these !$&'()*+,;=
+/// </summary>
+inline bool is_sub_delim(int c)
+{
+    switch (c)
+    {
+        case '!':
+        case '$':
+        case '&':
+        case '\'':
+        case '(':
+        case ')':
+        case '*':
+        case '+':
+        case ',':
+        case ';':
+        case '=': return true;
+        default: return false;
+    }
+}
+
+/// <summary>
+/// Reserved characters includes the general delimiters and sub delimiters. Some characters
+/// are neither reserved nor unreserved, and must be percent-encoded.
+/// </summary>
+inline bool is_reserved(int c) { return is_gen_delim(c) || is_sub_delim(c); }
+
+/// <summary>
+/// Legal characters in the scheme portion include:
+/// - Any alphanumeric character
+/// - '+' (plus)
+/// - '-' (hyphen)
+/// - '.' (period)
+///
+/// Note that the scheme must BEGIN with an alpha character.
+/// </summary>
+inline bool is_scheme_character(int c)
+{
+    return ::utility::details::is_alnum((char)c) || c == '+' || c == '-' || c == '.';
+}
+
+/// <summary>
+/// Legal characters in the user information portion include:
+/// - Any unreserved character
+/// - The percent character ('%'), and thus any percent-endcoded octet
+/// - The sub-delimiters
+/// - ':' (colon)
+/// </summary>
+inline bool is_user_info_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == ':'; }
+
+/// <summary>
+/// Legal characters in the authority portion include:
+/// - Any unreserved character
+/// - The percent character ('%'), and thus any percent-endcoded octet
+/// - The sub-delimiters
+/// - ':' (colon)
+/// - IPv6 requires '[]' allowed for it to be valid URI and passed to underlying platform for IPv6 support
+/// </summary>
+inline bool is_authority_character(int c)
+{
+    return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == '@' || c == ':' || c == '[' || c == ']';
+}
+
+/// <summary>
+/// Legal characters in the path portion include:
+/// - Any unreserved character
+/// - The percent character ('%'), and thus any percent-endcoded octet
+/// - The sub-delimiters
+/// - ':' (colon)
+/// - '@' (at sign)
+/// </summary>
+inline bool is_path_character(int c)
+{
+    return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == '/' || c == ':' || c == '@';
+}
+
+/// <summary>
+/// Legal characters in the query portion include:
+/// - Any path character
+/// - '?' (question mark)
+/// </summary>
+inline bool is_query_character(int c) { return is_path_character(c) || c == '?'; }
+
+/// <summary>
+/// Legal characters in the fragment portion include:
+/// - Any path character
+/// - '?' (question mark)
+/// </summary>
+inline bool is_fragment_character(int c)
+{
+    // this is intentional, they have the same set of legal characters
+    return is_query_character(c);
+}
+
+struct inner_parse_out
+{
+    const utility::char_t* scheme_begin = nullptr;
+    const utility::char_t* scheme_end = nullptr;
+    const utility::char_t* uinfo_begin = nullptr;
+    const utility::char_t* uinfo_end = nullptr;
+    const utility::char_t* host_begin = nullptr;
+    const utility::char_t* host_end = nullptr;
+    int port = 0;
+    const utility::char_t* path_begin = nullptr;
+    const utility::char_t* path_end = nullptr;
+    const utility::char_t* query_begin = nullptr;
+    const utility::char_t* query_end = nullptr;
+    const utility::char_t* fragment_begin = nullptr;
+    const utility::char_t* fragment_end = nullptr;
+
+    /// <summary>
+    /// Parses the uri, setting the given pointers to locations inside the given buffer.
+    /// 'encoded' is expected to point to an encoded zero-terminated string containing a uri
+    /// </summary>
+    bool parse_from(const utility::char_t* encoded)
+    {
+        const utility::char_t* p = encoded;
+
+        // IMPORTANT -- A uri may either be an absolute uri, or an relative-reference
+        // Absolute: 'http://host.com'
+        // Relative-Reference: '//:host.com', '/path1/path2?query', './path1:path2'
+        // A Relative-Reference can be disambiguated by parsing for a ':' before the first slash
+
+        bool is_relative_reference = true;
+        const utility::char_t* p2 = p;
+        for (; *p2 != _XPLATSTR('/') && *p2 != _XPLATSTR('\0'); p2++)
+        {
+            if (*p2 == _XPLATSTR(':'))
+            {
+                // found a colon, the first portion is a scheme
+                is_relative_reference = false;
+                break;
+            }
+        }
+
+        if (!is_relative_reference)
+        {
+            // the first character of a scheme must be a letter
+            if (!isalpha(*p))
+            {
+                return false;
+            }
+
+            // start parsing the scheme, it's always delimited by a colon (must be present)
+            scheme_begin = p++;
+            for (; *p != ':'; p++)
+            {
+                if (!is_scheme_character(*p))
+                {
+                    return false;
+                }
+            }
+            scheme_end = p;
+
+            // skip over the colon
+            p++;
+        }
+
+        // if we see two slashes next, then we're going to parse the authority portion
+        // later on we'll break up the authority into the port and host
+        const utility::char_t* authority_begin = nullptr;
+        const utility::char_t* authority_end = nullptr;
+        if (*p == _XPLATSTR('/') && p[1] == _XPLATSTR('/'))
+        {
+            // skip over the slashes
+            p += 2;
+            authority_begin = p;
+
+            // the authority is delimited by a slash (resource), question-mark (query) or octothorpe (fragment)
+            // or by EOS. The authority could be empty ('file:///C:\file_name.txt')
+            for (; *p != _XPLATSTR('/') && *p != _XPLATSTR('?') && *p != _XPLATSTR('#') && *p != _XPLATSTR('\0'); p++)
+            {
+                // We're NOT currently supporting IPvFuture or username/password in authority
+                // IPv6 as the host (i.e. http://[:::::::]) is allowed as valid URI and passed to subsystem for support.
+                if (!is_authority_character(*p))
+                {
+                    return false;
+                }
+            }
+            authority_end = p;
+
+            // now lets see if we have a port specified -- by working back from the end
+            if (authority_begin != authority_end)
+            {
+                // the port is made up of all digits
+                const utility::char_t* port_begin = authority_end - 1;
+                for (; isdigit(*port_begin) && port_begin != authority_begin; port_begin--)
+                {
+                }
+
+                if (*port_begin == _XPLATSTR(':'))
+                {
+                    // has a port
+                    host_begin = authority_begin;
+                    host_end = port_begin;
+
+                    // skip the colon
+                    port_begin++;
+
+                    port =
+                        utility::conversions::details::scan_string<int>(utility::string_t(port_begin, authority_end));
+                }
+                else
+                {
+                    // no port
+                    host_begin = authority_begin;
+                    host_end = authority_end;
+                }
+
+                // look for a user_info component
+                const utility::char_t* u_end = host_begin;
+                for (; is_user_info_character(*u_end) && u_end != host_end; u_end++)
+                {
+                }
+
+                if (*u_end == _XPLATSTR('@'))
+                {
+                    host_begin = u_end + 1;
+                    uinfo_begin = authority_begin;
+                    uinfo_end = u_end;
+                }
+            }
+        }
+
+        // if we see a path character or a slash, then the
+        // if we see a slash, or any other legal path character, parse the path next
+        if (*p == _XPLATSTR('/') || is_path_character(*p))
+        {
+            path_begin = p;
+
+            // the path is delimited by a question-mark (query) or octothorpe (fragment) or by EOS
+            for (; *p != _XPLATSTR('?') && *p != _XPLATSTR('#') && *p != _XPLATSTR('\0'); p++)
+            {
+                if (!is_path_character(*p))
+                {
+                    return false;
+                }
+            }
+            path_end = p;
+        }
+
+        // if we see a ?, then the query is next
+        if (*p == _XPLATSTR('?'))
+        {
+            // skip over the question mark
+            p++;
+            query_begin = p;
+
+            // the query is delimited by a '#' (fragment) or EOS
+            for (; *p != _XPLATSTR('#') && *p != _XPLATSTR('\0'); p++)
+            {
+                if (!is_query_character(*p))
+                {
+                    return false;
+                }
+            }
+            query_end = p;
+        }
+
+        // if we see a #, then the fragment is next
+        if (*p == _XPLATSTR('#'))
+        {
+            // skip over the hash mark
+            p++;
+            fragment_begin = p;
+
+            // the fragment is delimited by EOS
+            for (; *p != _XPLATSTR('\0'); p++)
+            {
+                if (!is_fragment_character(*p))
+                {
+                    return false;
+                }
+            }
+            fragment_end = p;
+        }
+
+        return true;
+    }
+
+    void write_to(uri_components& components)
+    {
+        if (scheme_begin)
+        {
+            components.m_scheme.assign(scheme_begin, scheme_end);
+            utility::details::inplace_tolower(components.m_scheme);
+        }
+        else
+        {
+            components.m_scheme.clear();
+        }
+
+        if (uinfo_begin)
+        {
+            components.m_user_info.assign(uinfo_begin, uinfo_end);
+        }
+
+        if (host_begin)
+        {
+            components.m_host.assign(host_begin, host_end);
+            utility::details::inplace_tolower(components.m_host);
+        }
+        else
+        {
+            components.m_host.clear();
+        }
+
+        components.m_port = port;
+
+        if (path_begin)
+        {
+            components.m_path.assign(path_begin, path_end);
+        }
+        else
+        {
+            // default path to begin with a slash for easy comparison
+            components.m_path = _XPLATSTR("/");
+        }
+
+        if (query_begin)
+        {
+            components.m_query.assign(query_begin, query_end);
+        }
+        else
+        {
+            components.m_query.clear();
+        }
+
+        if (fragment_begin)
+        {
+            components.m_fragment.assign(fragment_begin, fragment_end);
+        }
+        else
+        {
+            components.m_fragment.clear();
+        }
+    }
+};
+
+// Encodes all characters not in given set determined by given function.
+template<class F>
+utility::string_t encode_impl(const utf8string& raw, F should_encode)
+{
+    const utility::char_t* const hex = _XPLATSTR("0123456789ABCDEF");
+    utility::string_t encoded;
+    for (auto iter = raw.begin(); iter != raw.end(); ++iter)
+    {
+        // for utf8 encoded string, char ASCII can be greater than 127.
+        int ch = static_cast<unsigned char>(*iter);
+        // ch should be same under both utf8 and utf16.
+        if (should_encode(ch))
+        {
+            encoded.push_back(_XPLATSTR('%'));
+            encoded.push_back(hex[(ch >> 4) & 0xF]);
+            encoded.push_back(hex[ch & 0xF]);
+        }
+        else
+        {
+            // ASCII don't need to be encoded, which should be same on both utf8 and utf16.
+            encoded.push_back((utility::char_t)ch);
+        }
+    }
+    return encoded;
+}
+
+// 5.2.3. Merge Paths https://tools.ietf.org/html/rfc3986#section-5.2.3
+utility::string_t mergePaths(const utility::string_t& base, const utility::string_t& relative)
+{
+    const auto lastSlash = base.rfind(_XPLATSTR('/'));
+    if (lastSlash == utility::string_t::npos)
+    {
+        return base + _XPLATSTR('/') + relative;
+    }
+    else if (lastSlash == base.size() - 1)
+    {
+        return base + relative;
+    }
+    // path contains and does not end with '/', we remove segment after last '/'
+    return base.substr(0, lastSlash + 1) + relative;
+}
+
+// 5.2.4. Remove Dot Segments https://tools.ietf.org/html/rfc3986#section-5.2.4
+void removeDotSegments(uri_builder& builder)
+{
+    const ::utility::string_t dotSegment = _XPLATSTR(".");
+    const ::utility::string_t dotDotSegment = _XPLATSTR("..");
+
+    if (builder.path().find(_XPLATSTR('.')) == utility::string_t::npos) return;
+
+    const auto segments = uri::split_path(builder.path());
+    std::vector<std::reference_wrapper<const utility::string_t>> result;
+    for (auto& segment : segments)
+    {
+        if (segment == dotSegment)
+            continue;
+        else if (segment != dotDotSegment)
+            result.push_back(segment);
+        else if (!result.empty())
+            result.pop_back();
+    }
+    if (result.empty())
+    {
+        builder.set_path(utility::string_t());
+        return;
+    }
+    utility::string_t path = result.front().get();
+    for (size_t i = 1; i != result.size(); ++i)
+    {
+        path += _XPLATSTR('/');
+        path += result[i].get();
+    }
+    if (segments.back() == dotDotSegment || segments.back() == dotSegment || builder.path().back() == _XPLATSTR('/'))
+    {
+        path += _XPLATSTR('/');
+    }
+
+    builder.set_path(std::move(path));
+}
+} // namespace
+
+utility::string_t uri_components::join()
+{
+    // canonicalize components first
+
+    // convert scheme to lowercase
+    utility::details::inplace_tolower(m_scheme);
+    // convert host to lowercase
+    utility::details::inplace_tolower(m_host);
+
+    // canonicalize the path to have a leading slash if it's a full uri
+    if (!m_host.empty() && m_path.empty())
+    {
+        m_path = _XPLATSTR("/");
+    }
+    else if (!m_host.empty() && m_path[0] != _XPLATSTR('/'))
+    {
+        m_path.insert(m_path.begin(), 1, _XPLATSTR('/'));
+    }
+
+    utility::string_t ret;
+
+    if (!m_scheme.empty())
+    {
+        ret.append(m_scheme);
+        ret.push_back(_XPLATSTR(':'));
+    }
+
+    if (!m_host.empty())
+    {
+        ret.append(_XPLATSTR("//"));
+
+        if (!m_user_info.empty())
+        {
+            ret.append(m_user_info).append({_XPLATSTR('@')});
+        }
+
+        ret.append(m_host);
+
+        if (m_port > 0)
+        {
+            ret.append({_XPLATSTR(':')}).append(utility::conversions::details::to_string_t(m_port));
+        }
+    }
+
+    if (!m_path.empty())
+    {
+        // only add the leading slash when the host is present
+        if (!m_host.empty() && m_path.front() != _XPLATSTR('/'))
+        {
+            ret.push_back(_XPLATSTR('/'));
+        }
+
+        ret.append(m_path);
+    }
+
+    if (!m_query.empty())
+    {
+        ret.push_back(_XPLATSTR('?'));
+        ret.append(m_query);
+    }
+
+    if (!m_fragment.empty())
+    {
+        ret.push_back(_XPLATSTR('#'));
+        ret.append(m_fragment);
+    }
+
+    return ret;
+}
+} // namespace details
+
+uri::uri(const details::uri_components& components) : m_components(components)
+{
+    m_uri = m_components.join();
+
+    if (!uri::validate(m_uri.c_str()))
+    {
+        throw uri_exception("provided uri is invalid: " + utility::conversions::to_utf8string(m_uri));
+    }
+}
+
+uri::uri(const utility::string_t& uri_string) : uri(uri_string.c_str()) {}
+
+uri::uri(const utility::char_t* uri_string)
+{
+    details::inner_parse_out out;
+
+    if (!out.parse_from(uri_string))
+    {
+        throw uri_exception("provided uri is invalid: " + utility::conversions::to_utf8string(uri_string));
+    }
+
+    out.write_to(m_components);
+    m_uri = m_components.join();
+}
+
+utility::string_t uri::encode_query_impl(const utf8string& raw)
+{
+    return details::encode_impl(raw, [](int ch) -> bool {
+        switch (ch)
+        {
+                // Encode '&', ';', and '=' since they are used
+                // as delimiters in query component.
+            case '&':
+            case ';':
+            case '=':
+            case '%':
+            case '+': return true;
+            default: return !details::is_query_character(ch);
+        }
+    });
+}
+
+/// </summary>
+/// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their
+/// hexadecimal representation.
+/// </summary>
+utility::string_t uri::encode_data_string(const utility::string_t& data)
+{
+    auto&& raw = utility::conversions::to_utf8string(data);
+
+    return details::encode_impl(raw, [](int ch) -> bool { return !details::is_unreserved(ch); });
+}
+
+utility::string_t uri::encode_uri(const utility::string_t& raw, uri::components::component component)
+{
+    auto&& raw_utf8 = utility::conversions::to_utf8string(raw);
+
+    // Note: we also encode the '+' character because some non-standard implementations
+    // encode the space character as a '+' instead of %20. To better interoperate we encode
+    // '+' to avoid any confusion and be mistaken as a space.
+    switch (component)
+    {
+        case components::user_info:
+            return details::encode_impl(raw_utf8, [](int ch) -> bool {
+                return !details::is_user_info_character(ch) || ch == '%' || ch == '+';
+            });
+        case components::host:
+            return details::encode_impl(raw_utf8, [](int ch) -> bool {
+                // No encoding of ASCII characters in host name (RFC 3986 3.2.2)
+                return ch > 127;
+            });
+        case components::path:
+            return details::encode_impl(
+                raw_utf8, [](int ch) -> bool { return !details::is_path_character(ch) || ch == '%' || ch == '+'; });
+        case components::query:
+            return details::encode_impl(
+                raw_utf8, [](int ch) -> bool { return !details::is_query_character(ch) || ch == '%' || ch == '+'; });
+        case components::fragment:
+            return details::encode_impl(
+                raw_utf8, [](int ch) -> bool { return !details::is_fragment_character(ch) || ch == '%' || ch == '+'; });
+        case components::full_uri:
+        default:
+            return details::encode_impl(
+                raw_utf8, [](int ch) -> bool { return !details::is_unreserved(ch) && !details::is_reserved(ch); });
+    };
+}
+
+/// <summary>
+/// Helper function to convert a hex character digit to a decimal character value.
+/// Throws an exception if not a valid hex digit.
+/// </summary>
+static int hex_char_digit_to_decimal_char(int hex)
+{
+    int decimal;
+    if (hex >= '0' && hex <= '9')
+    {
+        decimal = hex - '0';
+    }
+    else if (hex >= 'A' && hex <= 'F')
+    {
+        decimal = 10 + (hex - 'A');
+    }
+    else if (hex >= 'a' && hex <= 'f')
+    {
+        decimal = 10 + (hex - 'a');
+    }
+    else
+    {
+        throw uri_exception("Invalid hexadecimal digit");
+    }
+    return decimal;
+}
+
+template<class String>
+static std::string decode_template(const String& encoded)
+{
+    std::string raw;
+    for (auto iter = encoded.begin(); iter != encoded.end(); ++iter)
+    {
+        if (*iter == '%')
+        {
+            if (++iter == encoded.end())
+            {
+                throw uri_exception("Invalid URI string, two hexadecimal digits must follow '%'");
+            }
+            int decimal_value = hex_char_digit_to_decimal_char(static_cast<int>(*iter)) << 4;
+            if (++iter == encoded.end())
+            {
+                throw uri_exception("Invalid URI string, two hexadecimal digits must follow '%'");
+            }
+            decimal_value += hex_char_digit_to_decimal_char(static_cast<int>(*iter));
+
+            raw.push_back(static_cast<char>(decimal_value));
+        }
+        else if (*iter > 127 || *iter < 0)
+        {
+            throw uri_exception("Invalid encoded URI string, must be entirely ascii");
+        }
+        else
+        {
+            // encoded string has to be ASCII.
+            raw.push_back(static_cast<char>(*iter));
+        }
+    }
+    return raw;
+}
+
+utility::string_t uri::decode(const utility::string_t& encoded) { return to_string_t(decode_template(encoded)); }
+
+std::vector<utility::string_t> uri::split_path(const utility::string_t& path)
+{
+    std::vector<utility::string_t> results;
+    utility::istringstream_t iss(path);
+    iss.imbue(std::locale::classic());
+    utility::string_t s;
+
+    while (std::getline(iss, s, _XPLATSTR('/')))
+    {
+        if (!s.empty())
+        {
+            results.push_back(s);
+        }
+    }
+
+    return results;
+}
+
+std::map<utility::string_t, utility::string_t> uri::split_query(const utility::string_t& query)
+{
+    std::map<utility::string_t, utility::string_t> results;
+
+    // Split into key value pairs separated by '&'.
+    size_t prev_amp_index = 0;
+    while (prev_amp_index != utility::string_t::npos)
+    {
+        size_t amp_index = query.find_first_of(_XPLATSTR('&'), prev_amp_index);
+        if (amp_index == utility::string_t::npos) amp_index = query.find_first_of(_XPLATSTR(';'), prev_amp_index);
+
+        utility::string_t key_value_pair = query.substr(
+            prev_amp_index,
+            amp_index == utility::string_t::npos ? query.size() - prev_amp_index : amp_index - prev_amp_index);
+        prev_amp_index = amp_index == utility::string_t::npos ? utility::string_t::npos : amp_index + 1;
+
+        size_t equals_index = key_value_pair.find_first_of(_XPLATSTR('='));
+        if (equals_index == utility::string_t::npos)
+        {
+            continue;
+        }
+        else if (equals_index == 0)
+        {
+            utility::string_t value(key_value_pair.begin() + equals_index + 1, key_value_pair.end());
+            results[utility::string_t {}] = value;
+        }
+        else
+        {
+            utility::string_t key(key_value_pair.begin(), key_value_pair.begin() + equals_index);
+            utility::string_t value(key_value_pair.begin() + equals_index + 1, key_value_pair.end());
+            results[key] = value;
+        }
+    }
+
+    return results;
+}
+
+bool uri::validate(const utility::string_t& uri_string)
+{
+    details::inner_parse_out out;
+    return out.parse_from(uri_string.c_str());
+}
+
+uri uri::authority() const
+{
+    return uri_builder()
+        .set_scheme(this->scheme())
+        .set_host(this->host())
+        .set_port(this->port())
+        .set_user_info(this->user_info())
+        .to_uri();
+}
+
+uri uri::resource() const
+{
+    return uri_builder().set_path(this->path()).set_query(this->query()).set_fragment(this->fragment()).to_uri();
+}
+
+bool uri::operator==(const uri& other) const
+{
+    // Each individual URI component must be decoded before performing comparison.
+    // TFS # 375865
+
+    if (this->is_empty() && other.is_empty())
+    {
+        return true;
+    }
+    else if (this->is_empty() || other.is_empty())
+    {
+        return false;
+    }
+    else if (this->scheme() != other.scheme())
+    {
+        // scheme is canonicalized to lowercase
+        return false;
+    }
+    else if (uri::decode(this->user_info()) != uri::decode(other.user_info()))
+    {
+        return false;
+    }
+    else if (uri::decode(this->host()) != uri::decode(other.host()))
+    {
+        // host is canonicalized to lowercase
+        return false;
+    }
+    else if (this->port() != other.port())
+    {
+        return false;
+    }
+    else if (uri::decode(this->path()) != uri::decode(other.path()))
+    {
+        return false;
+    }
+    else if (uri::decode(this->query()) != uri::decode(other.query()))
+    {
+        return false;
+    }
+    else if (uri::decode(this->fragment()) != uri::decode(other.fragment()))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+// resolving URI according to RFC3986, Section 5 https://tools.ietf.org/html/rfc3986#section-5
+utility::string_t uri::resolve_uri(const utility::string_t& relativeUri) const
+{
+    if (relativeUri.empty())
+    {
+        return to_string();
+    }
+
+    if (relativeUri[0] == _XPLATSTR('/')) // starts with '/'
+    {
+        if (relativeUri.size() >= 2 && relativeUri[1] == _XPLATSTR('/')) // starts with '//'
+        {
+            return this->scheme() + _XPLATSTR(':') + relativeUri;
+        }
+
+        // otherwise relative to root
+        auto builder = uri_builder(this->authority());
+        builder.append(relativeUri);
+        details::removeDotSegments(builder);
+        return builder.to_string();
+    }
+
+    const auto url = uri(relativeUri);
+    if (!url.scheme().empty()) return relativeUri;
+
+    if (!url.authority().is_empty())
+    {
+        return uri_builder(url).set_scheme(this->scheme()).to_string();
+    }
+
+    // relative url
+    auto builder = uri_builder(*this);
+    if (url.path() == _XPLATSTR("/") || url.path().empty()) // web::uri considers empty path as '/'
+    {
+        if (!url.query().empty())
+        {
+            builder.set_query(url.query());
+        }
+    }
+    else if (!this->path().empty())
+    {
+        builder.set_path(details::mergePaths(this->path(), url.path()));
+        details::removeDotSegments(builder);
+        builder.set_query(url.query());
+    }
+
+    return builder.set_fragment(url.fragment()).to_string();
+}
+
+} // namespace web
diff --git a/Release/src/uri/uri_builder.cpp b/Release/src/uri/uri_builder.cpp
new file mode 100644 (file)
index 0000000..e1bb0a1
--- /dev/null
@@ -0,0 +1,178 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Builder for constructing URIs.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+static const utility::string_t oneSlash = _XPLATSTR("/");
+
+namespace web
+{
+uri_builder& uri_builder::append_path(const utility::string_t& toAppend, bool do_encode)
+{
+    if (!toAppend.empty() && toAppend != oneSlash)
+    {
+        auto& thisPath = m_uri.m_path;
+        if (&thisPath == &toAppend)
+        {
+            auto appendCopy = toAppend;
+            return append_path(appendCopy, do_encode);
+        }
+
+        if (thisPath.empty() || thisPath == oneSlash)
+        {
+            thisPath.clear();
+            if (toAppend.front() != _XPLATSTR('/'))
+            {
+                thisPath.push_back(_XPLATSTR('/'));
+            }
+        }
+        else if (thisPath.back() == _XPLATSTR('/') && toAppend.front() == _XPLATSTR('/'))
+        {
+            thisPath.pop_back();
+        }
+        else if (thisPath.back() != _XPLATSTR('/') && toAppend.front() != _XPLATSTR('/'))
+        {
+            thisPath.push_back(_XPLATSTR('/'));
+        }
+        else
+        {
+            // Only one slash.
+        }
+
+        if (do_encode)
+        {
+            thisPath.append(uri::encode_uri(toAppend, uri::components::path));
+        }
+        else
+        {
+            thisPath.append(toAppend);
+        }
+    }
+
+    return *this;
+}
+
+uri_builder& uri_builder::append_path_raw(const utility::string_t& toAppend, bool do_encode)
+{
+    if (!toAppend.empty())
+    {
+        auto& thisPath = m_uri.m_path;
+        if (&thisPath == &toAppend)
+        {
+            auto appendCopy = toAppend;
+            return append_path_raw(appendCopy, do_encode);
+        }
+
+        if (thisPath != oneSlash)
+        {
+            thisPath.push_back(_XPLATSTR('/'));
+        }
+
+        if (do_encode)
+        {
+            thisPath.append(uri::encode_uri(toAppend, uri::components::path));
+        }
+        else
+        {
+            thisPath.append(toAppend);
+        }
+    }
+
+    return *this;
+}
+
+uri_builder& uri_builder::append_query(const utility::string_t& toAppend, bool do_encode)
+{
+    if (!toAppend.empty())
+    {
+        auto& thisQuery = m_uri.m_query;
+        if (&thisQuery == &toAppend)
+        {
+            auto appendCopy = toAppend;
+            return append_query(appendCopy, do_encode);
+        }
+
+        if (thisQuery.empty())
+        {
+            thisQuery.clear();
+        }
+        else if (thisQuery.back() == _XPLATSTR('&') && toAppend.front() == _XPLATSTR('&'))
+        {
+            thisQuery.pop_back();
+        }
+        else if (thisQuery.back() != _XPLATSTR('&') && toAppend.front() != _XPLATSTR('&'))
+        {
+            thisQuery.push_back(_XPLATSTR('&'));
+        }
+        else
+        {
+            // Only one ampersand.
+        }
+
+        if (do_encode)
+        {
+            thisQuery.append(uri::encode_uri(toAppend, uri::components::query));
+        }
+        else
+        {
+            thisQuery.append(toAppend);
+        }
+    }
+
+    return *this;
+}
+
+uri_builder& uri_builder::set_port(const utility::string_t& port)
+{
+    utility::istringstream_t portStream(port);
+    portStream.imbue(std::locale::classic());
+    int port_tmp;
+    portStream >> port_tmp;
+    if (portStream.fail() || portStream.bad())
+    {
+        throw std::invalid_argument("invalid port argument, must be non empty string containing integer value");
+    }
+    m_uri.m_port = port_tmp;
+    return *this;
+}
+
+uri_builder& uri_builder::append(const http::uri& relative_uri)
+{
+    append_path(relative_uri.path());
+    append_query(relative_uri.query());
+    this->set_fragment(this->fragment() + relative_uri.fragment());
+    return *this;
+}
+
+utility::string_t uri_builder::to_string() const { return to_uri().to_string(); }
+
+uri uri_builder::to_uri() const { return uri(m_uri); }
+
+bool uri_builder::is_valid() { return uri::validate(m_uri.join()); }
+
+void uri_builder::append_query_encode_impl(const utility::string_t& name, const utf8string& value)
+{
+    utility::string_t encodedQuery = uri::encode_query_impl(utility::conversions::to_utf8string(name));
+    encodedQuery.push_back(_XPLATSTR('='));
+    encodedQuery.append(uri::encode_query_impl(value));
+
+    // The query key value pair was already encoded by us or the user separately.
+    append_query(encodedQuery, false);
+}
+
+void uri_builder::append_query_no_encode_impl(const utility::string_t& name, const utility::string_t& value)
+{
+    append_query(name + _XPLATSTR("=") + value, false);
+}
+
+} // namespace web
diff --git a/Release/src/utilities/Resource.rc b/Release/src/utilities/Resource.rc
new file mode 100644 (file)
index 0000000..bb48f87
Binary files /dev/null and b/Release/src/utilities/Resource.rc differ
diff --git a/Release/src/utilities/asyncrt_utils.cpp b/Release/src/utilities/asyncrt_utils.cpp
new file mode 100644 (file)
index 0000000..cf747c6
--- /dev/null
@@ -0,0 +1,1518 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Utilities
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <algorithm>
+#include <cpprest/asyncrt_utils.h>
+#include <stdexcept>
+#include <string>
+#include <time.h>
+
+using namespace web;
+using namespace utility;
+using namespace utility::conversions;
+
+namespace
+{
+struct to_lower_ch_impl
+{
+    char operator()(char c) const CPPREST_NOEXCEPT
+    {
+        if (c >= 'A' && c <= 'Z') return static_cast<char>(c - 'A' + 'a');
+        return c;
+    }
+
+    wchar_t operator()(wchar_t c) const CPPREST_NOEXCEPT
+    {
+        if (c >= L'A' && c <= L'Z') return static_cast<wchar_t>(c - L'A' + L'a');
+        return c;
+    }
+};
+
+CPPREST_CONSTEXPR to_lower_ch_impl to_lower_ch {};
+
+struct eq_lower_ch_impl
+{
+    template<class CharT>
+    inline CharT operator()(const CharT left, const CharT right) const CPPREST_NOEXCEPT
+    {
+        return to_lower_ch(left) == to_lower_ch(right);
+    }
+};
+
+CPPREST_CONSTEXPR eq_lower_ch_impl eq_lower_ch {};
+
+struct lt_lower_ch_impl
+{
+    template<class CharT>
+    inline CharT operator()(const CharT left, const CharT right) const CPPREST_NOEXCEPT
+    {
+        return to_lower_ch(left) < to_lower_ch(right);
+    }
+};
+
+CPPREST_CONSTEXPR lt_lower_ch_impl lt_lower_ch {};
+} // namespace
+
+namespace utility
+{
+namespace details
+{
+_ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT
+{
+    return left.size() == right.size() && std::equal(left.cbegin(), left.cend(), right.cbegin(), eq_lower_ch);
+}
+
+_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT
+{
+    return left.size() == right.size() && std::equal(left.cbegin(), left.cend(), right.cbegin(), eq_lower_ch);
+}
+
+_ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT
+{
+    return std::lexicographical_compare(left.cbegin(), left.cend(), right.cbegin(), right.cend(), lt_lower_ch);
+}
+
+_ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT
+{
+    return std::lexicographical_compare(left.cbegin(), left.cend(), right.cbegin(), right.cend(), lt_lower_ch);
+}
+
+_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT
+{
+    for (auto& ch : target)
+    {
+        ch = to_lower_ch(ch);
+    }
+}
+
+_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT
+{
+    for (auto& ch : target)
+    {
+        ch = to_lower_ch(ch);
+    }
+}
+
+#if !defined(ANDROID) && !defined(__ANDROID__)
+std::once_flag g_c_localeFlag;
+std::unique_ptr<scoped_c_thread_locale::xplat_locale, void (*)(scoped_c_thread_locale::xplat_locale*)> g_c_locale(
+    nullptr, [](scoped_c_thread_locale::xplat_locale*) {});
+scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale()
+{
+    std::call_once(g_c_localeFlag, [&]() {
+        scoped_c_thread_locale::xplat_locale* clocale = new scoped_c_thread_locale::xplat_locale();
+#ifdef _WIN32
+        *clocale = _create_locale(LC_ALL, "C");
+        if (clocale == nullptr || *clocale == nullptr)
+        {
+            throw std::runtime_error("Unable to create 'C' locale.");
+        }
+        auto deleter = [](scoped_c_thread_locale::xplat_locale* clocale) {
+            _free_locale(*clocale);
+            delete clocale;
+        };
+#else
+        *clocale = newlocale(LC_ALL_MASK, "C", nullptr);
+        if (clocale == nullptr || *clocale == nullptr)
+        {
+            throw std::runtime_error("Unable to create 'C' locale.");
+        }
+        auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale)
+        {
+            freelocale(*clocale);
+            delete clocale;
+        };
+#endif
+        g_c_locale =
+            std::unique_ptr<scoped_c_thread_locale::xplat_locale, void (*)(scoped_c_thread_locale::xplat_locale*)>(
+                clocale, deleter);
+    });
+    return *g_c_locale;
+}
+#endif
+
+#ifdef _WIN32
+scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(), m_prevThreadSetting(-1)
+{
+    char* prevLocale = setlocale(LC_ALL, nullptr);
+    if (prevLocale == nullptr)
+    {
+        throw std::runtime_error("Unable to retrieve current locale.");
+    }
+
+    if (std::strcmp(prevLocale, "C") != 0)
+    {
+        m_prevLocale = prevLocale;
+        m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+        if (m_prevThreadSetting == -1)
+        {
+            throw std::runtime_error("Unable to enable per thread locale.");
+        }
+        if (setlocale(LC_ALL, "C") == nullptr)
+        {
+            _configthreadlocale(m_prevThreadSetting);
+            throw std::runtime_error("Unable to set locale");
+        }
+    }
+}
+
+scoped_c_thread_locale::~scoped_c_thread_locale()
+{
+    if (m_prevThreadSetting != -1)
+    {
+        setlocale(LC_ALL, m_prevLocale.c_str());
+        _configthreadlocale(m_prevThreadSetting);
+    }
+}
+#elif (defined(ANDROID) || defined(__ANDROID__))
+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)
+{
+    char* prevLocale = setlocale(LC_ALL, nullptr);
+    if (prevLocale == nullptr)
+    {
+        throw std::runtime_error("Unable to retrieve current locale.");
+    }
+
+    if (std::strcmp(prevLocale, "C") != 0)
+    {
+        m_prevLocale = uselocale(c_locale());
+        if (m_prevLocale == nullptr)
+        {
+            throw std::runtime_error("Unable to set locale");
+        }
+    }
+}
+
+scoped_c_thread_locale::~scoped_c_thread_locale()
+{
+    if (m_prevLocale != nullptr)
+    {
+        uselocale(m_prevLocale);
+    }
+}
+#endif
+} // namespace details
+
+namespace details
+{
+const std::error_category& __cdecl platform_category()
+{
+#ifdef _WIN32
+    return windows_category();
+#else
+    return linux_category();
+#endif
+}
+
+#ifdef _WIN32
+
+// Remove once VS 2013 is no longer supported.
+#if _MSC_VER < 1900
+static details::windows_category_impl instance;
+#endif
+const std::error_category& __cdecl windows_category()
+{
+#if _MSC_VER >= 1900
+    static details::windows_category_impl instance;
+#endif
+    return instance;
+}
+
+std::string windows_category_impl::message(int errorCode) const CPPREST_NOEXCEPT
+{
+    const size_t buffer_size = 4096;
+    DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
+    LPCVOID lpSource = NULL;
+
+#if !defined(__cplusplus_winrt)
+    if (errorCode >= 12000)
+    {
+        dwFlags = FORMAT_MESSAGE_FROM_HMODULE;
+        lpSource = GetModuleHandleA("winhttp.dll"); // this handle DOES NOT need to be freed
+    }
+#endif
+
+    std::wstring buffer(buffer_size, 0);
+
+    const auto result = ::FormatMessageW(dwFlags, lpSource, errorCode, 0, &buffer[0], buffer_size, NULL);
+
+    if (result == 0)
+    {
+        return "Unable to get an error message for error code: " + std::to_string(errorCode) + ".";
+    }
+
+    // strip exceeding characters of the initial resize call
+    buffer.resize(result);
+
+    return utility::conversions::to_utf8string(buffer);
+}
+
+std::error_condition windows_category_impl::default_error_condition(int errorCode) const CPPREST_NOEXCEPT
+{
+    // First see if the STL implementation can handle the mapping for common cases.
+    const std::error_condition errCondition = std::system_category().default_error_condition(errorCode);
+    const std::string errConditionMsg = errCondition.message();
+    if (!utility::details::str_iequal(errConditionMsg, "unknown error"))
+    {
+        return errCondition;
+    }
+
+    switch (errorCode)
+    {
+#ifndef __cplusplus_winrt
+        case ERROR_WINHTTP_TIMEOUT: return std::errc::timed_out;
+        case ERROR_WINHTTP_CANNOT_CONNECT: return std::errc::host_unreachable;
+        case ERROR_WINHTTP_CONNECTION_ERROR: return std::errc::connection_aborted;
+#endif
+        case INET_E_RESOURCE_NOT_FOUND:
+        case INET_E_CANNOT_CONNECT: return std::errc::host_unreachable;
+        case INET_E_CONNECTION_TIMEOUT: return std::errc::timed_out;
+        case INET_E_DOWNLOAD_FAILURE: return std::errc::connection_aborted;
+        default: break;
+    }
+
+    return std::error_condition(errorCode, *this);
+}
+
+#else
+
+const std::error_category& __cdecl linux_category()
+{
+    // On Linux we are using boost error codes which have the exact same
+    // mapping and are equivalent with std::generic_category error codes.
+    return std::generic_category();
+}
+
+#endif
+
+} // namespace details
+
+#define LOW_3BITS 0x7
+#define LOW_4BITS 0xF
+#define LOW_5BITS 0x1F
+#define LOW_6BITS 0x3F
+#define BIT4 0x8
+#define BIT5 0x10
+#define BIT6 0x20
+#define BIT7 0x40
+#define BIT8 0x80
+#define L_SURROGATE_START 0xDC00
+#define L_SURROGATE_END 0xDFFF
+#define H_SURROGATE_START 0xD800
+#define H_SURROGATE_END 0xDBFF
+#define SURROGATE_PAIR_START 0x10000
+
+// Create a dedicated type for characters to avoid the issue
+// of different platforms defaulting char to be either signed
+// or unsigned.
+using UtilCharInternal_t = signed char;
+
+inline size_t count_utf8_to_utf16(const std::string& s)
+{
+    const size_t sSize = s.size();
+    auto const sData = reinterpret_cast<const UtilCharInternal_t*>(s.data());
+    size_t result {sSize};
+
+    for (size_t index = 0; index < sSize;)
+    {
+        if (sData[index] >= 0)
+        {
+            // use fast inner loop to skip single byte code points (which are
+            // expected to be the most frequent)
+            while ((++index < sSize) && (sData[index] >= 0))
+                ;
+
+            if (index >= sSize) break;
+        }
+
+        // start special handling for multi-byte code points
+        const UtilCharInternal_t c {sData[index++]};
+
+        if ((c & BIT7) == 0)
+        {
+            throw std::range_error("UTF-8 string character can never start with 10xxxxxx");
+        }
+        else if ((c & BIT6) == 0) // 2 byte character, 0x80 to 0x7FF
+        {
+            if (index == sSize)
+            {
+                throw std::range_error("UTF-8 string is missing bytes in character");
+            }
+
+            const UtilCharInternal_t c2 {sData[index++]};
+            if ((c2 & 0xC0) != BIT8)
+            {
+                throw std::range_error("UTF-8 continuation byte is missing leading bit mask");
+            }
+
+            // can't require surrogates for 7FF
+            --result;
+        }
+        else if ((c & BIT5) == 0) // 3 byte character, 0x800 to 0xFFFF
+        {
+            if (sSize - index < 2)
+            {
+                throw std::range_error("UTF-8 string is missing bytes in character");
+            }
+
+            const UtilCharInternal_t c2 {sData[index++]};
+            const UtilCharInternal_t c3 {sData[index++]};
+            if (((c2 | c3) & 0xC0) != BIT8)
+            {
+                throw std::range_error("UTF-8 continuation byte is missing leading bit mask");
+            }
+
+            result -= 2;
+        }
+        else if ((c & BIT4) == 0) // 4 byte character, 0x10000 to 0x10FFFF
+        {
+            if (sSize - index < 3)
+            {
+                throw std::range_error("UTF-8 string is missing bytes in character");
+            }
+
+            const UtilCharInternal_t c2 {sData[index++]};
+            const UtilCharInternal_t c3 {sData[index++]};
+            const UtilCharInternal_t c4 {sData[index++]};
+            if (((c2 | c3 | c4) & 0xC0) != BIT8)
+            {
+                throw std::range_error("UTF-8 continuation byte is missing leading bit mask");
+            }
+
+            const uint32_t codePoint =
+                ((c & LOW_3BITS) << 18) | ((c2 & LOW_6BITS) << 12) | ((c3 & LOW_6BITS) << 6) | (c4 & LOW_6BITS);
+            result -= (3 - (codePoint >= SURROGATE_PAIR_START));
+        }
+        else
+        {
+            throw std::range_error("UTF-8 string has invalid Unicode code point");
+        }
+    }
+
+    return result;
+}
+
+utf16string __cdecl conversions::utf8_to_utf16(const std::string& s)
+{
+    // Save repeated heap allocations, use the length of resulting sequence.
+    const size_t srcSize = s.size();
+    auto const srcData = reinterpret_cast<const UtilCharInternal_t*>(s.data());
+    utf16string dest(count_utf8_to_utf16(s), L'\0');
+    utf16string::value_type* const destData = &dest[0];
+    size_t destIndex = 0;
+
+    for (size_t index = 0; index < srcSize; ++index)
+    {
+        UtilCharInternal_t src = srcData[index];
+        switch (src & 0xF0)
+        {
+            case 0xF0: // 4 byte character, 0x10000 to 0x10FFFF
+            {
+                const UtilCharInternal_t c2 {srcData[++index]};
+                const UtilCharInternal_t c3 {srcData[++index]};
+                const UtilCharInternal_t c4 {srcData[++index]};
+                uint32_t codePoint =
+                    ((src & LOW_3BITS) << 18) | ((c2 & LOW_6BITS) << 12) | ((c3 & LOW_6BITS) << 6) | (c4 & LOW_6BITS);
+                if (codePoint >= SURROGATE_PAIR_START)
+                {
+                    // In UTF-16 U+10000 to U+10FFFF are represented as two 16-bit code units, surrogate pairs.
+                    //  - 0x10000 is subtracted from the code point
+                    //  - high surrogate is 0xD800 added to the top ten bits
+                    //  - low surrogate is 0xDC00 added to the low ten bits
+                    codePoint -= SURROGATE_PAIR_START;
+                    destData[destIndex++] = static_cast<utf16string::value_type>((codePoint >> 10) | H_SURROGATE_START);
+                    destData[destIndex++] =
+                        static_cast<utf16string::value_type>((codePoint & 0x3FF) | L_SURROGATE_START);
+                }
+                else
+                {
+                    // In UTF-16 U+0000 to U+D7FF and U+E000 to U+FFFF are represented exactly as the Unicode code point
+                    // value. U+D800 to U+DFFF are not valid characters, for simplicity we assume they are not present
+                    // but will encode them if encountered.
+                    destData[destIndex++] = static_cast<utf16string::value_type>(codePoint);
+                }
+            }
+            break;
+            case 0xE0: // 3 byte character, 0x800 to 0xFFFF
+            {
+                const UtilCharInternal_t c2 {srcData[++index]};
+                const UtilCharInternal_t c3 {srcData[++index]};
+                destData[destIndex++] = static_cast<utf16string::value_type>(
+                    ((src & LOW_4BITS) << 12) | ((c2 & LOW_6BITS) << 6) | (c3 & LOW_6BITS));
+            }
+            break;
+            case 0xD0: // 2 byte character, 0x80 to 0x7FF
+            case 0xC0:
+            {
+                const UtilCharInternal_t c2 {srcData[++index]};
+                destData[destIndex++] =
+                    static_cast<utf16string::value_type>(((src & LOW_5BITS) << 6) | (c2 & LOW_6BITS));
+            }
+            break;
+            default: // single byte character, 0x0 to 0x7F
+                // try to use a fast inner loop for following single byte characters,
+                // since they are quite probable
+                do
+                {
+                    destData[destIndex++] = static_cast<utf16string::value_type>(srcData[index++]);
+                } while (index < srcSize && srcData[index] > 0);
+                // adjust index since it will be incremented by the for loop
+                --index;
+        }
+    }
+    return dest;
+}
+
+inline size_t count_utf16_to_utf8(const utf16string& w)
+{
+    const utf16string::value_type* const srcData = &w[0];
+    const size_t srcSize = w.size();
+    size_t destSize(srcSize);
+    for (size_t index = 0; index < srcSize; ++index)
+    {
+        const utf16string::value_type ch(srcData[index]);
+        if (ch <= 0x7FF)
+        {
+            if (ch > 0x7F) // 2 bytes needed (11 bits used)
+            {
+                ++destSize;
+            }
+        }
+        // Check for high surrogate.
+        else if (ch >= H_SURROGATE_START && ch <= H_SURROGATE_END) // 4 bytes needed (21 bits used)
+        {
+            ++index;
+            if (index == srcSize)
+            {
+                throw std::range_error("UTF-16 string is missing low surrogate");
+            }
+
+            const auto lowSurrogate = srcData[index];
+            if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END)
+            {
+                throw std::range_error("UTF-16 string has invalid low surrogate");
+            }
+
+            destSize += 2;
+        }
+        else // 3 bytes needed (16 bits used)
+        {
+            destSize += 2;
+        }
+    }
+
+    return destSize;
+}
+
+std::string __cdecl conversions::utf16_to_utf8(const utf16string& w)
+{
+    const size_t srcSize = w.size();
+    const utf16string::value_type* const srcData = &w[0];
+    std::string dest(count_utf16_to_utf8(w), '\0');
+    std::string::value_type* const destData = &dest[0];
+    size_t destIndex(0);
+
+    for (size_t index = 0; index < srcSize; ++index)
+    {
+        const utf16string::value_type src = srcData[index];
+        if (src <= 0x7FF)
+        {
+            if (src <= 0x7F) // single byte character
+            {
+                destData[destIndex++] = static_cast<char>(src);
+            }
+            else // 2 bytes needed (11 bits used)
+            {
+                destData[destIndex++] = static_cast<char>(char((src >> 6) | 0xC0));        // leading 5 bits
+                destData[destIndex++] = static_cast<char>(char((src & LOW_6BITS) | BIT8)); // trailing 6 bits
+            }
+        }
+        // Check for high surrogate.
+        else if (src >= H_SURROGATE_START && src <= H_SURROGATE_END)
+        {
+            const auto highSurrogate = src;
+            const auto lowSurrogate = srcData[++index];
+
+            // To get from surrogate pair to Unicode code point:
+            // - subtract 0xD800 from high surrogate, this forms top ten bits
+            // - subtract 0xDC00 from low surrogate, this forms low ten bits
+            // - add 0x10000
+            // Leaves a code point in U+10000 to U+10FFFF range.
+            uint32_t codePoint = highSurrogate - H_SURROGATE_START;
+            codePoint <<= 10;
+            codePoint |= lowSurrogate - L_SURROGATE_START;
+            codePoint += SURROGATE_PAIR_START;
+
+            // 4 bytes needed (21 bits used)
+            destData[destIndex++] = static_cast<char>((codePoint >> 18) | 0xF0);               // leading 3 bits
+            destData[destIndex++] = static_cast<char>(((codePoint >> 12) & LOW_6BITS) | BIT8); // next 6 bits
+            destData[destIndex++] = static_cast<char>(((codePoint >> 6) & LOW_6BITS) | BIT8);  // next 6 bits
+            destData[destIndex++] = static_cast<char>((codePoint & LOW_6BITS) | BIT8);         // trailing 6 bits
+        }
+        else // 3 bytes needed (16 bits used)
+        {
+            destData[destIndex++] = static_cast<char>((src >> 12) | 0xE0);              // leading 4 bits
+            destData[destIndex++] = static_cast<char>(((src >> 6) & LOW_6BITS) | BIT8); // middle 6 bits
+            destData[destIndex++] = static_cast<char>((src & LOW_6BITS) | BIT8);        // trailing 6 bits
+        }
+    }
+
+    return dest;
+}
+
+utf16string __cdecl conversions::usascii_to_utf16(const std::string& s)
+{
+    // Ascii is a subset of UTF-8 so just convert to UTF-16
+    return utf8_to_utf16(s);
+}
+
+utf16string __cdecl conversions::latin1_to_utf16(const std::string& s)
+{
+    // Latin1 is the first 256 code points in Unicode.
+    // In UTF-16 encoding each of these is represented as exactly the numeric code point.
+    utf16string dest;
+    // Prefer resize combined with for-loop over constructor dest(s.begin(), s.end())
+    // for faster assignment.
+    dest.resize(s.size());
+    for (size_t i = 0; i < s.size(); ++i)
+    {
+        dest[i] = utf16char(static_cast<unsigned char>(s[i]));
+    }
+    return dest;
+}
+
+utf8string __cdecl conversions::latin1_to_utf8(const std::string& s) { return utf16_to_utf8(latin1_to_utf16(s)); }
+
+#ifndef _UTF16_STRINGS
+utility::string_t __cdecl conversions::to_string_t(utf16string&& s) { return utf16_to_utf8(std::move(s)); }
+#endif
+
+#ifdef _UTF16_STRINGS
+utility::string_t __cdecl conversions::to_string_t(std::string&& s) { return utf8_to_utf16(std::move(s)); }
+#endif
+
+#ifndef _UTF16_STRINGS
+utility::string_t __cdecl conversions::to_string_t(const utf16string& s) { return utf16_to_utf8(s); }
+#endif
+
+#ifdef _UTF16_STRINGS
+utility::string_t __cdecl conversions::to_string_t(const std::string& s) { return utf8_to_utf16(s); }
+#endif
+
+std::string __cdecl conversions::to_utf8string(const utf16string& value) { return utf16_to_utf8(value); }
+
+utf16string __cdecl conversions::to_utf16string(const std::string& value) { return utf8_to_utf16(value); }
+
+static const int64_t NtToUnixOffsetSeconds = 11644473600; // diff between windows and unix epochs (seconds)
+
+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;
+static const int SecondsInDay = SecondsInHour * 24;
+
+static const int DaysInYear = 365;
+static const int DaysIn4Years = DaysInYear * 4 + 1;
+static const int DaysIn100Years = DaysIn4Years * 25 - 1;
+static const int DaysIn400Years = DaysIn100Years * 4 + 1;
+
+static const int SecondsInYear = SecondsInDay * DaysInYear;
+static const int SecondsIn4Years = SecondsInDay * DaysIn4Years;
+static const int64_t SecondsIn100Years = static_cast<int64_t>(SecondsInDay) * DaysIn100Years;
+static const int64_t SecondsIn400Years = static_cast<int64_t>(SecondsInDay) * DaysIn400Years;
+
+static int count_leap_years_1601(int yearsSince1601)
+{
+    int year400 = yearsSince1601 / 400;
+    yearsSince1601 -= year400 * 400;
+    int result = year400 * 97;
+
+    int year100 = yearsSince1601 / 100;
+    yearsSince1601 -= year100 * 100;
+    result += year100 * 24;
+
+    result += yearsSince1601 / 4;
+
+    return result;
+}
+
+// The following table assumes no leap year; leap year is added separately
+static const unsigned short cumulative_days_to_month[12] = {
+    0,   // Jan
+    31,  // Feb
+    59,  // Mar
+    90,  // Apr
+    120, // May
+    151, // Jun
+    181, // Jul
+    212, // Aug
+    243, // Sep
+    273, // Oct
+    304, // Nov
+    334  // Dec
+};
+
+static const unsigned short cumulative_days_to_month_leap[12] = {
+    0,   // Jan
+    31,  // Feb
+    60,  // Mar
+    91,  // Apr
+    121, // May
+    152, // Jun
+    182, // Jul
+    213, // Aug
+    244, // Sep
+    274, // Oct
+    305, // Nov
+    335  // Dec
+};
+
+datetime __cdecl datetime::utc_now()
+{
+#ifdef _WIN32
+    ULARGE_INTEGER largeInt;
+    FILETIME fileTime;
+    GetSystemTimeAsFileTime(&fileTime);
+
+    largeInt.LowPart = fileTime.dwLowDateTime;
+    largeInt.HighPart = fileTime.dwHighDateTime;
+
+    return datetime(largeInt.QuadPart);
+#else // LINUX
+    struct timeval time;
+    gettimeofday(&time, nullptr);
+    int64_t result = NtToUnixOffsetSeconds + time.tv_sec;
+    result *= _secondTicks;      // convert to 10e-7
+    result += time.tv_usec * 10; // convert and add microseconds, 10e-6 to 10e-7
+    return datetime(static_cast<interval_type>(result));
+#endif
+}
+
+static const char dayNames[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
+static const char monthNames[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
+
+struct compute_year_result
+{
+    int year;
+    int secondsLeftThisYear;
+};
+
+static compute_year_result compute_year_1601(int64_t secondsSince1601)
+{
+    int year400 = static_cast<int>(secondsSince1601 / SecondsIn400Years);
+    secondsSince1601 -= year400 * SecondsIn400Years;
+
+    int year100 = static_cast<int>(secondsSince1601 / SecondsIn100Years);
+    secondsSince1601 -= year100 * SecondsIn100Years;
+
+    int year4 = static_cast<int>(secondsSince1601 / SecondsIn4Years);
+    int secondsInt = static_cast<int>(secondsSince1601 - year4 * SecondsIn4Years);
+
+    int year1 = secondsInt / SecondsInYear;
+    if (year1 == 4)
+    {
+        // this is the last day in a leap year
+        year1 = 3;
+    }
+
+    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 <windows.h>
+// #include <stdio.h>
+
+// 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<FILETIME*>(&ft))) {
+//         printf("0x%016llX\n", ft);
+//     } else {
+//         puts("failed!");
+//     }
+// }
+
+utility::string_t datetime::to_string(date_format format) const
+{
+    const int64_t interval = static_cast<int64_t>(m_interval);
+    if (interval > INT64_C(0x24C85A5ED1C018F0))
+    {
+        throw std::out_of_range("The requested year exceeds the year 9999.");
+    }
+
+    const int64_t secondsSince1601 = interval / _secondTicks; // convert to seconds
+    const int fracSec = static_cast<int>(interval % _secondTicks);
+
+    const auto yearData = compute_year_1601(secondsSince1601);
+    const int year = yearData.year;
+    const int yearDay = yearData.secondsLeftThisYear / SecondsInDay;
+    int leftover = yearData.secondsLeftThisYear % SecondsInDay;
+    const int hour = leftover / SecondsInHour;
+    leftover = leftover % SecondsInHour;
+    const int minute = leftover / SecondsInMinute;
+    leftover = leftover % SecondsInMinute;
+
+    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)
+    {
+        ++month;
+    }
+
+    const auto monthDay = yearDay - monthTable[month] + 1;
+    const auto weekday = static_cast<int>((secondsSince1601 / SecondsInDay + 1) % 7);
+
+    char outBuffer[38]; // Thu, 01 Jan 1970 00:00:00 GMT\0
+                        // 1970-01-01T00:00:00.1234567Z\0
+    char* outCursor = outBuffer;
+    switch (format)
+    {
+        case RFC_1123:
+#ifdef _MSC_VER
+            sprintf_s(outCursor,
+                      26,
+                      "%s, %02d %s %04d %02d:%02d:%02d",
+                      dayNames + 4 * weekday,
+                      monthDay,
+                      monthNames + 4 * month,
+                      year + 1601,
+                      hour,
+                      minute,
+                      leftover);
+#else  // ^^^ _MSC_VER // !_MSC_VER vvv
+            sprintf(outCursor,
+                    "%s, %02d %s %04d %02d:%02d:%02d",
+                    dayNames + 4 * weekday,
+                    monthDay,
+                    monthNames + 4 * month,
+                    year + 1601,
+                    hour,
+                    minute,
+                    leftover);
+#endif // _MSC_VER
+            outCursor += 25;
+            memcpy(outCursor, " GMT", 4);
+            outCursor += 4;
+            return utility::string_t(outBuffer, outCursor);
+        case ISO_8601:
+#ifdef _MSC_VER
+            sprintf_s(outCursor,
+                      20,
+                      "%04d-%02d-%02dT%02d:%02d:%02d",
+                      year + 1601,
+                      month + 1,
+                      monthDay,
+                      hour,
+                      minute,
+                      leftover);
+#else  // ^^^ _MSC_VER // !_MSC_VER vvv
+            sprintf(
+                outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1601, month + 1, monthDay, hour, minute, leftover);
+#endif // _MSC_VER
+            outCursor += 19;
+            if (fracSec != 0)
+            {
+                // Append fractional second, which is a 7-digit value with no trailing zeros
+                // This way, '1200' becomes '00012'
+#ifdef _MSC_VER
+                size_t appended = sprintf_s(outCursor, 9, ".%07d", fracSec);
+#else  // ^^^ _MSC_VER // !_MSC_VER vvv
+                size_t appended = sprintf(outCursor, ".%07d", fracSec);
+#endif // _MSC_VER
+                while (outCursor[appended - 1] == '0')
+                {
+                    --appended; // trim trailing zeros
+                }
+
+                outCursor += appended;
+            }
+
+            *outCursor = 'Z';
+            ++outCursor;
+            return utility::string_t(outBuffer, outCursor);
+        default: throw std::invalid_argument("Unrecognized date format.");
+    }
+}
+
+template<class CharT>
+static bool string_starts_with(const CharT* haystack, const char* needle)
+{
+    while (*needle)
+    {
+        if (*haystack != static_cast<CharT>(*needle))
+        {
+            return false;
+        }
+
+        ++haystack;
+        ++needle;
+    }
+
+    return true;
+}
+
+#define ascii_isdigit(c) ((unsigned char)((unsigned char)(c) - '0') <= 9)
+#define ascii_isdigit6(c) ((unsigned char)((unsigned char)(c) - '0') <= 6)
+#define ascii_isdigit5(c) ((unsigned char)((unsigned char)(c) - '0') <= 5)
+#define ascii_isdigit3(c) ((unsigned char)((unsigned char)(c) - '0') <= 3)
+#define ascii_isdigit2(c) ((unsigned char)((unsigned char)(c) - '0') <= 2)
+#define ascii_isdigit1(c) ((unsigned char)((unsigned char)(c) - '0') <= 1)
+
+static const unsigned char max_days_in_month[12] = {
+    31, // Jan
+    00, // Feb, special handling for leap years
+    31, // Mar
+    30, // Apr
+    31, // May
+    30, // Jun
+    31, // Jul
+    31, // Aug
+    30, // Sep
+    31, // Oct
+    30, // Nov
+    31  // Dec
+};
+
+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_1601(year);
+    }
+    else
+    {
+        maxDaysThisMonth = max_days_in_month[month];
+    }
+
+    return day >= 1 && day <= maxDaysThisMonth;
+}
+
+static int get_year_day_1601(int month, int monthDay, int year)
+{
+    return cumulative_days_to_month[month] + monthDay + (year_is_leap_year_1601(year) && month > 1) - 1;
+}
+
+template<class CharT>
+static int atoi2(const CharT* str)
+{
+    return (static_cast<unsigned char>(str[0]) - '0') * 10 + (static_cast<unsigned char>(str[1]) - '0');
+}
+
+static int64_t timezone_adjust(int64_t result, unsigned char chSign, int adjustHours, int adjustMinutes)
+{
+    if (adjustHours > 23)
+    {
+        return -1;
+    }
+
+    // adjustMinutes > 59 is impossible due to digit 5 check
+    const int tzAdjust = adjustMinutes * 60 + adjustHours * 60 * 60;
+    if (chSign == '-')
+    {
+        if (INT64_MAX - result < tzAdjust)
+        {
+            return -1;
+        }
+
+        result += tzAdjust;
+    }
+    else
+    {
+        if (tzAdjust > result)
+        {
+            return -1;
+        }
+
+        result -= tzAdjust;
+    }
+
+    return result;
+}
+
+/*
+https://tools.ietf.org/html/rfc822
+https://tools.ietf.org/html/rfc1123
+
+date-time   =  [ day "," ] date time        ; dd mm yy
+                                            ;  hh:mm:ss zzz
+
+day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
+            /  "Fri"  / "Sat" /  "Sun"
+
+date        =  1*2DIGIT month 2DIGIT        ; day month year
+                                            ;  e.g. 20 Jun 82
+RFC1123 changes this to:
+date        =  1*2DIGIT month 2*4DIGIT        ; day month year
+                                              ;  e.g. 20 Jun 1982
+This implementation only accepts 4 digit years.
+
+month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
+            /  "May"  /  "Jun" /  "Jul"  /  "Aug"
+            /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
+
+time        =  hour zone                    ; ANSI and Military
+
+hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
+                                            ; 00:00:00 - 23:59:59
+
+zone        =  "UT"  / "GMT"                ; Universal Time
+                                            ; North American : UT
+            /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
+            /  "CST" / "CDT"                ;  Central:  - 6/ - 5
+            /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
+            /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
+
+// military time deleted by RFC 1123
+
+            / ( ("+" / "-") 4DIGIT )        ; Local differential
+                                            ;  hours+min. (HHMM)
+*/
+
+datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format)
+{
+    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)
+    {
+        int parsedWeekday = 0;
+        for (; parsedWeekday < 7; ++parsedWeekday)
+        {
+            if (string_starts_with(str, dayNames + parsedWeekday * 4) && str[3] == _XPLATSTR(',') &&
+                str[4] == _XPLATSTR(' '))
+            {
+                str += 5; // parsed day of week
+                break;
+            }
+        }
+
+        int monthDay;
+        if (ascii_isdigit3(str[0]) && ascii_isdigit(str[1]) && str[2] == _XPLATSTR(' '))
+        {
+            monthDay = atoi2(str); // validity checked later
+            str += 3;              // parsed day
+        }
+        else if (ascii_isdigit(str[0]) && str[1] == _XPLATSTR(' '))
+        {
+            monthDay = str[0] - _XPLATSTR('0');
+            str += 2; // parsed day
+        }
+        else
+        {
+            return result;
+        }
+
+        if (monthDay == 0)
+        {
+            return result;
+        }
+
+        int month = 0;
+        for (;;)
+        {
+            if (string_starts_with(str, monthNames + month * 4))
+            {
+                break;
+            }
+
+            ++month;
+            if (month == 12)
+            {
+                return result;
+            }
+        }
+
+        if (str[3] != _XPLATSTR(' '))
+        {
+            return result;
+        }
+
+        str += 4; // parsed month
+
+        if (!ascii_isdigit(str[0]) || !ascii_isdigit(str[1]) || !ascii_isdigit(str[2]) || !ascii_isdigit(str[3]) ||
+            str[4] != ' ')
+        {
+            return result;
+        }
+
+        int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 +
+                   (str[3] - _XPLATSTR('0'));
+        if (year < 1601)
+        {
+            return result;
+        }
+
+        year -= 1601;
+
+        // days in month validity check
+        if (!validate_day_month_1601(monthDay, month, year))
+        {
+            return result;
+        }
+
+        str += 5; // parsed 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]))
+        {
+            return result;
+        }
+
+        const int hour = atoi2(str);
+        if (hour > 23)
+        {
+            return result;
+        }
+
+        str += 3; // parsed hour
+        const int minute = atoi2(str);
+        str += 2; // parsed mins
+
+        int sec;
+        if (str[0] == ':')
+        {
+            if (!ascii_isdigit6(str[1]) || !ascii_isdigit(str[2]) || str[3] != _XPLATSTR(' '))
+            {
+                return result;
+            }
+
+            sec = atoi2(str + 1);
+            str += 4; // parsed seconds
+        }
+        else if (str[0] == _XPLATSTR(' '))
+        {
+            sec = 0;
+            str += 1; // parsed seconds
+        }
+        else
+        {
+            return result;
+        }
+
+        if (sec > 60)
+        { // 60 to allow leap seconds
+            return result;
+        }
+
+        int daysSince1601 = year * DaysInYear + count_leap_years_1601(year) + yearDay;
+
+        if (parsedWeekday != 7)
+        {
+            const int actualWeekday = (daysSince1601 + 1) % 7;
+
+            if (parsedWeekday != actualWeekday)
+            {
+                return result;
+            }
+        }
+
+        secondsSince1601 =
+            static_cast<int64_t>(daysSince1601) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;
+
+        if (!string_starts_with(str, "GMT") && !string_starts_with(str, "UT"))
+        {
+            // some timezone adjustment necessary
+            auto tzCh = _XPLATSTR('-');
+            int tzHours;
+            int tzMinutes = 0;
+            if (string_starts_with(str, "EDT"))
+            {
+                tzHours = 4;
+            }
+            else if (string_starts_with(str, "EST") || string_starts_with(str, "CDT"))
+            {
+                tzHours = 5;
+            }
+            else if (string_starts_with(str, "CST") || string_starts_with(str, "MDT"))
+            {
+                tzHours = 6;
+            }
+            else if (string_starts_with(str, "MST") || string_starts_with(str, "PDT"))
+            {
+                tzHours = 7;
+            }
+            else if (string_starts_with(str, "PST"))
+            {
+                tzHours = 8;
+            }
+            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];
+                tzHours = atoi2(str + 1);
+                tzMinutes = atoi2(str + 3);
+            }
+            else
+            {
+                return result;
+            }
+
+            secondsSince1601 = timezone_adjust(secondsSince1601, static_cast<unsigned char>(tzCh), tzHours, tzMinutes);
+            if (secondsSince1601 < 0)
+            {
+                return result;
+            }
+        }
+    }
+    else if (format == ISO_8601)
+    {
+        // parse year
+        if (!ascii_isdigit(str[0]) || !ascii_isdigit(str[1]) || !ascii_isdigit(str[2]) || !ascii_isdigit(str[3]))
+        {
+            return result;
+        }
+
+        int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 +
+                   (str[3] - _XPLATSTR('0'));
+        if (year < 1601)
+        {
+            return result;
+        }
+
+        year -= 1601;
+
+        str += 4;
+        if (*str == _XPLATSTR('-'))
+        {
+            ++str;
+        }
+
+        // parse month
+        if (!ascii_isdigit1(str[0]) || !ascii_isdigit(str[1]))
+        {
+            return result;
+        }
+
+        int month = atoi2(str);
+        if (month < 1 || month > 12)
+        {
+            return result;
+        }
+
+        month -= 1;
+        str += 2;
+
+        if (*str == _XPLATSTR('-'))
+        {
+            ++str;
+        }
+
+        // parse day
+        if (!ascii_isdigit3(str[0]) || !ascii_isdigit(str[1]))
+        {
+            return result;
+        }
+
+        int monthDay = atoi2(str);
+        if (!validate_day_month_1601(monthDay, month, year))
+        {
+            return result;
+        }
+
+        const int yearDay = get_year_day_1601(month, monthDay, year);
+
+        str += 2;
+        int daysSince1601 = year * DaysInYear + count_leap_years_1601(year) + yearDay;
+
+        if (str[0] != _XPLATSTR('T') && str[0] != _XPLATSTR('t'))
+        {
+            // No time
+            secondsSince1601 = static_cast<int64_t>(daysSince1601) * SecondsInDay;
+
+            result.m_interval = static_cast<interval_type>(secondsSince1601 * _secondTicks + fracSec);
+            return result;
+        }
+
+        ++str; // skip 'T'
+
+        // parse hour
+        if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1]))
+        {
+            return result;
+        }
+
+        const int hour = atoi2(str);
+        str += 2;
+        if (hour > 23)
+        {
+            return result;
+        }
+
+        if (*str == _XPLATSTR(':'))
+        {
+            ++str;
+        }
+
+        // parse minute
+        if (!ascii_isdigit5(str[0]) || !ascii_isdigit(str[1]))
+        {
+            return result;
+        }
+
+        const int minute = atoi2(str);
+        // minute > 59 is impossible because we checked that the first digit is <= 5 in the basic format
+        // check above
+
+        str += 2;
+
+        if (*str == _XPLATSTR(':'))
+        {
+            ++str;
+        }
+
+        // parse seconds
+        if (!ascii_isdigit6(str[0]) || !ascii_isdigit(str[1]))
+        {
+            return result;
+        }
+
+        const int sec = atoi2(str);
+        // We allow 60 to account for leap seconds
+        if (sec > 60)
+        {
+            return result;
+        }
+
+        str += 2;
+        if (str[0] == _XPLATSTR('.') && ascii_isdigit(str[1]))
+        {
+            ++str;
+            int digits = 7;
+            for (;;)
+            {
+                fracSec *= 10;
+                fracSec += *str - _XPLATSTR('0');
+                --digits;
+                ++str;
+                if (digits == 0)
+                {
+                    while (ascii_isdigit(*str))
+                    {
+                        // consume remaining fractional second digits we can't use
+                        ++str;
+                    }
+
+                    break;
+                }
+
+                if (!ascii_isdigit(*str))
+                {
+                    // no more digits in the input, do the remaining multiplies we need
+                    for (; digits != 0; --digits)
+                    {
+                        fracSec *= 10;
+                    }
+
+                    break;
+                }
+            }
+        }
+
+        secondsSince1601 =
+            static_cast<int64_t>(daysSince1601) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;
+
+        if (str[0] == _XPLATSTR('Z') || str[0] == _XPLATSTR('z'))
+        {
+            // no adjustment needed for zulu time
+        }
+        else if (str[0] == _XPLATSTR('+') || str[0] == _XPLATSTR('-'))
+        {
+            const unsigned char offsetDirection = static_cast<unsigned char>(str[0]);
+            if (!ascii_isdigit2(str[1]) || !ascii_isdigit(str[2]) || str[3] != _XPLATSTR(':') ||
+                !ascii_isdigit5(str[4]) || !ascii_isdigit(str[5]))
+            {
+                return result;
+            }
+
+            secondsSince1601 = timezone_adjust(secondsSince1601, offsetDirection, atoi2(str + 1), atoi2(str + 4));
+            if (secondsSince1601 < 0)
+            {
+                return result;
+            }
+        }
+        else
+        {
+            // the timezone is malformed, but cpprestsdk currently accepts this as no timezone
+        }
+    }
+    else
+    {
+        throw std::invalid_argument("unrecognized date format");
+    }
+
+    result.m_interval = static_cast<interval_type>(secondsSince1601 * _secondTicks + fracSec);
+    return result;
+}
+
+/// <summary>
+/// Converts a timespan/interval in seconds to xml duration string as specified by
+/// http://www.w3.org/TR/xmlschema-2/#duration
+/// </summary>
+utility::string_t __cdecl timespan::seconds_to_xml_duration(utility::seconds durationSecs)
+{
+    auto numSecs = durationSecs.count();
+
+    // Find the number of minutes
+    auto numMins = numSecs / 60;
+    if (numMins > 0)
+    {
+        numSecs = numSecs % 60;
+    }
+
+    // Hours
+    auto numHours = numMins / 60;
+    if (numHours > 0)
+    {
+        numMins = numMins % 60;
+    }
+
+    // Days
+    auto numDays = numHours / 24;
+    if (numDays > 0)
+    {
+        numHours = numHours % 24;
+    }
+
+    // The format is:
+    // PdaysDThoursHminutesMsecondsS
+    utility::string_t result;
+    // (approximate mins/hours/secs as 2 digits each + 1 prefix character) + 1 for P prefix + 1 for T
+    size_t baseReserveSize = ((numHours > 0) + (numMins > 0) + (numSecs > 0)) * 3 + 1;
+    if (numDays > 0)
+    {
+        utility::string_t daysStr = utility::conversions::details::to_string_t(numDays);
+        result.reserve(baseReserveSize + daysStr.size() + 1);
+        result += _XPLATSTR('P');
+        result += daysStr;
+        result += _XPLATSTR('D');
+    }
+    else
+    {
+        result.reserve(baseReserveSize);
+        result += _XPLATSTR('P');
+    }
+
+    result += _XPLATSTR('T');
+
+    if (numHours > 0)
+    {
+        result += utility::conversions::details::to_string_t(numHours);
+        result += _XPLATSTR('H');
+    }
+
+    if (numMins > 0)
+    {
+        result += utility::conversions::details::to_string_t(numMins);
+        result += _XPLATSTR('M');
+    }
+
+    if (numSecs > 0)
+    {
+        result += utility::conversions::details::to_string_t(numSecs);
+        result += _XPLATSTR('S');
+    }
+
+    return result;
+}
+
+utility::seconds __cdecl timespan::xml_duration_to_seconds(const utility::string_t& timespanString)
+{
+    // The format is:
+    // PnDTnHnMnS
+    // if n == 0 then the field could be omitted
+    // The final S could be omitted
+
+    int64_t numSecs = 0;
+    auto cursor = timespanString.c_str();
+    auto c = *cursor++; // skip 'P'
+    while (c)
+    {
+        int val = 0;
+        c = *cursor++;
+
+        while (ascii_isdigit(c))
+        {
+            val = val * 10 + (c - _XPLATSTR('0'));
+            c = *cursor++;
+
+            if (c == _XPLATSTR('.'))
+            {
+                // decimal point is not handled
+                do
+                {
+                    c = *cursor++;
+                } while (ascii_isdigit(c));
+            }
+        }
+
+        if (c == _XPLATSTR('D')) numSecs += val * 24 * 3600; // days
+        if (c == _XPLATSTR('H')) numSecs += val * 3600;      // Hours
+        if (c == _XPLATSTR('M')) numSecs += val * 60;        // Minutes
+        if (c == _XPLATSTR('S') || c == _XPLATSTR('\0'))
+        {
+            numSecs += val; // seconds
+            break;
+        }
+    }
+
+    return utility::seconds(numSecs);
+}
+
+static const char c_allowed_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+static const int chars_count = static_cast<int>(sizeof(c_allowed_chars) - 1);
+
+utility::string_t nonce_generator::generate()
+{
+    std::uniform_int_distribution<> distr(0, chars_count - 1);
+    utility::string_t result;
+    result.reserve(length());
+    std::generate_n(std::back_inserter(result), length(), [&] {
+        return static_cast<utility::char_t>(c_allowed_chars[distr(m_random)]);
+    });
+    return result;
+}
+
+} // namespace utility
diff --git a/Release/src/utilities/base64.cpp b/Release/src/utilities/base64.cpp
new file mode 100644 (file)
index 0000000..1cb235c
--- /dev/null
@@ -0,0 +1,260 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+
+std::vector<unsigned char> _from_base64(const utility::string_t& str);
+utility::string_t _to_base64(const unsigned char* ptr, size_t size);
+
+std::vector<unsigned char> __cdecl conversions::from_base64(const utility::string_t& str) { return _from_base64(str); }
+
+utility::string_t __cdecl conversions::to_base64(const std::vector<unsigned char>& input)
+{
+    if (input.size() == 0)
+    {
+        // return empty string
+        return utility::string_t();
+    }
+
+    return _to_base64(&input[0], input.size());
+}
+
+utility::string_t __cdecl conversions::to_base64(uint64_t input)
+{
+    return _to_base64(reinterpret_cast<const unsigned char*>(&input), sizeof(input));
+}
+
+static const char* _base64_enctbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+const std::array<unsigned char, 128> _base64_dectbl = {
+    {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62,
+     255, 255, 255, 63,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  255, 255, 255, 254, 255, 255, 255, 0,
+     1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,
+     23,  24,  25,  255, 255, 255, 255, 255, 255, 26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
+     39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  255, 255, 255, 255, 255}};
+
+struct _triple_byte
+{
+    unsigned char _1_1 : 2;
+    unsigned char _0 : 6;
+    unsigned char _2_1 : 4;
+    unsigned char _1_2 : 4;
+    unsigned char _3 : 6;
+    unsigned char _2_2 : 2;
+};
+
+struct _double_byte
+{
+    unsigned char _1_1 : 2;
+    unsigned char _0 : 6;
+    unsigned char _2_1 : 4;
+    unsigned char _1_2 : 4;
+};
+
+struct _single_byte
+{
+    unsigned char _1_1 : 2;
+    unsigned char _0 : 6;
+};
+
+//
+// A note on the implementation of BASE64 encoding and decoding:
+//
+// This is a fairly basic and naive implementation; there is probably a lot of room for
+// performance improvement, as well as for adding options such as support for URI-safe base64,
+// ignoring CRLF, relaxed validation rules, etc. The decoder is currently pretty strict.
+//
+
+#ifdef __GNUC__
+// gcc is concerned about the bitfield uses in the code, something we simply need to ignore.
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+std::vector<unsigned char> _from_base64(const utility::string_t& input)
+{
+    std::vector<unsigned char> result;
+
+    if (input.empty()) return result;
+
+    size_t padding = 0;
+
+    // Validation
+    {
+        auto size = input.size();
+
+        if ((size % 4) != 0)
+        {
+            throw std::runtime_error("length of base64 string is not an even multiple of 4");
+        }
+
+        for (auto iter = input.begin(); iter != input.end(); ++iter, --size)
+        {
+            const size_t ch_sz = static_cast<size_t>(*iter);
+            if (ch_sz >= _base64_dectbl.size() || _base64_dectbl[ch_sz] == 255)
+            {
+                throw std::runtime_error("invalid character found in base64 string");
+            }
+            if (_base64_dectbl[ch_sz] == 254)
+            {
+                padding++;
+                // padding only at the end
+                if (size > 2)
+                {
+                    throw std::runtime_error("invalid padding character found in base64 string");
+                }
+                if (size == 2)
+                {
+                    const size_t ch2_sz = static_cast<size_t>(*(iter + 1));
+                    if (ch2_sz >= _base64_dectbl.size() || _base64_dectbl[ch2_sz] != 254)
+                    {
+                        throw std::runtime_error("invalid padding character found in base64 string");
+                    }
+                }
+            }
+        }
+    }
+
+    auto size = input.size();
+    const char_t* ptr = &input[0];
+
+    auto outsz = (size / 4) * 3;
+    outsz -= padding;
+
+    result.resize(outsz);
+
+    size_t idx = 0;
+    for (; size > 4; ++idx)
+    {
+        unsigned char target[3];
+        memset(target, 0, sizeof(target));
+        _triple_byte* record = reinterpret_cast<_triple_byte*>(target);
+
+        unsigned char val0 = _base64_dectbl[ptr[0]];
+        unsigned char val1 = _base64_dectbl[ptr[1]];
+        unsigned char val2 = _base64_dectbl[ptr[2]];
+        unsigned char val3 = _base64_dectbl[ptr[3]];
+
+        record->_0 = val0;
+        record->_1_1 = val1 >> 4;
+        result[idx] = target[0];
+
+        record->_1_2 = val1 & 0xF;
+        record->_2_1 = val2 >> 2;
+        result[++idx] = target[1];
+
+        record->_2_2 = val2 & 0x3;
+        record->_3 = val3 & 0x3F;
+        result[++idx] = target[2];
+
+        ptr += 4;
+        size -= 4;
+    }
+
+    // Handle the last four bytes separately, to avoid having the conditional statements
+    // in all the iterations (a performance issue).
+
+    {
+        unsigned char target[3];
+        memset(target, 0, sizeof(target));
+        _triple_byte* record = reinterpret_cast<_triple_byte*>(target);
+
+        unsigned char val0 = _base64_dectbl[ptr[0]];
+        unsigned char val1 = _base64_dectbl[ptr[1]];
+        unsigned char val2 = _base64_dectbl[ptr[2]];
+        unsigned char val3 = _base64_dectbl[ptr[3]];
+
+        record->_0 = val0;
+        record->_1_1 = val1 >> 4;
+        result[idx] = target[0];
+
+        record->_1_2 = val1 & 0xF;
+        if (val2 != 254)
+        {
+            record->_2_1 = val2 >> 2;
+            result[++idx] = target[1];
+        }
+        else
+        {
+            // There shouldn't be any information (ones) in the unused bits,
+            if (record->_1_2 != 0)
+            {
+                throw std::runtime_error("Invalid end of base64 string");
+            }
+            return result;
+        }
+
+        record->_2_2 = val2 & 0x3;
+        if (val3 != 254)
+        {
+            record->_3 = val3 & 0x3F;
+            result[++idx] = target[2];
+        }
+        else
+        {
+            // There shouldn't be any information (ones) in the unused bits.
+            if (record->_2_2 != 0)
+            {
+                throw std::runtime_error("Invalid end of base64 string");
+            }
+            return result;
+        }
+    }
+
+    return result;
+}
+
+utility::string_t _to_base64(const unsigned char* ptr, size_t size)
+{
+    utility::string_t result;
+
+    for (; size >= 3;)
+    {
+        const _triple_byte* record = reinterpret_cast<const _triple_byte*>(ptr);
+        unsigned char idx0 = record->_0;
+        unsigned char idx1 = (record->_1_1 << 4) | record->_1_2;
+        unsigned char idx2 = (record->_2_1 << 2) | record->_2_2;
+        unsigned char idx3 = record->_3;
+        result.push_back(char_t(_base64_enctbl[idx0]));
+        result.push_back(char_t(_base64_enctbl[idx1]));
+        result.push_back(char_t(_base64_enctbl[idx2]));
+        result.push_back(char_t(_base64_enctbl[idx3]));
+        size -= 3;
+        ptr += 3;
+    }
+    switch (size)
+    {
+        case 1:
+        {
+            const _single_byte* record = reinterpret_cast<const _single_byte*>(ptr);
+            unsigned char idx0 = record->_0;
+            unsigned char idx1 = (record->_1_1 << 4);
+            result.push_back(char_t(_base64_enctbl[idx0]));
+            result.push_back(char_t(_base64_enctbl[idx1]));
+            result.push_back('=');
+            result.push_back('=');
+            break;
+        }
+        case 2:
+        {
+            const _double_byte* record = reinterpret_cast<const _double_byte*>(ptr);
+            unsigned char idx0 = record->_0;
+            unsigned char idx1 = (record->_1_1 << 4) | record->_1_2;
+            unsigned char idx2 = (record->_2_1 << 2);
+            result.push_back(char_t(_base64_enctbl[idx0]));
+            result.push_back(char_t(_base64_enctbl[idx1]));
+            result.push_back(char_t(_base64_enctbl[idx2]));
+            result.push_back('=');
+            break;
+        }
+    }
+    return result;
+}
diff --git a/Release/src/utilities/web_utilities.cpp b/Release/src/utilities/web_utilities.cpp
new file mode 100644 (file)
index 0000000..ce00078
--- /dev/null
@@ -0,0 +1,159 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Credential and proxy utilities.
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <assert.h>
+
+#if defined(_WIN32) && !defined(__cplusplus_winrt)
+#include <Wincrypt.h>
+#endif
+
+#if defined(__cplusplus_winrt)
+#include <robuffer.h>
+#endif
+
+namespace web
+{
+namespace details
+{
+#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)
+{
+    Microsoft::WRL::ComPtr<IInspectable> bufferInspectable(reinterpret_cast<IInspectable*>(buffer));
+    Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
+    bufferInspectable.As(&bufferByteAccess);
+
+    // This shouldn't happen but if can't get access to the raw bytes for some reason
+    // then we can't zero out.
+    byte* rawBytes;
+    if (bufferByteAccess->Buffer(&rawBytes) == S_OK)
+    {
+        SecureZeroMemory(rawBytes, buffer->Length);
+    }
+}
+
+winrt_encryption::winrt_encryption(const std::wstring& data)
+{
+    auto provider = ref new Windows::Security::Cryptography::DataProtection::DataProtectionProvider(
+        ref new Platform::String(L"Local=user"));
+
+    // Create buffer containing plain text password.
+    Platform::ArrayReference<unsigned char> arrayref(
+        reinterpret_cast<unsigned char*>(const_cast<std::wstring::value_type*>(data.c_str())),
+        static_cast<unsigned int>(data.size()) * sizeof(std::wstring::value_type));
+    Windows::Storage::Streams::IBuffer ^ plaintext =
+        Windows::Security::Cryptography::CryptographicBuffer::CreateFromByteArray(arrayref);
+    m_buffer = pplx::create_task(provider->ProtectAsync(plaintext));
+    m_buffer.then(
+        [plaintext](pplx::task<Windows::Storage::Streams::IBuffer ^>) { winrt_secure_zero_buffer(plaintext); });
+}
+
+plaintext_string winrt_encryption::decrypt() const
+{
+    // To fully guarantee asynchrony would require significant impact on existing code. This code path
+    // is never run on a user's thread and is only done once when setting up a connection.
+    auto encrypted = m_buffer.get();
+    auto provider = ref new Windows::Security::Cryptography::DataProtection::DataProtectionProvider();
+    auto plaintext = pplx::create_task(provider->UnprotectAsync(encrypted)).get();
+
+    // Get access to raw bytes in plain text buffer.
+    Microsoft::WRL::ComPtr<IInspectable> bufferInspectable(reinterpret_cast<IInspectable*>(plaintext));
+    Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
+    bufferInspectable.As(&bufferByteAccess);
+    byte* rawPlaintext;
+    const auto& result = bufferByteAccess->Buffer(&rawPlaintext);
+    if (result != S_OK)
+    {
+        throw ::utility::details::create_system_error(result);
+    }
+
+    // Construct string and zero out memory from plain text buffer.
+    auto data = plaintext_string(
+        new std::wstring(reinterpret_cast<const std::wstring::value_type*>(rawPlaintext), plaintext->Length / 2));
+    SecureZeroMemory(rawPlaintext, plaintext->Length);
+    return std::move(data);
+}
+
+#else  // ^^^ __cplusplus_winrt ^^^ // vvv !__cplusplus_winrt vvv
+
+win32_encryption::win32_encryption(const std::wstring& data) : m_numCharacters(data.size())
+{
+    // Early return because CryptProtectMemory crashes with empty string
+    if (m_numCharacters == 0)
+    {
+        return;
+    }
+
+    if (data.size() > (std::numeric_limits<DWORD>::max)() / sizeof(wchar_t))
+    {
+        throw std::length_error("Encryption string too long");
+    }
+
+    const auto dataSizeDword = static_cast<DWORD>(data.size() * sizeof(wchar_t));
+
+    // Round up dataSizeDword to be a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE
+    static_assert(CRYPTPROTECTMEMORY_BLOCK_SIZE == 16, "Power of 2 assumptions in this bit masking violated");
+    const auto mask = static_cast<DWORD>(CRYPTPROTECTMEMORY_BLOCK_SIZE - 1u);
+    const auto dataNumBytes = (dataSizeDword & ~mask) + ((dataSizeDword & mask) != 0) * CRYPTPROTECTMEMORY_BLOCK_SIZE;
+    assert((dataNumBytes % CRYPTPROTECTMEMORY_BLOCK_SIZE) == 0);
+    assert(dataNumBytes >= dataSizeDword);
+    m_buffer.resize(dataNumBytes);
+    memcpy_s(m_buffer.data(), m_buffer.size(), data.c_str(), dataNumBytes);
+    if (!CryptProtectMemory(m_buffer.data(), dataNumBytes, CRYPTPROTECTMEMORY_SAME_PROCESS))
+    {
+        throw ::utility::details::create_system_error(GetLastError());
+    }
+}
+
+win32_encryption::~win32_encryption() { SecureZeroMemory(m_buffer.data(), m_buffer.size()); }
+
+plaintext_string win32_encryption::decrypt() const
+{
+    // Copy the buffer and decrypt to avoid having to re-encrypt.
+    auto result = plaintext_string(new std::wstring(reinterpret_cast<const std::wstring::value_type*>(m_buffer.data()),
+                                                    m_buffer.size() / sizeof(wchar_t)));
+    auto& data = *result;
+    if (!m_buffer.empty())
+    {
+        if (!CryptUnprotectMemory(&data[0], static_cast<DWORD>(m_buffer.size()), CRYPTPROTECTMEMORY_SAME_PROCESS))
+        {
+            throw ::utility::details::create_system_error(GetLastError());
+        }
+
+        assert(m_numCharacters <= m_buffer.size());
+        SecureZeroMemory(&data[m_numCharacters], data.size() - m_numCharacters);
+        data.erase(m_numCharacters);
+    }
+
+    return result;
+}
+#endif // __cplusplus_winrt
+#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#endif // _WIN32
+
+void zero_memory_deleter::operator()(::utility::string_t* data) const
+{
+    (void)data;
+#ifdef _WIN32
+    SecureZeroMemory(&(*data)[0], data->size() * sizeof(::utility::string_t::value_type));
+    delete data;
+#endif
+}
+} // namespace details
+
+} // namespace web
diff --git a/Release/src/websockets/client/ws_client.cpp b/Release/src/websockets/client/ws_client.cpp
new file mode 100644 (file)
index 0000000..48a4079
--- /dev/null
@@ -0,0 +1,100 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Portions common to both WinRT and Websocket++ implementations.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "cpprest/ws_client.h"
+
+#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
+
+namespace web
+{
+namespace websockets
+{
+namespace client
+{
+namespace details
+{
+websocket_client_task_impl::~websocket_client_task_impl() CPPREST_NOEXCEPT
+{
+    close_pending_tasks_with_error(websocket_exception("Websocket client is being destroyed"));
+}
+
+void websocket_client_task_impl::set_handler()
+{
+    m_callback_client->set_message_handler([=](const websocket_incoming_message& msg) {
+        pplx::task_completion_event<websocket_incoming_message>
+            tce; // This will be set if there are any tasks waiting to receive a message
+        {
+            std::lock_guard<std::mutex> lock(m_receive_queue_lock);
+            if (m_receive_task_queue.empty()) // Push message to the queue as no one is waiting to receive
+            {
+                m_receive_msg_queue.push(msg);
+                return;
+            }
+            else // There are tasks waiting to receive a message.
+            {
+                tce = m_receive_task_queue.front();
+                m_receive_task_queue.pop();
+            }
+        }
+        // Setting the tce outside the receive lock for better performance
+        tce.set(msg);
+    });
+
+    m_callback_client->set_close_handler(
+        [=](websocket_close_status, const utility::string_t& reason, const std::error_code& error_code) {
+            close_pending_tasks_with_error(websocket_exception(error_code, reason));
+        });
+}
+
+void websocket_client_task_impl::close_pending_tasks_with_error(const websocket_exception& exc)
+{
+    std::lock_guard<std::mutex> lock(m_receive_queue_lock);
+    m_client_closed = true;
+    while (!m_receive_task_queue.empty()) // There are tasks waiting to receive a message, signal them
+    {
+        auto tce = m_receive_task_queue.front();
+        m_receive_task_queue.pop();
+        tce.set_exception(std::make_exception_ptr(exc));
+    }
+}
+
+pplx::task<websocket_incoming_message> websocket_client_task_impl::receive()
+{
+    std::lock_guard<std::mutex> lock(m_receive_queue_lock);
+    if (m_client_closed == true)
+    {
+        return pplx::task_from_exception<websocket_incoming_message>(
+            std::make_exception_ptr(websocket_exception("Websocket connection has closed.")));
+    }
+
+    if (m_receive_msg_queue
+            .empty()) // Push task completion event to the tce queue, so that it gets signaled when we have a message.
+    {
+        pplx::task_completion_event<websocket_incoming_message> tce;
+        m_receive_task_queue.push(tce);
+        return pplx::create_task(tce);
+    }
+    else // Receive message queue is not empty, return a message from the queue.
+    {
+        auto msg = m_receive_msg_queue.front();
+        m_receive_msg_queue.pop();
+        return pplx::task_from_result<websocket_incoming_message>(msg);
+    }
+}
+
+} // namespace details
+} // namespace client
+} // namespace websockets
+} // namespace web
+
+#endif
diff --git a/Release/src/websockets/client/ws_client_impl.h b/Release/src/websockets/client/ws_client_impl.h
new file mode 100644 (file)
index 0000000..5c66c8b
--- /dev/null
@@ -0,0 +1,59 @@
+#pragma once
+
+#include "cpprest/ws_client.h"
+#include "cpprest/ws_msg.h"
+#include <mutex>
+#include <queue>
+
+namespace web
+{
+namespace websockets
+{
+namespace client
+{
+namespace details
+{
+struct outgoing_msg_queue
+{
+    enum class state
+    {
+        was_empty,
+        was_not_empty,
+    };
+
+    state push(websocket_outgoing_message& msg)
+    {
+        state ret = state::was_not_empty;
+        std::lock_guard<std::mutex> lock(m_lock);
+        if (m_queue.empty())
+        {
+            ret = state::was_empty;
+        }
+
+        m_queue.push(msg);
+        return ret;
+    }
+
+    bool pop_and_peek(websocket_outgoing_message& msg)
+    {
+        std::lock_guard<std::mutex> lock(m_lock);
+
+        m_queue.pop();
+
+        if (m_queue.empty())
+        {
+            return false;
+        }
+        msg = m_queue.front();
+        return true;
+    }
+
+private:
+    std::mutex m_lock;
+    std::queue<websocket_outgoing_message> m_queue;
+};
+
+} // namespace details
+} // namespace client
+} // namespace websockets
+} // namespace web
\ No newline at end of file
diff --git a/Release/src/websockets/client/ws_client_winrt.cpp b/Release/src/websockets/client/ws_client_winrt.cpp
new file mode 100644 (file)
index 0000000..6bb4351
--- /dev/null
@@ -0,0 +1,499 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Websocket library: Client-side APIs.
+ *
+ * This file contains the implementation for the Windows Runtime based on
+ *Windows::Networking::Sockets::MessageWebSocket.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include <concrt.h>
+
+#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
+
+#include "ws_client_impl.h"
+
+using namespace ::Windows::Foundation;
+using namespace ::Windows::Storage;
+using namespace ::Windows::Storage::Streams;
+using namespace ::Windows::Networking;
+using namespace ::Windows::Networking::Sockets;
+using namespace Concurrency::streams::details;
+
+namespace web
+{
+namespace websockets
+{
+namespace client
+{
+namespace details
+{
+// Helper function to build an error string from a Platform::Exception and a location.
+static std::string build_error_msg(Platform::Exception ^ exc, const std::string& location)
+{
+    std::string msg(location);
+    msg.append(": ");
+    msg.append(std::to_string(exc->HResult));
+    msg.append(": ");
+    msg.append(utility::conversions::utf16_to_utf8(exc->Message->Data()));
+    return msg;
+}
+
+// This class is required by the implementation in order to function:
+// The TypedEventHandler requires the message received and close handler to be a member of WinRT class.
+ref class ReceiveContext sealed
+{
+public:
+    ReceiveContext() {}
+
+    friend class winrt_callback_client;
+    friend class winrt_task_client;
+    void OnReceive(MessageWebSocket ^ sender, MessageWebSocketMessageReceivedEventArgs ^ args);
+    void OnClosed(IWebSocket ^ sender, WebSocketClosedEventArgs ^ args);
+
+private:
+    // Public members cannot have native types
+    ReceiveContext(
+        std::function<void(const websocket_incoming_message&)> receive_handler,
+        std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)> close_handler)
+        : m_receive_handler(std::move(receive_handler)), m_close_handler(std::move(close_handler))
+    {
+    }
+
+    // Handler to be executed when a message has been received by the client
+    std::function<void(const websocket_incoming_message&)> m_receive_handler;
+
+    // Handler to be executed when a close message has been received by the client
+    std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)> m_close_handler;
+};
+
+class winrt_callback_client : public websocket_client_callback_impl,
+                              public std::enable_shared_from_this<winrt_callback_client>
+{
+public:
+    winrt_callback_client(websocket_client_config config)
+        : websocket_client_callback_impl(std::move(config)), m_connected(false)
+    {
+        m_msg_websocket = ref new MessageWebSocket();
+
+        // Sets the HTTP request headers to the HTTP request message used in the WebSocket protocol handshake
+        const utility::string_t protocolHeader(_XPLATSTR("Sec-WebSocket-Protocol"));
+        const auto& headers = m_config.headers();
+        for (const auto& header : headers)
+        {
+            // Unfortunately the MessageWebSocket API throws a COMException if you try to set the
+            // 'Sec-WebSocket-Protocol' header here. It requires you to go through their API instead.
+            if (!utility::details::str_iequal(header.first, protocolHeader))
+            {
+                m_msg_websocket->SetRequestHeader(Platform::StringReference(header.first.c_str()),
+                                                  Platform::StringReference(header.second.c_str()));
+            }
+        }
+
+        // Add any specified subprotocols.
+        if (headers.has(protocolHeader))
+        {
+            const std::vector<utility::string_t> protocols = m_config.subprotocols();
+            for (const auto& value : protocols)
+            {
+                m_msg_websocket->Control->SupportedProtocols->Append(Platform::StringReference(value.c_str()));
+            }
+        }
+
+        if (m_config.credentials().is_set())
+        {
+            auto password = m_config.credentials()._internal_decrypt();
+            m_msg_websocket->Control->ServerCredential = ref new Windows::Security::Credentials::PasswordCredential(
+                "WebSocketClientCredentialResource",
+                Platform::StringReference(m_config.credentials().username().c_str()),
+                Platform::StringReference(password->c_str()));
+        }
+
+        m_context = ref new ReceiveContext(
+            [=](const websocket_incoming_message& msg) {
+                if (m_external_message_handler)
+                {
+                    m_external_message_handler(msg);
+                }
+            },
+            [=](websocket_close_status status, const utility::string_t& reason, const std::error_code& error_code) {
+                if (m_external_close_handler)
+                {
+                    m_external_close_handler(status, reason, error_code);
+                }
+
+                // Locally copy the task completion event since there is a PPL bug
+                // that the set method accesses internal state in the event and the websocket
+                // client could be destroyed.
+                auto local_close_tce = m_close_tce;
+                local_close_tce.set();
+            });
+    }
+
+    ~winrt_callback_client()
+    {
+        // Only call close if successfully connected.
+        if (m_connected)
+        {
+            // Users should have already called close and wait on the returned task
+            // before destroying the client. In case they didn't we call close and wait for
+            // it to complete. It is safe to call MessageWebSocket::Close multiple times and
+            // concurrently, it has safe guards in place to only execute once.
+            close().wait();
+        }
+    }
+
+    pplx::task<void> connect()
+    {
+        _ASSERTE(!m_connected);
+        const auto& proxy = m_config.proxy();
+        if (!proxy.is_default())
+        {
+            return pplx::task_from_exception<void>(websocket_exception("Only a default proxy server is supported."));
+        }
+
+        const auto& proxy_cred = proxy.credentials();
+        if (proxy_cred.is_set())
+        {
+            auto password = proxy_cred._internal_decrypt();
+            m_msg_websocket->Control->ProxyCredential = ref new Windows::Security::Credentials::PasswordCredential(
+                "WebSocketClientProxyCredentialResource",
+                Platform::StringReference(proxy_cred.username().c_str()),
+                Platform::StringReference(password->c_str()));
+        }
+
+        const auto uri = ref new Windows::Foundation::Uri(Platform::StringReference(m_uri.to_string().c_str()));
+
+        m_msg_websocket->MessageReceived +=
+            ref new TypedEventHandler<MessageWebSocket ^, MessageWebSocketMessageReceivedEventArgs ^>(
+                m_context, &ReceiveContext::OnReceive);
+        m_msg_websocket->Closed +=
+            ref new TypedEventHandler<IWebSocket ^, WebSocketClosedEventArgs ^>(m_context, &ReceiveContext::OnClosed);
+
+        std::weak_ptr<winrt_callback_client> thisWeakPtr = shared_from_this();
+        return pplx::create_task(m_msg_websocket->ConnectAsync(uri))
+            .then([thisWeakPtr](pplx::task<void> result) -> pplx::task<void> {
+                // result.get() should happen before anything else, to make sure there is no unobserved exception
+                // in the task chain.
+                try
+                {
+                    result.get();
+                }
+                catch (Platform::Exception ^ e)
+                {
+                    throw websocket_exception(e->HResult, build_error_msg(e, "ConnectAsync"));
+                }
+
+                if (auto pThis = thisWeakPtr.lock())
+                {
+                    try
+                    {
+                        pThis->m_messageWriter = ref new DataWriter(pThis->m_msg_websocket->OutputStream);
+                    }
+                    catch (Platform::Exception ^ e)
+                    {
+                        throw websocket_exception(e->HResult, build_error_msg(e, "ConnectAsync"));
+                    }
+                    pThis->m_connected = true;
+                }
+                else
+                {
+                    return pplx::task_from_exception<void>(websocket_exception("Websocket client is being destroyed"));
+                }
+
+                return pplx::task_from_result();
+            });
+    }
+
+    pplx::task<void> send(websocket_outgoing_message& msg)
+    {
+        if (m_messageWriter == nullptr)
+        {
+            return pplx::task_from_exception<void>(websocket_exception("Client not connected."));
+        }
+
+        switch (msg.m_msg_type)
+        {
+            case websocket_message_type::binary_message:
+                m_msg_websocket->Control->MessageType = SocketMessageType::Binary;
+                break;
+            case websocket_message_type::text_message:
+                m_msg_websocket->Control->MessageType = SocketMessageType::Utf8;
+                break;
+            default: return pplx::task_from_exception<void>(websocket_exception("Message Type not supported."));
+        }
+
+        const auto length = msg.m_length;
+        if (length == 0)
+        {
+            return pplx::task_from_exception<void>(websocket_exception("Cannot send empty message."));
+        }
+        if (length >= UINT_MAX && length != SIZE_MAX)
+        {
+            return pplx::task_from_exception<void>(
+                websocket_exception("Message size too large. Ensure message length is less than UINT_MAX."));
+        }
+
+        auto msg_pending = m_out_queue.push(msg);
+
+        // No sends in progress
+        if (msg_pending == outgoing_msg_queue::state::was_empty)
+        {
+            // Start sending the message
+            send_msg(msg);
+        }
+
+        return pplx::create_task(msg.body_sent());
+    }
+
+    void send_msg(websocket_outgoing_message& msg)
+    {
+        auto this_client = this->shared_from_this();
+        auto& is_buf = msg.m_body;
+        auto length = msg.m_length;
+
+        if (length == SIZE_MAX)
+        {
+            // This indicates we should determine the length automatically.
+            if (is_buf.has_size())
+            {
+                // The user's stream knows how large it is -- there's no need to buffer.
+                auto buf_sz = is_buf.size();
+                if (buf_sz >= SIZE_MAX)
+                {
+                    msg.signal_body_sent(
+                        std::make_exception_ptr(websocket_exception("Cannot send messages larger than SIZE_MAX.")));
+                    return;
+                }
+                length = static_cast<size_t>(buf_sz);
+                // We have determined the length and can proceed normally.
+            }
+            else
+            {
+                // The stream needs to be buffered.
+                auto is_buf_istream = is_buf.create_istream();
+                msg.m_body = concurrency::streams::container_buffer<std::vector<uint8_t>>();
+                is_buf_istream.read_to_end(msg.m_body).then([this_client, msg](pplx::task<size_t> t) mutable {
+                    try
+                    {
+                        msg.m_length = t.get();
+                        this_client->send_msg(msg);
+                    }
+                    catch (...)
+                    {
+                        msg.signal_body_sent(std::current_exception());
+                    }
+                });
+                // We have postponed the call to send_msg() until after the data is buffered.
+                return;
+            }
+        }
+
+        // First try to acquire the data (Get a pointer to the next already allocated contiguous block of data)
+        // If acquire succeeds, send the data over the socket connection, there is no copy of data from stream to
+        // temporary buffer. If acquire fails, copy the data to a temporary buffer managed by sp_allocated and send it
+        // over the socket connection.
+        std::shared_ptr<uint8_t> sp_allocated;
+        size_t acquired_size = 0;
+        uint8_t* ptr;
+        auto read_task = pplx::task_from_result();
+        bool acquired = is_buf.acquire(ptr, acquired_size);
+
+        if (!acquired ||
+            acquired_size < length) // Stream does not support acquire or failed to acquire specified number of bytes
+        {
+            // If acquire did not return the required number of bytes, do not rely on its return value.
+            if (acquired_size < length)
+            {
+                acquired = false;
+                is_buf.release(ptr, 0);
+            }
+
+            // Allocate buffer to hold the data to be read from the stream.
+            sp_allocated.reset(new uint8_t[length], [=](uint8_t* p) { delete[] p; });
+
+            read_task = is_buf.getn(sp_allocated.get(), length).then([length](size_t bytes_read) {
+                if (bytes_read != length)
+                {
+                    throw websocket_exception("Failed to read required length of data from the stream.");
+                }
+            });
+        }
+        else
+        {
+            // Acquire succeeded, assign the acquired pointer to sp_allocated. Use an empty custom destructor
+            // so that the data is not released when sp_allocated goes out of scope. The streambuf will manage its
+            // memory.
+            sp_allocated.reset(ptr, [](uint8_t*) {});
+        }
+
+        read_task
+            .then([this_client, acquired, sp_allocated, length]() {
+                this_client->m_messageWriter->WriteBytes(
+                    Platform::ArrayReference<unsigned char>(sp_allocated.get(), static_cast<unsigned int>(length)));
+
+                // Send the data as one complete message, in WinRT we do not have an option to send fragments.
+                return pplx::task<unsigned int>(this_client->m_messageWriter->StoreAsync());
+            })
+            .then([this_client, msg, is_buf, acquired, sp_allocated, length](
+                      pplx::task<unsigned int> previousTask) mutable {
+                std::exception_ptr eptr;
+                unsigned int bytes_written = 0;
+                try
+                {
+                    // Catch exceptions from previous tasks, if any and convert it to websocket exception.
+                    bytes_written = previousTask.get();
+                    if (bytes_written != length)
+                    {
+                        eptr = std::make_exception_ptr(websocket_exception("Failed to send all the bytes."));
+                    }
+                }
+                catch (Platform::Exception ^ e)
+                {
+                    // Convert to websocket_exception.
+                    eptr = std::make_exception_ptr(websocket_exception(e->HResult, build_error_msg(e, "send_msg")));
+                }
+                catch (const websocket_exception& e)
+                {
+                    // Catch to avoid slicing and losing the type if falling through to catch (...).
+                    eptr = std::make_exception_ptr(e);
+                }
+                catch (...)
+                {
+                    eptr = std::make_exception_ptr(std::current_exception());
+                }
+
+                if (acquired)
+                {
+                    is_buf.release(sp_allocated.get(), bytes_written);
+                }
+
+                // Set the send_task_completion_event after calling release.
+                if (eptr)
+                {
+                    msg.signal_body_sent(eptr);
+                }
+                else
+                {
+                    msg.signal_body_sent();
+                }
+
+                websocket_outgoing_message next_msg;
+                bool msg_pending = this_client->m_out_queue.pop_and_peek(next_msg);
+                if (msg_pending)
+                {
+                    this_client->send_msg(next_msg);
+                }
+            });
+    }
+
+    void set_message_handler(const std::function<void(const websocket_incoming_message&)>& handler)
+    {
+        m_external_message_handler = handler;
+    }
+
+    pplx::task<void> close()
+    {
+        // Send a close frame to the server
+        return close(websocket_close_status::normal, _XPLATSTR("Normal"));
+    }
+
+    pplx::task<void> close(websocket_close_status status, const utility::string_t& strreason = {})
+    {
+        // Send a close frame to the server
+        m_msg_websocket->Close(static_cast<unsigned short>(status), Platform::StringReference(strreason.c_str()));
+        // Wait for the close response frame from the server.
+        return pplx::create_task(m_close_tce);
+    }
+
+    void set_close_handler(
+        const std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)>& handler)
+    {
+        m_external_close_handler = handler;
+    }
+
+private:
+    // WinRT MessageWebSocket object
+    Windows::Networking::Sockets::MessageWebSocket ^ m_msg_websocket;
+    Windows::Storage::Streams::DataWriter ^ m_messageWriter;
+    // Context object that implements the WinRT handlers: receive handler and close handler
+    ReceiveContext ^ m_context;
+
+    pplx::task_completion_event<void> m_close_tce;
+
+    // Tracks whether or not the websocket client successfully connected to the server.
+    std::atomic<bool> m_connected;
+
+    // External callback for handling received and close event
+    std::function<void(websocket_incoming_message)> m_external_message_handler;
+    std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)>
+        m_external_close_handler;
+
+    // Queue to track pending sends
+    outgoing_msg_queue m_out_queue;
+};
+
+void ReceiveContext::OnReceive(MessageWebSocket ^ sender, MessageWebSocketMessageReceivedEventArgs ^ args)
+{
+    websocket_incoming_message incoming_msg;
+
+    switch (args->MessageType)
+    {
+        case SocketMessageType::Binary: incoming_msg.m_msg_type = websocket_message_type::binary_message; break;
+        case SocketMessageType::Utf8: incoming_msg.m_msg_type = websocket_message_type::text_message; break;
+    }
+
+    try
+    {
+        DataReader ^ reader = args->GetDataReader();
+        const auto len = reader->UnconsumedBufferLength;
+        if (len > 0)
+        {
+            std::string payload;
+            payload.resize(len);
+            reader->ReadBytes(Platform::ArrayReference<uint8_t>(reinterpret_cast<uint8*>(&payload[0]), len));
+            incoming_msg.m_body = concurrency::streams::container_buffer<std::string>(std::move(payload));
+        }
+        m_receive_handler(incoming_msg);
+    }
+    catch (Platform::Exception ^ e)
+    {
+        m_close_handler(websocket_close_status::abnormal_close,
+                        _XPLATSTR("Abnormal close"),
+                        utility::details::create_error_code(e->HResult));
+    }
+}
+
+void ReceiveContext::OnClosed(IWebSocket ^ sender, WebSocketClosedEventArgs ^ args)
+{
+    m_close_handler(
+        static_cast<websocket_close_status>(args->Code), args->Reason->Data(), utility::details::create_error_code(0));
+}
+
+websocket_client_task_impl::websocket_client_task_impl(websocket_client_config config)
+    : m_callback_client(std::make_shared<details::winrt_callback_client>(std::move(config))), m_client_closed(false)
+{
+    set_handler();
+}
+} // namespace details
+
+websocket_callback_client::websocket_callback_client()
+    : m_client(std::make_shared<details::winrt_callback_client>(websocket_client_config()))
+{
+}
+
+websocket_callback_client::websocket_callback_client(websocket_client_config config)
+    : m_client(std::make_shared<details::winrt_callback_client>(std::move(config)))
+{
+}
+
+} // namespace client
+} // namespace websockets
+} // namespace web
+#endif
diff --git a/Release/src/websockets/client/ws_client_wspp.cpp b/Release/src/websockets/client/ws_client_wspp.cpp
new file mode 100644 (file)
index 0000000..d7c31c4
--- /dev/null
@@ -0,0 +1,846 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Websocket library: Client-side APIs.
+ *
+ * This file contains a cross platform implementation based on WebSocket++.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <thread>
+
+#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
+
+#include "../../http/common/x509_cert_utilities.h"
+#include "pplx/threadpool.h"
+#include "ws_client_impl.h"
+
+// Force websocketpp to use C++ std::error_code instead of Boost.
+#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4100 4127 4512 4996 4701 4267)
+#define _WEBSOCKETPP_CPP11_STL_
+#define _WEBSOCKETPP_CONSTEXPR_TOKEN_
+#if _MSC_VER < 1900
+#define _WEBSOCKETPP_NOEXCEPT_TOKEN_
+#endif
+#elif defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+#pragma clang diagnostic ignored "-Wtautological-constant-compare"
+#pragma clang diagnostic ignored "-Wtautological-unsigned-enum-zero-compare"
+#pragma clang diagnostic ignored "-Wcast-qual"
+#elif defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wignored-qualifiers"
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+
+#include <websocketpp/client.hpp>
+#include <websocketpp/config/asio_client.hpp>
+#include <websocketpp/config/asio_no_tls_client.hpp>
+
+#if defined(_WIN32)
+#pragma warning(pop)
+#elif defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(disable : 4503)
+#endif
+
+// Workaround data-race on websocketpp's _htonll function, see
+// https://github.com/Microsoft/cpprestsdk/pull/1082
+auto avoidDataRaceOnHtonll = websocketpp::lib::net::_htonll(0);
+
+// This is a hack to avoid memory leak reports from the debug MSVC CRT for all
+// programs using the library: ASIO calls SSL_library_init() which calls
+// SSL_COMP_get_compression_methods(), which allocates some heap memory and the
+// only way to free it later is to call SSL_COMP_free_compression_methods(),
+// but this function is unaccessible from the application code as OpenSSL is
+// statically linked into the C++ REST SDK DLL. So, just to be nice, call it
+// here ourselves -- even if the real problem is in ASIO (up to v1.60.0).
+#if defined(_WIN32) && !defined(NDEBUG) && !defined(CPPREST_NO_SSL_LEAK_SUPPRESS)
+
+#include <openssl/ssl.h>
+static struct ASIO_SSL_memory_leak_suppress
+{
+    ~ASIO_SSL_memory_leak_suppress()
+    {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+        ::SSL_COMP_free_compression_methods();
+#endif
+    }
+} ASIO_SSL_memory_leak_suppressor;
+
+#endif /* _WIN32 && !NDEBUG */
+
+using websocketpp::lib::bind;
+using websocketpp::lib::placeholders::_1;
+using websocketpp::lib::placeholders::_2;
+
+namespace web
+{
+namespace websockets
+{
+namespace client
+{
+namespace details
+{
+// Utility function to build up error string based on error code and location.
+static std::string build_error_msg(const std::error_code& ec, const std::string& location)
+{
+    std::string result = location;
+    result += ": ";
+    result += std::to_string(ec.value());
+    result += ": ";
+    result += ec.message();
+    return result;
+}
+
+static utility::string_t g_subProtocolHeader(_XPLATSTR("Sec-WebSocket-Protocol"));
+
+class wspp_callback_client : public websocket_client_callback_impl,
+                             public std::enable_shared_from_this<wspp_callback_client>
+{
+private:
+    enum State
+    {
+        CREATED,
+        CONNECTING,
+        CONNECTED,
+        CLOSING,
+        CLOSED,
+        DESTROYED
+    };
+
+public:
+    wspp_callback_client(websocket_client_config config)
+        : websocket_client_callback_impl(std::move(config))
+        , m_state(CREATED)
+#ifdef CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+        , m_openssl_failed(false)
+#endif
+    {
+    }
+
+    ~wspp_callback_client() CPPREST_NOEXCEPT
+    {
+        _ASSERTE(m_state < DESTROYED);
+        State localState;
+        {
+            std::lock_guard<std::mutex> lock(m_wspp_client_lock);
+            localState = m_state;
+        } // Unlock the mutex so connect/close can use it.
+
+        // Now, what states could we be in?
+        switch (localState)
+        {
+            case DESTROYED:
+                // This should be impossible
+                std::abort();
+            case CREATED: break;
+            case CLOSED:
+            case CONNECTING:
+            case CONNECTED:
+            case CLOSING:
+                try
+                {
+                    // This will do nothing in the already-connected case
+                    pplx::task<void>(m_connect_tce).get();
+                }
+                catch (...)
+                {
+                }
+                try
+                {
+                    // This will do nothing in the already-closing case
+                    close().wait();
+                }
+                catch (...)
+                {
+                }
+                break;
+        }
+
+        // At this point, there should be no more references to me.
+        m_state = DESTROYED;
+    }
+
+    pplx::task<void> connect()
+    {
+        if (m_uri.scheme() == U("wss"))
+        {
+            m_client = std::unique_ptr<websocketpp_client_base>(new websocketpp_tls_client());
+
+            // Options specific to TLS client.
+            auto& client = m_client->client<websocketpp::config::asio_tls_client>();
+            client.set_tls_init_handler([this](websocketpp::connection_hdl) {
+                auto sslContext = websocketpp::lib::shared_ptr<boost::asio::ssl::context>(
+                    new boost::asio::ssl::context(boost::asio::ssl::context::sslv23));
+                sslContext->set_default_verify_paths();
+                sslContext->set_options(boost::asio::ssl::context::default_workarounds);
+                if (m_config.get_ssl_context_callback())
+                {
+                    m_config.get_ssl_context_callback()(*sslContext);
+                }
+                if (m_config.validate_certificates())
+                {
+                    sslContext->set_verify_mode(boost::asio::ssl::context::verify_peer);
+                }
+                else
+                {
+                    sslContext->set_verify_mode(boost::asio::ssl::context::verify_none);
+                }
+
+#ifdef CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+                m_openssl_failed = false;
+#endif
+                sslContext->set_verify_callback([this](bool preverified, boost::asio::ssl::verify_context& verifyCtx) {
+#ifdef CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+                    // Attempt to use platform certificate validation when it is available:
+                    // If OpenSSL fails we will doing verification at the end using the whole certificate chain,
+                    // so wait until the 'leaf' cert. For now return true so OpenSSL continues down the certificate
+                    // chain.
+                    if (!preverified)
+                    {
+                        m_openssl_failed = true;
+                    }
+                    if (m_openssl_failed)
+                    {
+                        return http::client::details::verify_cert_chain_platform_specific(
+                            verifyCtx, utility::conversions::to_utf8string(m_uri.host()));
+                    }
+#endif
+                    boost::asio::ssl::rfc2818_verification rfc2818(utility::conversions::to_utf8string(m_uri.host()));
+                    return rfc2818(preverified, verifyCtx);
+                });
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+                // OpenSSL stores some per thread state that never will be cleaned up until
+                // the dll is unloaded. If static linking, like we do, the state isn't cleaned up
+                // at all and will be reported as leaks.
+                // See http://www.openssl.org/support/faq.html#PROG13
+                // This is necessary here because it is called on the user's thread calling connect(...)
+                // eventually through websocketpp::client::get_connection(...)
+                ERR_remove_thread_state(nullptr);
+#endif
+
+                return sslContext;
+            });
+
+            // Options specific to underlying socket.
+            client.set_socket_init_handler([this](websocketpp::connection_hdl,
+                                                  boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& ssl_stream) {
+                // Support for SNI.
+                if (m_config.is_sni_enabled())
+                {
+                    // If user specified server name is empty default to use URI host name.
+                    if (!m_config.server_name().empty())
+                    {
+                        // OpenSSL runs the string parameter through a macro casting away const with a C style cast.
+                        // Do a C++ cast ourselves to avoid warnings.
+                        SSL_set_tlsext_host_name(ssl_stream.native_handle(),
+                                                 const_cast<char*>(m_config.server_name().c_str()));
+                    }
+                    else
+                    {
+                        const auto& server_name = utility::conversions::to_utf8string(m_uri.host());
+                        SSL_set_tlsext_host_name(ssl_stream.native_handle(), const_cast<char*>(server_name.c_str()));
+                    }
+                }
+            });
+
+            return connect_impl<websocketpp::config::asio_tls_client>();
+        }
+        else
+        {
+            m_client = std::unique_ptr<websocketpp_client_base>(new websocketpp_client());
+            return connect_impl<websocketpp::config::asio_client>();
+        }
+    }
+
+    template<typename WebsocketConfigType>
+    pplx::task<void> connect_impl()
+    {
+        auto& client = m_client->client<WebsocketConfigType>();
+
+        client.clear_access_channels(websocketpp::log::alevel::all);
+        client.clear_error_channels(websocketpp::log::alevel::all);
+        client.init_asio();
+        client.start_perpetual();
+
+        _ASSERTE(m_state == CREATED);
+        client.set_open_handler([this](websocketpp::connection_hdl) {
+            _ASSERTE(m_state == CONNECTING);
+            m_state = CONNECTED;
+            m_connect_tce.set();
+        });
+
+        client.set_fail_handler([this](websocketpp::connection_hdl con_hdl) {
+            _ASSERTE(m_state == CONNECTING);
+            this->shutdown_wspp_impl<WebsocketConfigType>(con_hdl, true);
+        });
+
+        client.set_message_handler(
+            [this](websocketpp::connection_hdl, const websocketpp::config::asio_client::message_type::ptr& msg) {
+                if (m_external_message_handler)
+                {
+                    _ASSERTE(m_state >= CONNECTED && m_state < CLOSED);
+                    websocket_incoming_message incoming_msg;
+
+                    switch (msg->get_opcode())
+                    {
+                        case websocketpp::frame::opcode::binary:
+                            incoming_msg.m_msg_type = websocket_message_type::binary_message;
+                            break;
+                        case websocketpp::frame::opcode::text:
+                            incoming_msg.m_msg_type = websocket_message_type::text_message;
+                            break;
+                        default:
+                            // Unknown message type. Since both websocketpp and our code use the RFC codes, we'll just
+                            // pass it on to the user.
+                            incoming_msg.m_msg_type = static_cast<websocket_message_type>(msg->get_opcode());
+                            break;
+                    }
+
+                    // 'move' the payload into a container buffer to avoid any copies.
+                    auto& payload = msg->get_raw_payload();
+                    incoming_msg.m_body = concurrency::streams::container_buffer<std::string>(std::move(payload));
+
+                    m_external_message_handler(incoming_msg);
+                }
+            });
+
+        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<std::string>(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<std::string>(msg);
+
+                m_external_message_handler(incoming_msg);
+            }
+        });
+
+        client.set_close_handler([this](websocketpp::connection_hdl con_hdl) {
+            _ASSERTE(m_state != CLOSED);
+            this->shutdown_wspp_impl<WebsocketConfigType>(con_hdl, false);
+        });
+
+        // Set User Agent specified by the user. This needs to happen before any connection is created
+        const auto& headers = m_config.headers();
+
+        auto user_agent_it = headers.find(web::http::header_names::user_agent);
+        if (user_agent_it != headers.end())
+        {
+            client.set_user_agent(utility::conversions::to_utf8string(user_agent_it->second));
+        }
+
+        // Get the connection handle to save for later, have to create temporary
+        // because type erasure occurs with connection_hdl.
+        websocketpp::lib::error_code ec;
+        auto con = client.get_connection(utility::conversions::to_utf8string(m_uri.to_string()), ec);
+        m_con = con;
+        if (ec.value() != 0)
+        {
+            return pplx::task_from_exception<void>(websocket_exception(ec, build_error_msg(ec, "get_connection")));
+        }
+
+        // Add any request headers specified by the user.
+        for (const auto& header : headers)
+        {
+            if (!utility::details::str_iequal(header.first, g_subProtocolHeader))
+            {
+                con->append_header(utility::conversions::to_utf8string(header.first),
+                                   utility::conversions::to_utf8string(header.second));
+            }
+        }
+
+        // Add any specified subprotocols.
+        if (headers.has(g_subProtocolHeader))
+        {
+            const std::vector<utility::string_t> protocols = m_config.subprotocols();
+            for (const auto& value : protocols)
+            {
+                con->add_subprotocol(utility::conversions::to_utf8string(value), ec);
+                if (ec.value())
+                {
+                    return pplx::task_from_exception<void>(
+                        websocket_exception(ec, build_error_msg(ec, "add_subprotocol")));
+                }
+            }
+        }
+
+        // Setup proxy options.
+        const auto& proxy = m_config.proxy();
+        if (proxy.is_specified())
+        {
+            con->set_proxy(utility::conversions::to_utf8string(proxy.address().to_string()), ec);
+            if (ec)
+            {
+                return pplx::task_from_exception<void>(websocket_exception(ec, build_error_msg(ec, "set_proxy")));
+            }
+
+            const auto& cred = proxy.credentials();
+            if (cred.is_set())
+            {
+                con->set_proxy_basic_auth(utility::conversions::to_utf8string(cred.username()),
+                                          utility::conversions::to_utf8string(*cred._internal_decrypt()),
+                                          ec);
+                if (ec)
+                {
+                    return pplx::task_from_exception<void>(
+                        websocket_exception(ec, build_error_msg(ec, "set_proxy_basic_auth")));
+                }
+            }
+        }
+
+        m_state = CONNECTING;
+        client.connect(con);
+        {
+            std::lock_guard<std::mutex> lock(m_wspp_client_lock);
+            m_thread = std::thread([&client]() {
+#if defined(__ANDROID__)
+                crossplat::get_jvm_env();
+#endif
+                client.run();
+#if defined(__ANDROID__)
+                crossplat::JVM.load()->DetachCurrentThread();
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+                // OpenSSL stores some per thread state that never will be cleaned up until
+                // the dll is unloaded. If static linking, like we do, the state isn't cleaned up
+                // at all and will be reported as leaks.
+                // See http://www.openssl.org/support/faq.html#PROG13
+                ERR_remove_thread_state(nullptr);
+#endif
+            });
+        } // unlock
+        return pplx::create_task(m_connect_tce);
+    }
+
+    pplx::task<void> send(websocket_outgoing_message& msg)
+    {
+        if (!m_connect_tce._IsTriggered())
+        {
+            return pplx::task_from_exception<void>(websocket_exception("Client not connected."));
+        }
+
+        switch (msg.m_msg_type)
+        {
+            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<void>(websocket_exception("Message Type not supported."));
+        }
+
+        const auto length = msg.m_length;
+        if (length == 0 && msg.m_msg_type != websocket_message_type::ping &&
+            msg.m_msg_type != websocket_message_type::pong)
+        {
+            return pplx::task_from_exception<void>(websocket_exception("Cannot send empty message."));
+        }
+        if (length >= UINT_MAX && length != SIZE_MAX)
+        {
+            return pplx::task_from_exception<void>(
+                websocket_exception("Message size too large. Ensure message length is less than UINT_MAX."));
+        }
+
+        auto msg_pending = m_out_queue.push(msg);
+
+        // No sends in progress
+        if (msg_pending == outgoing_msg_queue::state::was_empty)
+        {
+            // Start sending the message
+            send_msg(msg);
+        }
+
+        return pplx::create_task(msg.body_sent());
+    }
+
+    void send_msg(websocket_outgoing_message& msg)
+    {
+        auto this_client = this->shared_from_this();
+        auto& is_buf = msg.m_body;
+        auto length = msg.m_length;
+
+        if (length == SIZE_MAX)
+        {
+            // This indicates we should determine the length automatically.
+            if (is_buf.has_size())
+            {
+                // The user's stream knows how large it is -- there's no need to buffer.
+                auto buf_sz = is_buf.size();
+                if (buf_sz >= SIZE_MAX)
+                {
+                    msg.signal_body_sent(
+                        std::make_exception_ptr(websocket_exception("Cannot send messages larger than SIZE_MAX.")));
+                    return;
+                }
+                length = static_cast<size_t>(buf_sz);
+                // We have determined the length and can proceed normally.
+            }
+            else
+            {
+                // The stream needs to be buffered.
+                auto is_buf_istream = is_buf.create_istream();
+                msg.m_body = concurrency::streams::container_buffer<std::vector<uint8_t>>();
+                is_buf_istream.read_to_end(msg.m_body).then([this_client, msg](pplx::task<size_t> t) mutable {
+                    try
+                    {
+                        msg.m_length = t.get();
+                        this_client->send_msg(msg);
+                    }
+                    catch (...)
+                    {
+                        msg.signal_body_sent(std::current_exception());
+                    }
+                });
+                // We have postponed the call to send_msg() until after the data is buffered.
+                return;
+            }
+        }
+
+        // First try to acquire the data (Get a pointer to the next already allocated contiguous block of data)
+        // If acquire succeeds, send the data over the socket connection, there is no copy of data from stream to
+        // temporary buffer. If acquire fails, copy the data to a temporary buffer managed by sp_allocated and send it
+        // over the socket connection.
+        std::shared_ptr<uint8_t> sp_allocated;
+        size_t acquired_size = 0;
+        uint8_t* ptr;
+        auto read_task = pplx::task_from_result();
+        bool acquired = is_buf.acquire(ptr, acquired_size);
+
+        if (!acquired ||
+            acquired_size < length) // Stream does not support acquire or failed to acquire specified number of bytes
+        {
+            // If acquire did not return the required number of bytes, do not rely on its return value.
+            if (acquired_size < length)
+            {
+                acquired = false;
+                is_buf.release(ptr, 0);
+            }
+
+            // Allocate buffer to hold the data to be read from the stream.
+            sp_allocated.reset(new uint8_t[length], [=](uint8_t* p) { delete[] p; });
+
+            read_task = is_buf.getn(sp_allocated.get(), length).then([length](size_t bytes_read) {
+                if (bytes_read != length)
+                {
+                    throw websocket_exception("Failed to read required length of data from the stream.");
+                }
+            });
+        }
+        else
+        {
+            // Acquire succeeded, assign the acquired pointer to sp_allocated. Use an empty custom destructor
+            // so that the data is not released when sp_allocated goes out of scope. The streambuf will manage its
+            // memory.
+            sp_allocated.reset(ptr, [](uint8_t*) {});
+        }
+
+        read_task
+            .then([this_client, msg, sp_allocated, length]() {
+                std::lock_guard<std::mutex> lock(this_client->m_wspp_client_lock);
+                if (this_client->m_state > CONNECTED)
+                {
+                    // The client has already been closed.
+                    throw websocket_exception("Websocket connection is closed.");
+                }
+
+                websocketpp::lib::error_code ec;
+                if (this_client->m_client->is_tls_client())
+                {
+                    this_client->send_msg_impl<websocketpp::config::asio_tls_client>(
+                        this_client, msg, sp_allocated, length, ec);
+                }
+                else
+                {
+                    this_client->send_msg_impl<websocketpp::config::asio_client>(
+                        this_client, msg, sp_allocated, length, ec);
+                }
+                return ec;
+            })
+            .then([this_client, msg, is_buf, acquired, sp_allocated, length](
+                      pplx::task<websocketpp::lib::error_code> previousTask) mutable {
+                std::exception_ptr eptr;
+                try
+                {
+                    // Catch exceptions from previous tasks, if any and convert it to websocket exception.
+                    const auto& ec = previousTask.get();
+                    if (ec.value() != 0)
+                    {
+                        eptr = std::make_exception_ptr(websocket_exception(ec, build_error_msg(ec, "sending message")));
+                    }
+                }
+                catch (...)
+                {
+                    eptr = std::current_exception();
+                }
+
+                if (acquired)
+                {
+                    is_buf.release(sp_allocated.get(), length);
+                }
+
+                // Set the send_task_completion_event after calling release.
+                if (eptr)
+                {
+                    msg.signal_body_sent(eptr);
+                }
+                else
+                {
+                    msg.signal_body_sent();
+                }
+
+                websocket_outgoing_message next_msg;
+                bool msg_pending = this_client->m_out_queue.pop_and_peek(next_msg);
+
+                if (msg_pending)
+                {
+                    this_client->send_msg(next_msg);
+                }
+            });
+    }
+
+    pplx::task<void> close()
+    {
+        return close(static_cast<websocket_close_status>(websocketpp::close::status::normal), U("Normal"));
+    }
+
+    pplx::task<void> close(websocket_close_status status, const utility::string_t& reason)
+    {
+        websocketpp::lib::error_code ec;
+        {
+            std::lock_guard<std::mutex> lock(m_wspp_client_lock);
+            if (m_state == CONNECTED)
+            {
+                m_state = CLOSING;
+                if (m_client->is_tls_client())
+                {
+                    close_impl<websocketpp::config::asio_tls_client>(status, reason, ec);
+                }
+                else
+                {
+                    close_impl<websocketpp::config::asio_client>(status, reason, ec);
+                }
+            }
+        }
+        return pplx::task<void>(m_close_tce);
+    }
+
+private:
+    template<typename WebsocketConfigType>
+    void shutdown_wspp_impl(const websocketpp::connection_hdl& con_hdl, bool connecting)
+    {
+        // Only need to hold the lock when setting the state to closed.
+        {
+            std::lock_guard<std::mutex> lock(m_wspp_client_lock);
+            m_state = CLOSED;
+        }
+
+        auto& client = m_client->client<WebsocketConfigType>();
+        const auto& connection = client.get_con_from_hdl(con_hdl);
+        const auto& closeCode = connection->get_local_close_code();
+        const auto& reason = connection->get_local_close_reason();
+        const auto& ec = connection->get_ec();
+        client.stop_perpetual();
+
+        // Can't join thread directly since it is the current thread.
+        pplx::create_task([] {}).then([this, connecting, ec, closeCode, reason]() mutable {
+            {
+                std::lock_guard<std::mutex> lock(m_wspp_client_lock);
+                if (m_thread.joinable())
+                {
+                    m_thread.join();
+                }
+            } // unlock
+
+            if (connecting)
+            {
+                websocket_exception exc(ec, build_error_msg(ec, "set_fail_handler"));
+                m_connect_tce.set_exception(exc);
+            }
+            if (m_external_close_handler)
+            {
+                m_external_close_handler(
+                    static_cast<websocket_close_status>(closeCode), utility::conversions::to_string_t(reason), ec);
+            }
+            // Making a local copy of the TCE prevents it from being destroyed along with "this"
+            auto tceref = m_close_tce;
+            tceref.set();
+        });
+    }
+
+    template<typename WebsocketClientType>
+    static void send_msg_impl(const std::shared_ptr<wspp_callback_client>& this_client,
+                              const websocket_outgoing_message& msg,
+                              const std::shared_ptr<uint8_t>& sp_allocated,
+                              size_t length,
+                              websocketpp::lib::error_code& ec)
+    {
+        auto& client = this_client->m_client->client<WebsocketClientType>();
+        switch (msg.m_msg_type)
+        {
+            case websocket_message_type::text_message:
+                client.send(this_client->m_con, sp_allocated.get(), length, websocketpp::frame::opcode::text, ec);
+                break;
+            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::ping:
+            {
+                std::string s(reinterpret_cast<char*>(sp_allocated.get()), length);
+                client.ping(this_client->m_con, s, ec);
+                break;
+            }
+            case websocket_message_type::pong:
+            {
+                std::string s(reinterpret_cast<char*>(sp_allocated.get()), length);
+                client.pong(this_client->m_con, s, ec);
+                break;
+            }
+            default:
+                // This case should have already been filtered above.
+                std::abort();
+        }
+    }
+
+    template<typename WebsocketConfig>
+    void close_impl(websocket_close_status status, const utility::string_t& reason, websocketpp::lib::error_code& ec)
+    {
+        auto& client = m_client->client<WebsocketConfig>();
+        client.close(m_con,
+                     static_cast<websocketpp::close::status::value>(status),
+                     utility::conversions::to_utf8string(reason),
+                     ec);
+    }
+
+    void set_message_handler(const std::function<void(const websocket_incoming_message&)>& handler)
+    {
+        m_external_message_handler = handler;
+    }
+
+    void set_close_handler(
+        const std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)>& handler)
+    {
+        m_external_close_handler = handler;
+    }
+
+    std::thread m_thread;
+
+    // Perform type erasure to set the websocketpp client in use at runtime
+    // after construction based on the URI.
+    struct websocketpp_client_base
+    {
+        virtual ~websocketpp_client_base() CPPREST_NOEXCEPT {}
+        template<typename WebsocketConfig>
+        websocketpp::client<WebsocketConfig>& client()
+        {
+            if (is_tls_client())
+            {
+                return reinterpret_cast<websocketpp::client<WebsocketConfig>&>(tls_client());
+            }
+            else
+            {
+                return reinterpret_cast<websocketpp::client<WebsocketConfig>&>(non_tls_client());
+            }
+        }
+        virtual websocketpp::client<websocketpp::config::asio_client>& non_tls_client() { throw std::bad_cast(); }
+        virtual websocketpp::client<websocketpp::config::asio_tls_client>& tls_client() { throw std::bad_cast(); }
+        virtual bool is_tls_client() const = 0;
+    };
+    struct websocketpp_client : websocketpp_client_base
+    {
+        ~websocketpp_client() CPPREST_NOEXCEPT {}
+        websocketpp::client<websocketpp::config::asio_client>& non_tls_client() override { return m_client; }
+        bool is_tls_client() const override { return false; }
+        websocketpp::client<websocketpp::config::asio_client> m_client;
+    };
+    struct websocketpp_tls_client : websocketpp_client_base
+    {
+        ~websocketpp_tls_client() CPPREST_NOEXCEPT {}
+        websocketpp::client<websocketpp::config::asio_tls_client>& tls_client() override { return m_client; }
+        bool is_tls_client() const override { return true; }
+        websocketpp::client<websocketpp::config::asio_tls_client> m_client;
+    };
+
+    pplx::task_completion_event<void> m_connect_tce;
+    pplx::task_completion_event<void> m_close_tce;
+
+    // Used to safe guard the wspp client.
+    std::mutex m_wspp_client_lock;
+    State m_state;
+    std::unique_ptr<websocketpp_client_base> m_client;
+    websocketpp::connection_hdl m_con;
+
+    // Queue to track pending sends
+    outgoing_msg_queue m_out_queue;
+
+    // External callback for handling received and close event
+    std::function<void(websocket_incoming_message)> m_external_message_handler;
+    std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)>
+        m_external_close_handler;
+
+    // Used to track if any of the OpenSSL server certificate verifications
+    // failed. This can safely be tracked at the client level since connections
+    // only happen once for each client.
+#ifdef CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE
+    bool m_openssl_failed;
+#endif
+};
+
+websocket_client_task_impl::websocket_client_task_impl(websocket_client_config config)
+    : m_client_closed(false), m_callback_client(std::make_shared<details::wspp_callback_client>(std::move(config)))
+{
+    set_handler();
+}
+} // namespace details
+
+websocket_callback_client::websocket_callback_client()
+    : m_client(std::make_shared<details::wspp_callback_client>(websocket_client_config()))
+{
+}
+
+websocket_callback_client::websocket_callback_client(websocket_client_config config)
+    : m_client(std::make_shared<details::wspp_callback_client>(std::move(config)))
+{
+}
+
+} // namespace client
+} // namespace websockets
+} // namespace web
+
+#endif
diff --git a/Release/src/websockets/client/ws_msg.cpp b/Release/src/websockets/client/ws_msg.cpp
new file mode 100644 (file)
index 0000000..730e625
--- /dev/null
@@ -0,0 +1,79 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Websocket library: Client-side APIs.
+ *
+ * This file contains the websocket message implementation
+ *
+ * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "cpprest/ws_msg.h"
+
+#include "../../http/common/internal_http_helpers.h"
+#include "cpprest/ws_client.h"
+#include <sstream>
+
+#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
+
+using namespace concurrency;
+using namespace concurrency::streams::details;
+
+namespace web
+{
+namespace websockets
+{
+namespace client
+{
+static ::utility::string_t g_subProtocolHeader = _XPLATSTR("Sec-WebSocket-Protocol");
+
+void websocket_client_config::set_user_agent(const utf8string& user_agent)
+{
+    headers().add(web::http::header_names::user_agent, utility::conversions::to_string_t(user_agent));
+}
+
+void websocket_client_config::add_subprotocol(const ::utility::string_t& name)
+{
+    m_headers.add(g_subProtocolHeader, name);
+}
+
+std::vector<::utility::string_t> websocket_client_config::subprotocols() const
+{
+    std::vector<::utility::string_t> values;
+    auto subprotocolHeader = m_headers.find(g_subProtocolHeader);
+    if (subprotocolHeader != m_headers.end())
+    {
+        utility::istringstream_t header(subprotocolHeader->second);
+        utility::string_t token;
+        while (std::getline(header, token, U(',')))
+        {
+            http::details::trim_whitespace(token);
+            if (!token.empty())
+            {
+                values.push_back(token);
+            }
+        }
+    }
+    return values;
+}
+
+pplx::task<std::string> websocket_incoming_message::extract_string() const
+{
+    if (m_msg_type == websocket_message_type::binary_message)
+    {
+        return pplx::task_from_exception<std::string>(websocket_exception("Invalid message type"));
+    }
+    return pplx::task_from_result(std::move(m_body.collection()));
+}
+
+} // namespace client
+} // namespace websockets
+} // namespace web
+
+#endif
diff --git a/Release/tests/.gitignore b/Release/tests/.gitignore
new file mode 100644 (file)
index 0000000..211686a
--- /dev/null
@@ -0,0 +1 @@
+*.cmake
diff --git a/Release/tests/CMakeLists.txt b/Release/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0730f29
--- /dev/null
@@ -0,0 +1,7 @@
+set(UnitTestpp_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/common/UnitTestpp)
+set(Utilities_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/common/utilities/include)
+
+include_directories (${UnitTestpp_INCLUDE_DIR} ${Utilities_INCLUDE_DIR})
+
+add_subdirectory(common)
+add_subdirectory(functional)
diff --git a/Release/tests/common/CMakeLists.txt b/Release/tests/common/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8be5176
--- /dev/null
@@ -0,0 +1,3 @@
+add_subdirectory(utilities)
+add_subdirectory(UnitTestpp)
+add_subdirectory(TestRunner)
diff --git a/Release/tests/common/TestRunner/CMakeLists.txt b/Release/tests/common/TestRunner/CMakeLists.txt
new file mode 100644 (file)
index 0000000..48540d5
--- /dev/null
@@ -0,0 +1,67 @@
+if (WIN32)
+  if (WINDOWS_STORE OR WINDOWS_PHONE)
+    add_definitions(-DWINRT_TEST_RUNNER -D_CONSOLE)
+  else()
+    add_definitions(-DDESKTOP_TEST_RUNNER)
+  endif()
+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)
+  target_link_libraries(test_runner PRIVATE
+    -Wl,-force_load httpclient_test
+    -Wl,-force_load json_test
+    -Wl,-force_load uri_test
+    -Wl,-force_load pplx_test
+    -Wl,-force_load httplistener_test
+    -Wl,-force_load streams_test
+    -Wl,-force_load utils_test
+    )
+elseif(UNIX)
+  target_link_libraries(test_runner PRIVATE
+    -Wl,--whole-archive
+    httpclient_test
+    json_test
+    uri_test
+    pplx_test
+    httplistener_test
+    streams_test
+    utils_test
+    -Wl,--no-whole-archive
+    )
+else()
+  # In order to achieve --whole-archive on windows, we link all the test files into the test_runner directly
+  # This means that the tests themselves must be created as "OBJECT" libraries
+  target_sources(test_runner PRIVATE
+    $<TARGET_OBJECTS:httpclient_test>
+    $<TARGET_OBJECTS:json_test>
+    $<TARGET_OBJECTS:uri_test>
+    $<TARGET_OBJECTS:pplx_test>
+    $<TARGET_OBJECTS:streams_test>
+    $<TARGET_OBJECTS:utils_test>
+  )
+  if(NOT WINDOWS_STORE AND NOT WINDOWS_PHONE)
+    target_sources(test_runner PRIVATE $<TARGET_OBJECTS:httplistener_test>)
+  endif()
+  target_link_libraries(test_runner PRIVATE
+    common_utilities
+    httptest_utilities
+    cpprest
+  )
+  if(TARGET websockettest_utilities)
+      target_link_libraries(test_runner PRIVATE websockettest_utilities)
+  endif()
+  if(CPPREST_WEBSOCKETS_IMPL STREQUAL "wspp")
+    cpprest_find_websocketpp()
+    target_link_libraries(test_runner PRIVATE cpprestsdk_websocketpp_internal)
+  endif()
+  if (WINDOWS_STORE)
+    target_link_libraries(test_runner PRIVATE ucrtd.lib vcruntimed.lib vccorlibd.lib msvcrtd.lib msvcprtd.lib concrtd.lib RuntimeObject.lib)
+  endif()
+endif()
diff --git a/Release/tests/common/TestRunner/test_module_loader.cpp b/Release/tests/common/TestRunner/test_module_loader.cpp
new file mode 100644 (file)
index 0000000..b6cb3c0
--- /dev/null
@@ -0,0 +1,166 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ */
+#ifdef WIN32
+#include <Windows.h>
+#else
+#include "dlfcn.h"
+#include <boost/filesystem.hpp>
+#endif
+
+#include "test_module_loader.h"
+#include <iostream>
+
+class test_module
+{
+public:
+    test_module(const std::string& dllName) : m_dllName(dllName), m_handle(nullptr) {}
+
+    GetTestsFunc get_test_list()
+    {
+#if defined(_WIN32)
+        return (GetTestsFunc)GetProcAddress(m_handle, "GetTestList");
+#else
+        auto ptr = dlsym(m_handle, "GetTestList");
+        if (ptr == nullptr)
+        {
+            std::cerr << "couldn't find GetTestList"
+                      <<
+#ifdef __APPLE__
+                " " << dlerror() <<
+#endif
+                std::endl;
+        }
+        return (GetTestsFunc)ptr;
+#endif
+    }
+
+    unsigned long load()
+    {
+        if (m_handle == nullptr)
+        {
+#if defined(_WIN32)
+            // Make sure ends in .dll
+            if (*(m_dllName.end() - 1) != 'l' || *(m_dllName.end() - 2) != 'l' || *(m_dllName.end() - 3) != 'd' ||
+                *(m_dllName.end() - 4) != '.')
+            {
+                return (unsigned long)-1;
+            }
+            m_handle = LoadLibraryA(m_dllName.c_str());
+            if (m_handle == nullptr)
+            {
+                return GetLastError();
+            }
+            return 0;
+#else
+#ifdef __APPLE__
+            auto exe_directory = getcwd(nullptr, 0);
+            auto path = std::string(exe_directory) + "/" + m_dllName;
+            free(exe_directory);
+#else
+            auto path = boost::filesystem::initial_path().string() + "/" + m_dllName;
+#endif
+
+            m_handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL);
+            if (m_handle == nullptr)
+            {
+                std::cerr << std::string(dlerror()) << std::endl;
+                return -1;
+            }
+            return 0;
+#endif
+        }
+        return 0;
+    }
+
+    unsigned long unload()
+    {
+        if (m_handle != nullptr)
+        {
+#if defined(_WIN32)
+            if (!FreeLibrary(m_handle))
+            {
+                return GetLastError();
+            }
+            m_handle = nullptr;
+            return 0;
+#else
+            if (dlclose(m_handle) != 0)
+            {
+                std::cerr << std::string(dlerror()) << std::endl;
+                return -1;
+            }
+            m_handle = nullptr;
+            return 0;
+#endif
+        }
+        return 0;
+    }
+
+private:
+    const std::string m_dllName;
+
+#if defined(_WIN32)
+    HMODULE m_handle;
+#else
+    void* m_handle;
+#endif
+
+    test_module(const test_module&) = delete;
+    test_module& operator=(const test_module&) = delete;
+};
+
+test_module_loader::test_module_loader() {}
+
+test_module_loader::~test_module_loader()
+{
+    for (auto iter = m_modules.begin(); iter != m_modules.end(); ++iter)
+    {
+        iter->second->unload();
+        delete iter->second;
+    }
+}
+
+unsigned long test_module_loader::load(const std::string& dllName)
+{
+    // Check if the module is already loaded.
+    if (m_modules.find(dllName) != m_modules.end())
+    {
+        return 0;
+    }
+
+    test_module* pModule;
+    pModule = new test_module(dllName);
+
+    // Load dll.
+    const unsigned long error_code = pModule->load();
+    if (error_code != 0)
+    {
+        delete pModule;
+        return error_code;
+    }
+    else
+    {
+        m_modules[dllName] = pModule;
+    }
+    return 0;
+}
+
+UnitTest::TestList g_list;
+
+UnitTest::TestList& test_module_loader::get_test_list(const std::string& dllName)
+{
+    GetTestsFunc getTestsFunc = m_modules[dllName]->get_test_list();
+
+    // If there is no GetTestList function then it must be a dll without any tests.
+    // Simply return an empty TestList.
+    if (getTestsFunc == nullptr)
+    {
+        return g_list;
+    }
+
+    return getTestsFunc();
+}
diff --git a/Release/tests/common/TestRunner/test_module_loader.h b/Release/tests/common/TestRunner/test_module_loader.h
new file mode 100644 (file)
index 0000000..ce52328
--- /dev/null
@@ -0,0 +1,40 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ ***/
+
+#ifndef INCLUDED_TEST_MODULE_LOADER
+#define INCLUDED_TEST_MODULE_LOADER
+
+#include "unittestpp.h"
+#include <string>
+
+// Exported function from all test dlls.
+typedef UnitTest::TestList&(__cdecl* GetTestsFunc)();
+
+// Interface to implement on each platform to be be able to load/unload and call global functions.
+class test_module;
+
+// Handles organizing all test binaries and using the correct module loader.
+class test_module_loader
+{
+public:
+    test_module_loader();
+    ~test_module_loader();
+
+    // Does't complain if module with same name is already loaded.
+    unsigned long load(const std::string& dllName);
+
+    // Module must have already been loaded.
+    UnitTest::TestList& get_test_list(const std::string& dllName);
+
+private:
+    test_module_loader(const test_module_loader&) = delete;
+    test_module_loader& operator=(const test_module_loader&) = delete;
+
+    std::map<std::string, test_module*> m_modules;
+};
+
+#endif
diff --git a/Release/tests/common/TestRunner/test_runner.cpp b/Release/tests/common/TestRunner/test_runner.cpp
new file mode 100644 (file)
index 0000000..4913699
--- /dev/null
@@ -0,0 +1,643 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ **/
+// TestRunner.cpp : Defines the entry point for the console application.
+//
+
+#include <algorithm>
+#include <iostream>
+#include <map>
+#include <regex>
+#include <vector>
+
+#ifdef _WIN32
+#include <conio.h>
+
+#include <Windows.h>
+#else
+#include <unistd.h>
+#ifdef __APPLE__
+#include <dirent.h>
+#else
+#include <boost/filesystem.hpp>
+#endif
+#endif
+
+#include "../UnitTestpp/src/GlobalSettings.h"
+#include "../UnitTestpp/src/TestReporterStdout.h"
+#include "../UnitTestpp/src/TimeHelpers.h"
+#include "test_module_loader.h"
+
+static void print_help()
+{
+    std::cout
+        << "Usage: testrunner.exe <test_binaries> [/list] [/listproperties] [/noignore] [/breakonerror] [/detectleaks]"
+        << std::endl;
+    std::cout << "    [/name:<test_name>] [/select:@key=value] [/loop:<num_times>]" << std::endl;
+    std::cout << std::endl;
+    std::cout << "    /list              List all the names of the test_binaries and their" << std::endl;
+    std::cout << "                       test cases." << std::endl;
+    std::cout << std::endl;
+    std::cout << "    /listproperties    List all the names of the test binaries, test cases, and" << std::endl;
+    std::cout << "                       test properties." << std::endl;
+    std::cout << std::endl;
+    std::cout << "    /breakonerror      Break into the debugger when a failure is encountered." << std::endl;
+    std::cout << "    /detectleaks       Turns CRT leak detection and prints any leaks, Windows only." << std::endl;
+    std::cout << std::endl;
+    std::cout << "    /name:<test_name>  Run only test cases with matching name. Can contain the" << std::endl;
+    std::cout << "                       wildcard '*' character." << std::endl;
+    std::cout << std::endl;
+    std::cout << "    /noignore          Include tests even if they have the 'Ignore' property set" << std::endl;
+    std::cout << std::endl;
+    std::cout << "    /select:@key=value Filter by the value of a particular test property." << std::endl;
+    std::cout << std::endl;
+    std::cout << "    /loop:<num_times>  Run test cases a specified number of times." << std::endl;
+
+    std::cout << std::endl;
+    std::cout << "Can also specify general global settings with the following:" << std::endl;
+    std::cout << "    /global_key:global_value OR /global_key" << std::endl << std::endl;
+}
+
+static std::string to_lower(const std::string& str)
+{
+    std::string lower;
+    for (auto iter = str.begin(); iter != str.end(); ++iter)
+    {
+        lower.push_back((char)tolower(*iter));
+    }
+    return lower;
+}
+
+static std::vector<std::string> get_files_in_directory()
+{
+    std::vector<std::string> files;
+
+#ifdef _WIN32
+
+    char exe_directory_buffer[MAX_PATH];
+    GetModuleFileNameA(NULL, exe_directory_buffer, MAX_PATH);
+    std::string exe_directory = to_lower(exe_directory_buffer);
+    auto location = exe_directory.rfind("\\");
+    if (location != std::string::npos)
+    {
+        exe_directory.erase(location + 1);
+    }
+    else
+    {
+        std::cout << "Could not determine execution directory" << std::endl;
+        exit(-1);
+    }
+
+    exe_directory.append("*");
+    WIN32_FIND_DATAA findFileData;
+    HANDLE hFind = FindFirstFileA(exe_directory.c_str(), &findFileData);
+    if (hFind != INVALID_HANDLE_VALUE && !(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+    {
+        files.push_back(findFileData.cFileName);
+    }
+    while (FindNextFileA(hFind, &findFileData) != 0)
+    {
+        if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+        {
+            files.push_back(findFileData.cFileName);
+        }
+    }
+    FindClose(hFind);
+
+#elif defined(__APPLE__)
+    auto exe_directory = getcwd(nullptr, 0);
+
+    DIR* dir = opendir(exe_directory);
+    free(exe_directory);
+
+    if (dir != nullptr)
+    {
+        struct dirent* ent = readdir(dir);
+        while (ent != nullptr)
+        {
+            if (ent->d_type == DT_REG)
+            {
+                files.push_back(ent->d_name);
+            }
+            ent = readdir(dir);
+        }
+        closedir(dir);
+    }
+#else
+    using namespace boost::filesystem;
+
+    auto exe_directory = initial_path().string();
+    for (auto it = directory_iterator(path(exe_directory)); it != directory_iterator(); ++it)
+    {
+        if (is_regular_file(*it))
+        {
+            files.push_back(it->path().filename().string());
+        }
+    }
+#endif
+
+    return files;
+}
+
+static std::string replace_wildcard_for_regex(const std::string& str)
+{
+    std::string result;
+    for (auto iter = str.begin(); iter != str.end(); ++iter)
+    {
+        if (*iter == '*')
+        {
+            result.push_back('.');
+        }
+        result.push_back(*iter);
+    }
+    return result;
+}
+
+static std::vector<std::string> get_matching_binaries(const std::string& dllName)
+{
+    std::vector<std::string> matchingFiles;
+
+    // If starts with .\ remove it.
+    std::string expandedDllName(dllName);
+    if (expandedDllName.size() > 2 && expandedDllName[0] == '.' && expandedDllName[1] == '\\')
+    {
+        expandedDllName = expandedDllName.substr(2);
+    }
+
+    // Escape any '.'
+    size_t oldLocation = 0;
+    size_t location = expandedDllName.find(".", oldLocation);
+    while (location != std::string::npos)
+    {
+        expandedDllName.insert(expandedDllName.find(".", oldLocation), "\\");
+        oldLocation = location + 2;
+        location = expandedDllName.find(".", oldLocation);
+    }
+
+    // Replace all '*' in dllName with '.*'
+    expandedDllName = replace_wildcard_for_regex(expandedDllName);
+
+    std::vector<std::string> allFiles = get_files_in_directory();
+
+    // Filter out any files that don't match.
+    std::regex dllRegex(expandedDllName, std::regex_constants::icase);
+
+    for (auto iter = allFiles.begin(); iter != allFiles.end(); ++iter)
+    {
+        if (std::regex_match(*iter, dllRegex))
+        {
+            matchingFiles.push_back(*iter);
+        }
+    }
+
+    return matchingFiles;
+}
+
+static std::multimap<std::string, std::string> g_properties;
+static std::vector<std::string> g_test_binaries;
+static int g_individual_test_timeout = 60000 * 3;
+
+static int parse_command_line(int argc, char** argv)
+{
+    for (int i = 1; i < argc; ++i)
+    {
+        std::string arg(argv[i]);
+        arg = to_lower(arg);
+
+        if (arg.compare("/?") == 0)
+        {
+            print_help();
+            return -1;
+        }
+        else if (arg.find("/") == 0)
+        {
+            if (arg.find("/select:@") == 0)
+            {
+                std::string prop_asgn = std::string(argv[i]).substr(std::string("/select:@").size());
+                auto eqsgn = prop_asgn.find('=');
+                if (eqsgn < prop_asgn.size())
+                {
+                    auto key = prop_asgn.substr(0, eqsgn);
+                    auto value = prop_asgn.substr(eqsgn + 1);
+                    g_properties.insert(std::make_pair(key, value));
+                }
+                else
+                {
+                    g_properties.insert(std::make_pair(prop_asgn, "*"));
+                }
+            }
+            else if (arg.find(":") != std::string::npos)
+            {
+                const size_t index = arg.find(":");
+                const std::string key = std::string(argv[i]).substr(1, index - 1);
+                const std::string value = std::string(argv[i]).substr(index + 1);
+                UnitTest::GlobalSettings::Add(key, value);
+            }
+            else
+            {
+                UnitTest::GlobalSettings::Add(arg.substr(1), std::string{});
+            }
+        }
+        else if (arg.find("/debug") == 0)
+        {
+            printf("Attach debugger now...\n");
+            int temp;
+            std::cin >> temp;
+        }
+        else
+        {
+            g_test_binaries.push_back(arg);
+        }
+    }
+
+    return 0;
+}
+
+static bool matched_properties(const UnitTest::TestProperties& test_props)
+{
+    // TestRunner can only execute either desktop or winrt tests, but not both.
+    // This starts with visual studio versions after VS 2012.
+#if defined(_MSC_VER) && (_MSC_VER >= 1800)
+#ifdef WINRT_TEST_RUNNER
+    UnitTest::GlobalSettings::Add("winrt", std::string{});
+#elif defined DESKTOP_TEST_RUNNER
+    UnitTest::GlobalSettings::Add("desktop", std::string{});
+#endif
+#endif
+
+    // The 'Require' property on a test case is special.
+    // It requires a certain global setting to be fulfilled to execute.
+    if (test_props.Has("Requires"))
+    {
+        const std::string requires = test_props.Get("Requires");
+        std::vector<std::string> requirements;
+
+        // Can be multiple requirements, a semi colon seperated list
+        std::string::size_type pos = requires.find_first_of(';');
+        std::string::size_type last_pos = 0;
+        while (pos != std::string::npos)
+        {
+            requirements.push_back(requires.substr(last_pos, pos - last_pos));
+            last_pos = pos + 1;
+            pos = requires.find_first_of(';', last_pos);
+        }
+        requirements.push_back(requires.substr(last_pos));
+        for (auto iter = requirements.begin(); iter != requirements.end(); ++iter)
+        {
+            if (!UnitTest::GlobalSettings::Has(to_lower(*iter)))
+            {
+                return false;
+            }
+        }
+    }
+
+    if (g_properties.size() == 0) return true;
+
+    // All the properties specified at the cmd line act as a 'filter'.
+    for (auto iter = g_properties.begin(); iter != g_properties.end(); ++iter)
+    {
+        auto name = iter->first;
+        auto value = iter->second;
+        if (test_props.Has(name) && (value == "*" || test_props[name] == value))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+// Functions to list all the test cases and their properties.
+static void handle_list_option(bool listProperties, const UnitTest::TestList& tests, const std::regex& nameRegex)
+{
+    UnitTest::Test* pTest = tests.GetFirst();
+    while (pTest != nullptr)
+    {
+        std::string fullTestName = pTest->m_details.suiteName;
+        fullTestName.append(":");
+        fullTestName.append(pTest->m_details.testName);
+
+        if (matched_properties(pTest->m_properties) && std::regex_match(fullTestName, nameRegex))
+        {
+            std::cout << "    " << fullTestName << std::endl;
+            if (listProperties)
+            {
+                std::for_each(pTest->m_properties.begin(),
+                              pTest->m_properties.end(),
+                              [&](const std::pair<std::string, std::string> key_value) {
+                                  std::cout << "        " << key_value.first << ": " << key_value.second << std::endl;
+                              });
+            }
+        }
+        pTest = pTest->m_nextTest;
+    }
+}
+
+static void ChangeConsoleTextColorToRed()
+{
+#if defined(__cplusplus_winrt)
+#elif defined(_WIN32)
+    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0004 | 0x0008);
+#else
+    std::cout << "\033[1;31m";
+#endif
+}
+
+static void ChangeConsoleTextColorToGreen()
+{
+#if defined(__cplusplus_winrt)
+#elif defined(_WIN32)
+    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0002 | 0x0008);
+#else
+    std::cout << "\033[1;32m";
+#endif
+}
+
+static void ChangeConsoleTextColorToGrey()
+{
+#if defined(__cplusplus_winrt)
+#elif defined(_WIN32)
+    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
+#else
+    std::cout << "\033[0m";
+#endif
+}
+
+bool IsTestIgnored(UnitTest::Test* pTest)
+{
+    if (pTest->m_properties.Has("Ignore")) return true;
+#ifdef _WIN32
+    if (pTest->m_properties.Has("Ignore:Windows")) return true;
+#elif defined(__APPLE__)
+    if (pTest->m_properties.Has("Ignore:Apple")) return true;
+#elif (defined(ANDROID) || defined(__ANDROID__))
+    if (pTest->m_properties.Has("Ignore:Android")) return true;
+#else
+    if (pTest->m_properties.Has("Ignore:Linux")) return true;
+#endif
+    return false;
+}
+
+typedef std::map<std::string, UnitTest::TestList> testlist_t;
+
+void list_test_options(testlist_t& testlists)
+{
+    std::regex nameRegex;
+
+    if (UnitTest::GlobalSettings::Has("name"))
+    {
+        nameRegex = replace_wildcard_for_regex(UnitTest::GlobalSettings::Get("name"));
+    }
+    else
+    {
+        nameRegex = std::regex(".*");
+    }
+
+    bool listProperties = UnitTest::GlobalSettings::Has("listproperties");
+
+    for (auto& test_p : testlists)
+    {
+        std::cout << "=== Showing options for " << test_p.first << " ===" << std::endl;
+        handle_list_option(listProperties, test_p.second, nameRegex);
+    }
+}
+
+testlist_t load_all_tests(test_module_loader& module_loader)
+{
+    // Remember where each list of tests came from.
+    testlist_t testlists;
+
+    // Retrieve the static tests and clear for dll loading.
+    testlists.insert({"<static>", UnitTest::GetTestList()});
+    UnitTest::GetTestList().Clear();
+
+    // Cycle through all the test binaries and load them
+    for (auto& binary_names : g_test_binaries)
+    {
+        std::vector<std::string> matchingBinaries = get_matching_binaries(binary_names);
+        if (matchingBinaries.empty())
+        {
+            ChangeConsoleTextColorToRed();
+            std::cout << "Pattern '" << binary_names << "' not found." << std::endl;
+            ChangeConsoleTextColorToGrey();
+        }
+        for (auto& binary : matchingBinaries)
+        {
+            unsigned long error_code = module_loader.load(binary);
+            if (error_code != 0)
+            {
+                // Only omit an error if a wildcard wasn't used.
+                if (binary_names.find('*') == std::string::npos)
+                {
+                    ChangeConsoleTextColorToRed();
+                    std::cout << "Error loading " << binary << ": " << error_code << std::endl;
+                    ChangeConsoleTextColorToGrey();
+
+                    std::exit(error_code);
+                }
+                else
+                {
+                    continue;
+                }
+            }
+            std::cout << "Loaded " << binary << "..." << std::endl;
+
+            // Store the loaded binary into the test list map
+            testlists.insert({binary, UnitTest::GetTestList()});
+            UnitTest::GetTestList().Clear();
+        }
+    }
+
+    return testlists;
+}
+
+void run_all_tests(UnitTest::TestRunner& testRunner, testlist_t& testlists)
+{
+    int numTimesToRun = 1;
+    if (UnitTest::GlobalSettings::Has("loop"))
+    {
+        std::istringstream strstream(UnitTest::GlobalSettings::Get("loop"));
+        strstream >> numTimesToRun;
+    }
+
+    const bool include_ignored_tests = UnitTest::GlobalSettings::Has("noignore");
+
+    for (int i = 0; i < numTimesToRun; ++i)
+    {
+        for (auto& test_p : testlists)
+        {
+            std::cout << "=== Running tests from: " << test_p.first << " ===" << std::endl;
+            UnitTest::TestList& tests = test_p.second;
+
+            std::regex nameRegex(".*");
+
+            if (UnitTest::GlobalSettings::Has("name"))
+            {
+                nameRegex = replace_wildcard_for_regex(UnitTest::GlobalSettings::Get("name"));
+            }
+            testRunner.RunTestsIf(tests,
+                                  [&](UnitTest::Test* pTest) -> bool {
+                                      // Combine suite and test name
+                                      std::string fullTestName = pTest->m_details.suiteName;
+                                      fullTestName.append(":");
+                                      fullTestName.append(pTest->m_details.testName);
+
+                                      if (IsTestIgnored(pTest) && !include_ignored_tests)
+                                          return false;
+                                      else
+                                          return matched_properties(pTest->m_properties) &&
+                                                 std::regex_match(fullTestName, nameRegex);
+                                  },
+                                  g_individual_test_timeout);
+        }
+    }
+}
+
+#if defined(__cplusplus_winrt)
+#include "ROApi.h"
+#endif
+
+int main(int argc, char* argv[])
+{
+#if defined(__cplusplus_winrt)
+    Windows::Foundation::Initialize(RO_INIT_MULTITHREADED);
+#elif defined(_WIN32)
+    // Add standard error as output as well.
+    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_WNDW | _CRTDBG_MODE_DEBUG);
+    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_WNDW | _CRTDBG_MODE_DEBUG);
+    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+
+    // The test runner built with WinRT support might be used on a pre Win8 machine.
+    // Obviously in that case WinRT test cases can't run, but non WinRT ones should be
+    // fine. So dynamically try to call RoInitialize/RoUninitialize.
+    HMODULE hComBase = LoadLibrary(L"combase.dll");
+    if (hComBase != nullptr)
+    {
+        typedef HRESULT(WINAPI * RoInit)(int);
+        RoInit roInitFunc = (RoInit)GetProcAddress(hComBase, "RoInitialize");
+        if (roInitFunc != nullptr)
+        {
+            roInitFunc(1); // RO_INIT_MULTITHREADED
+        }
+    }
+
+    struct console_restorer
+    {
+        CONSOLE_SCREEN_BUFFER_INFO m_originalConsoleInfo;
+        console_restorer() { GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &m_originalConsoleInfo); }
+        ~console_restorer()
+        {
+            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), m_originalConsoleInfo.wAttributes);
+        }
+    } local;
+#endif
+
+    if (parse_command_line(argc, argv) != 0)
+    {
+        return -1;
+    }
+
+    if (g_test_binaries.empty())
+    {
+        std::cout << "Warning: no test binaries were specified" << std::endl;
+    }
+
+    int totalTestCount = 0, failedTestCount = 0;
+    std::vector<std::string> failedTests;
+    UnitTest::TestReporterStdout testReporter;
+
+    bool breakOnError = false;
+    if (UnitTest::GlobalSettings::Has("breakonerror"))
+    {
+        breakOnError = true;
+    }
+
+    // The list_test_options() function determines if list or listProperties.
+    bool listOption = false;
+    if (UnitTest::GlobalSettings::Has("list"))
+    {
+        listOption = true;
+    }
+    if (UnitTest::GlobalSettings::Has("listproperties"))
+    {
+        listOption = true;
+    }
+#ifdef _WIN32
+    if (UnitTest::GlobalSettings::Has("detectleaks"))
+    {
+        _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+    }
+#endif
+
+    // Start timer.
+    UnitTest::Timer timer;
+    timer.Start();
+
+    test_module_loader module_loader;
+
+    testlist_t testlists = load_all_tests(module_loader);
+
+    if (listOption)
+    {
+        list_test_options(testlists);
+        return 0;
+    }
+
+    // Run test cases
+    UnitTest::TestRunner testRunner(testReporter, breakOnError);
+
+    run_all_tests(testRunner, testlists);
+
+    totalTestCount += testRunner.GetTestResults()->GetTotalTestCount();
+    failedTestCount += testRunner.GetTestResults()->GetFailedTestCount();
+    if (totalTestCount == 0)
+    {
+        std::cout << "No tests were run. Check the command line syntax (try 'TestRunner.exe /help')" << std::endl;
+    }
+    else
+    {
+        if (testRunner.GetTestResults()->GetFailedTestCount() > 0)
+        {
+            ChangeConsoleTextColorToRed();
+            const std::vector<std::string>& failed = testRunner.GetTestResults()->GetFailedTests();
+            std::for_each(failed.begin(), failed.end(), [](const std::string& failedTest) {
+                std::cout << "**** " << failedTest << " FAILED ****" << std::endl << std::endl;
+                std::fflush(stdout);
+            });
+            ChangeConsoleTextColorToGrey();
+        }
+        else
+        {
+            ChangeConsoleTextColorToGreen();
+            std::cout << "All test cases PASSED" << std::endl << std::endl;
+            ChangeConsoleTextColorToGrey();
+        }
+        const std::vector<std::string>& newFailedTests = testRunner.GetTestResults()->GetFailedTests();
+        failedTests.insert(failedTests.end(), newFailedTests.begin(), newFailedTests.end());
+
+        const double elapsedTime = timer.GetTimeInMs();
+        std::cout << "Finished running all " << totalTestCount << " tests." << std::endl
+                  << "Took " << elapsedTime << "ms" << std::endl;
+    }
+
+#if defined(__cplusplus_winrt)
+#elif defined(_WIN32)
+    if (hComBase != nullptr)
+    {
+        typedef void(WINAPI * RoUnInit)();
+        RoUnInit roUnInitFunc = (RoUnInit)GetProcAddress(hComBase, "RoUninitialize");
+        if (roUnInitFunc != nullptr)
+        {
+            roUnInitFunc();
+        }
+        FreeLibrary(hComBase);
+    }
+#endif
+
+    return failedTestCount;
+}
diff --git a/Release/tests/common/TestRunner/test_runner.manifest b/Release/tests/common/TestRunner/test_runner.manifest
new file mode 100644 (file)
index 0000000..5621c55
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">\r
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">\r
+      <application>\r
+        <!--This Id value indicates the application supports Windows Vista functionality -->\r
+          <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>\r
+        <!--This Id value indicates the application supports Windows 7 functionality-->\r
+          <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>\r
+        <!--This Id value indicates the application supports Windows 8 functionality-->\r
+          <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>\r
+        <!--This Id value indicates the application supports Windows 8.1 functionality-->\r
+          <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>\r
+        <!--This Id value indicates the application supports Windows 10 functionality -->\r
+          <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>\r
+      </application>\r
+  </compatibility>\r
+</assembly>\r
diff --git a/Release/tests/common/UnitTestpp/CMakeLists.txt b/Release/tests/common/UnitTestpp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..309c5f2
--- /dev/null
@@ -0,0 +1,59 @@
+set(UT_SOURCES
+  src/AssertException.cpp
+  src/CompositeTestReporter.cpp
+  src/CurrentTest.cpp
+  src/DeferredTestReporter.cpp
+  src/DeferredTestResult.cpp
+  src/GlobalSettings.cpp
+  src/MemoryOutStream.cpp
+  src/ReportAssert.cpp
+  src/Test.cpp
+  src/TestDetails.cpp
+  src/TestList.cpp
+  src/TestReporter.cpp
+  src/TestReporterStdout.cpp
+  src/TestResults.cpp
+  src/TestRunner.cpp
+  src/XmlTestReporter.cpp
+  )
+
+set(TEST_SOURCES
+  src/tests/TestAssertHandler.cpp
+  src/tests/TestCheckMacros.cpp
+  src/tests/TestChecks.cpp
+  src/tests/TestCompositeTestReporter.cpp
+  src/tests/TestCurrentTest.cpp
+  src/tests/TestDeferredTestReporter.cpp
+  src/tests/TestMemoryOutStream.cpp
+  src/tests/TestTest.cpp
+  src/tests/TestTestList.cpp
+  src/tests/TestTestMacros.cpp
+  src/tests/TestTestResults.cpp
+  src/tests/TestTestRunner.cpp
+  src/tests/TestTestSuite.cpp
+  src/tests/TestUnitTestPP.cpp
+  src/tests/TestXmlTestReporter.cpp
+  )
+
+if(UNIX)
+  list(APPEND UT_SOURCES
+    src/Posix/SignalTranslator.cpp
+    src/Posix/TimeHelpers.cpp
+    )
+elseif(WIN32)
+  list(APPEND UT_SOURCES src/Win32/TimeHelpers.cpp)
+
+  add_definitions(-DWIN32 -D_USRDLL -D_CRT_SECURE_NO_DEPRECATE -DUNITTEST_DLL_EXPORT)
+endif()
+
+add_library(unittestpp ${UT_SOURCES})
+target_link_libraries(unittestpp PUBLIC cpprest)
+
+if(UNIX)
+  cpprest_find_boost()
+  target_link_libraries(unittestpp PUBLIC cpprestsdk_boost_internal)
+endif()
+target_link_libraries(unittestpp ${ANDROID_STL_FLAGS})
+
+target_include_directories(unittestpp PRIVATE src)
+target_include_directories(unittestpp PUBLIC .)
diff --git a/Release/tests/common/UnitTestpp/COPYING b/Release/tests/common/UnitTestpp/COPYING
new file mode 100644 (file)
index 0000000..a86c179
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright(c) 2006 Noel Llopis and Charles Nicholson
+
+    Permission is hereby granted,
+    free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
+    to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and / or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions :
+
+    The above copyright notice and this permission notice shall be included in all copies
+    or
+    substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS",
+    WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Release/tests/common/UnitTestpp/ThirdPartyNotices.txt b/Release/tests/common/UnitTestpp/ThirdPartyNotices.txt
new file mode 100644 (file)
index 0000000..0dacfc8
--- /dev/null
@@ -0,0 +1,20 @@
+----------------- ThirdPartyNotices----------------------------------------------
+
+This file is based on or incorporates material from the UnitTest++ r30 open source project.Microsoft is not the original author of this code but has modified it and is licensing the code under the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License, whether by implication, estoppel or otherwise. 
+
+UnitTest++ r30 
+
+Copyright (c) 2006 Noel Llopis and Charles Nicholson
+Portions Copyright (c) Microsoft Corporation
+
+All Rights Reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-------------End of ThirdPartyNotices---------------------------------------
diff --git a/Release/tests/common/UnitTestpp/config.h b/Release/tests/common/UnitTestpp/config.h
new file mode 100644 (file)
index 0000000..1fb3ad6
--- /dev/null
@@ -0,0 +1,83 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_CONFIG_H
+#define UNITTEST_CONFIG_H
+
+// Standard defines documented here: http://predef.sourceforge.net
+
+#if defined(_MSC_VER)
+#pragma warning(disable : 4702) // unreachable code
+#pragma warning(disable : 4722) // destructor never returns, potential memory leak
+
+#if (_MSC_VER == 1200) // VC6
+#pragma warning(disable : 4786)
+#pragma warning(disable : 4290)
+#endif
+
+#ifdef _USRDLL
+#define UNITTEST_WIN32_DLL
+#endif
+#define UNITTEST_WIN32
+#endif
+
+#if defined(unix) || defined(__unix__) || defined(__unix) || defined(linux) || defined(__APPLE__) ||                   \
+    defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
+#define UNITTEST_POSIX
+#endif
+
+#if defined(__MINGW32__)
+#define UNITTEST_MINGW
+#endif
+
+// MemoryOutStream is a custom reimplementation of parts of std::ostringstream.
+// Uncomment this line to have MemoryOutStream implemented in terms of std::ostringstream.
+// This is useful if you are using the CHECK macros on objects that have something like this defined:
+// std::ostringstream& operator<<(std::ostringstream& s, const YourObject& value)
+
+#define UNITTEST_MEMORYOUTSTREAM_IS_STD_OSTRINGSTREAM
+
+// DeferredTestReporter uses the STL to collect test results for subsequent export by reporters like
+// XmlTestReporter.  If you don't want to use this functionality, uncomment this line and no STL
+// headers or code will be compiled into UnitTest++
+
+//#define UNITTEST_NO_DEFERRED_REPORTER
+
+// By default, asserts that you report via UnitTest::ReportAssert() abort the current test and
+// continue to the next one by throwing an exception, which unwinds the stack naturally, destroying
+// all auto variables on its way back down.  If you don't want to (or can't) use exceptions for your
+// platform/compiler, uncomment this line.  All exception code will be removed from UnitTest++,
+// assert recovery will be done via setjmp/longjmp, and NO correct stack unwinding will happen!
+
+//#define UNITTEST_NO_EXCEPTIONS
+
+#include <cpprest/details/basic_types.h>
+#endif
\ No newline at end of file
diff --git a/Release/tests/common/UnitTestpp/src/AssertException.cpp b/Release/tests/common/UnitTestpp/src/AssertException.cpp
new file mode 100644 (file)
index 0000000..6b8372e
--- /dev/null
@@ -0,0 +1,44 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#ifndef UNITTEST_NO_EXCEPTIONS
+
+namespace UnitTest
+{
+AssertException::AssertException() {}
+
+AssertException::~AssertException() throw() {}
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/AssertException.h b/Release/tests/common/UnitTestpp/src/AssertException.h
new file mode 100644 (file)
index 0000000..59d4b83
--- /dev/null
@@ -0,0 +1,54 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_ASSERTEXCEPTION_H
+#define UNITTEST_ASSERTEXCEPTION_H
+
+#include "../config.h"
+#ifndef UNITTEST_NO_EXCEPTIONS
+
+#include "HelperMacros.h"
+#include <exception>
+
+namespace UnitTest
+{
+class AssertException : public std::exception
+{
+public:
+    UNITTEST_LINKAGE AssertException();
+    UNITTEST_LINKAGE virtual ~AssertException() throw();
+};
+
+} // namespace UnitTest
+
+#endif
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/CheckMacros.h b/Release/tests/common/UnitTestpp/src/CheckMacros.h
new file mode 100644 (file)
index 0000000..43a9ccc
--- /dev/null
@@ -0,0 +1,311 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_CHECKMACROS_H
+#define UNITTEST_CHECKMACROS_H
+
+#include "AssertException.h"
+#include "Checks.h"
+#include "CurrentTest.h"
+#include "ExceptionMacros.h"
+#include "HelperMacros.h"
+#include "MemoryOutStream.h"
+#include "ReportAssertImpl.h"
+#include "TestDetails.h"
+#include <stdarg.h>
+
+#ifdef CHECK
+#error UnitTest++ redefines CHECK
+#endif
+
+#ifdef CHECK_EQUAL
+#error UnitTest++ redefines CHECK_EQUAL
+#endif
+
+#ifdef CHECK_CLOSE
+#error UnitTest++ redefines CHECK_CLOSE
+#endif
+
+#ifdef CHECK_ARRAY_EQUAL
+#error UnitTest++ redefines CHECK_ARRAY_EQUAL
+#endif
+
+#ifdef CHECK_ARRAY_CLOSE
+#error UnitTest++ redefines CHECK_ARRAY_CLOSE
+#endif
+
+#ifdef CHECK_ARRAY2D_CLOSE
+#error UnitTest++ redefines CHECK_ARRAY2D_CLOSE
+#endif
+
+#ifdef VERIFY_IS_TRUE
+#error UnitTest++ redefines VERIFY_IS_TRUE
+#endif
+
+#ifdef VERIFY_IS_FALSE
+#error UnitTest++ redefines VERIFY_IS_FALSE
+#endif
+
+#ifdef VERIFY_ARE_EQUAL
+#error UnitTest++ redefines VERIFY_ARE_EQUAL
+#endif
+
+#ifdef VERIFY_ARE_NOT_EQUAL
+#error UnitTest++ redefines VERIFY_ARE_NOT_EQUAL
+#endif
+
+#ifdef VERIFY_THROWS
+#error UnitTest++ redefines VERIFY_THROWS
+#endif
+
+#ifdef VERIFY_IS_NOT_NULL
+#error UnitTest++ redefines VERIFY_IS_NOT_NULL
+#endif
+
+#ifdef VERIFY_IS_NULL
+#error UnitTest++ redefines VERIFY_IS_NULL
+#endif
+
+#ifdef WIN32
+#define VERIFY_IS_TRUE(expression, ...) CHECK_EQUAL(true, expression, __VA_ARGS__)
+#define VERIFY_IS_FALSE(expression, ...) CHECK_EQUAL(false, expression, __VA_ARGS__)
+#define VERIFY_ARE_NOT_EQUAL(expected, actual, ...) CHECK_NOT_EQUAL(expected, actual, __VA_ARGS__)
+#define VERIFY_ARE_EQUAL(expected, actual, ...) CHECK_EQUAL(expected, actual, __VA_ARGS__)
+#else
+#define VERIFY_IS_TRUE(expression, ...) CHECK_EQUAL(true, expression, ##__VA_ARGS__)
+#define VERIFY_IS_FALSE(expression, ...) CHECK_EQUAL(false, expression, ##__VA_ARGS__)
+#define VERIFY_ARE_NOT_EQUAL(expected, actual, ...) CHECK_NOT_EQUAL(expected, actual, ##__VA_ARGS__)
+#define VERIFY_ARE_EQUAL(expected, actual, ...) CHECK_EQUAL(expected, actual, ##__VA_ARGS__)
+#endif
+
+#define VERIFY_NO_THROWS(expression) CHECK_NO_THROW(expression)
+#define VERIFY_THROWS(expression, exception) CHECK_THROW(expression, exception)
+#define VERIFY_IS_NOT_NULL(expression) CHECK_NOT_NULL(expression)
+#define VERIFY_IS_NULL(expression) CHECK_NULL(expression)
+
+#define CHECK(value)                                                                                                   \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    if (!UnitTest::Check(value))                                                                                       \
+        UnitTest::CurrentTest::Results()->OnTestFailure(                                                               \
+            UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), #value);                               \
+    UNITTEST_MULTILINE_MACRO_END
+
+#ifdef WIN32
+
+#define CHECK_EQUAL(expected, actual, ...)                                                                             \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        UnitTest::CheckEqual(*UnitTest::CurrentTest::Results(),                                                        \
+                             #expected,                                                                                \
+                             #actual,                                                                                  \
+                             expected,                                                                                 \
+                             actual,                                                                                   \
+                             UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__),                       \
+                             __VA_ARGS__);                                                                             \
+    UNITTEST_MULTILINE_MACRO_END
+
+#define CHECK_NOT_EQUAL(expected, actual, ...)                                                                         \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        UnitTest::CheckNotEqual(*UnitTest::CurrentTest::Results(),                                                     \
+                                #expected,                                                                             \
+                                #actual,                                                                               \
+                                expected,                                                                              \
+                                actual,                                                                                \
+                                UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__),                    \
+                                __VA_ARGS__);                                                                          \
+    UNITTEST_MULTILINE_MACRO_END
+
+#else
+
+#define CHECK_EQUAL(expected, actual, ...)                                                                             \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        try                                                                                                            \
+        {                                                                                                              \
+            UnitTest::CheckEqual(*UnitTest::CurrentTest::Results(),                                                    \
+                                 #expected,                                                                            \
+                                 #actual,                                                                              \
+                                 expected,                                                                             \
+                                 actual,                                                                               \
+                                 UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__),                   \
+                                 ##__VA_ARGS__);                                                                       \
+        }                                                                                                              \
+        catch (const std::exception& ex)                                                                               \
+        {                                                                                                              \
+            std::cerr << ex.what() << std::endl;                                                                       \
+            UnitTest::CurrentTest::Results()->OnTestFailure(                                                           \
+                UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__),                                    \
+                "Unhandled exception in CHECK_EQUAL(" #expected ", " #actual ") - details: ");                         \
+        }                                                                                                              \
+        UT_CATCH_ALL({                                                                                                 \
+            UnitTest::CurrentTest::Results()->OnTestFailure(                                                           \
+                UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__),                                    \
+                "Unhandled exception in CHECK_EQUAL(" #expected ", " #actual ")");                                     \
+        })                                                                                                             \
+    UNITTEST_MULTILINE_MACRO_END
+
+#define CHECK_NOT_EQUAL(expected, actual, ...)                                                                         \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        try                                                                                                            \
+        {                                                                                                              \
+            UnitTest::CheckNotEqual(*UnitTest::CurrentTest::Results(),                                                 \
+                                    #expected,                                                                         \
+                                    #actual,                                                                           \
+                                    expected,                                                                          \
+                                    actual,                                                                            \
+                                    UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__),                \
+                                    ##__VA_ARGS__);                                                                    \
+        }                                                                                                              \
+        UT_CATCH_ALL({                                                                                                 \
+            UnitTest::CurrentTest::Results()->OnTestFailure(                                                           \
+                UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__),                                    \
+                "Unhandled exception in CHECK_NOT_EQUAL(" #expected ", " #actual ")");                                 \
+        })                                                                                                             \
+    UNITTEST_MULTILINE_MACRO_END
+#endif
+
+#define CHECK_NULL(expression)                                                                                         \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    UnitTest::CheckNull(*UnitTest::CurrentTest::Results(),                                                             \
+                        #expression,                                                                                   \
+                        expression,                                                                                    \
+                        UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__));                           \
+    UNITTEST_MULTILINE_MACRO_END
+
+#define CHECK_NOT_NULL(expression)                                                                                     \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    UnitTest::CheckNotNull(*UnitTest::CurrentTest::Results(),                                                          \
+                           #expression,                                                                                \
+                           expression,                                                                                 \
+                           UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__));                        \
+    UNITTEST_MULTILINE_MACRO_END
+
+#define CHECK_CLOSE(expected, actual, tolerance)                                                                       \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    UnitTest::CheckClose(*UnitTest::CurrentTest::Results(),                                                            \
+                         expected,                                                                                     \
+                         actual,                                                                                       \
+                         tolerance,                                                                                    \
+                         UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__));                          \
+    UNITTEST_MULTILINE_MACRO_END
+
+#define CHECK_ARRAY_EQUAL(expected, actual, count)                                                                     \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    UnitTest::CheckArrayEqual(*UnitTest::CurrentTest::Results(),                                                       \
+                              expected,                                                                                \
+                              actual,                                                                                  \
+                              count,                                                                                   \
+                              UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__));                     \
+    UNITTEST_MULTILINE_MACRO_END
+
+#define CHECK_ARRAY_CLOSE(expected, actual, count, tolerance)                                                          \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    UnitTest::CheckArrayClose(*UnitTest::CurrentTest::Results(),                                                       \
+                              expected,                                                                                \
+                              actual,                                                                                  \
+                              count,                                                                                   \
+                              tolerance,                                                                               \
+                              UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__));                     \
+    UNITTEST_MULTILINE_MACRO_END
+
+#define CHECK_ARRAY2D_CLOSE(expected, actual, rows, columns, tolerance)                                                \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    UnitTest::CheckArray2DClose(*UnitTest::CurrentTest::Results(),                                                     \
+                                expected,                                                                              \
+                                actual,                                                                                \
+                                rows,                                                                                  \
+                                columns,                                                                               \
+                                tolerance,                                                                             \
+                                UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__));                   \
+    UNITTEST_MULTILINE_MACRO_END
+
+// CHECK_THROW and CHECK_ASSERT only exist when UNITTEST_NO_EXCEPTIONS isn't defined (see config.h)
+#ifndef UNITTEST_NO_EXCEPTIONS
+#define CHECK_THROW(expression, ExpectedExceptionType)                                                                 \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    bool caught_ = false;                                                                                              \
+    try                                                                                                                \
+    {                                                                                                                  \
+        try                                                                                                            \
+        {                                                                                                              \
+            expression;                                                                                                \
+        }                                                                                                              \
+        catch (const std::exception& _exc)                                                                             \
+        {                                                                                                              \
+            std::string _msg(_exc.what());                                                                             \
+            VERIFY_IS_TRUE(_msg.size() > 0);                                                                           \
+            throw;                                                                                                     \
+        }                                                                                                              \
+    }                                                                                                                  \
+    catch (ExpectedExceptionType const&)                                                                               \
+    {                                                                                                                  \
+        caught_ = true;                                                                                                \
+    }                                                                                                                  \
+    catch (...)                                                                                                        \
+    {                                                                                                                  \
+    }                                                                                                                  \
+    if (!caught_)                                                                                                      \
+        UnitTest::CurrentTest::Results()->OnTestFailure(                                                               \
+            UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__),                                        \
+            "Expected exception: \"" #ExpectedExceptionType "\" not thrown");                                          \
+    UNITTEST_MULTILINE_MACRO_END
+
+#define CHECK_NO_THROW(expression)                                                                                     \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    try                                                                                                                \
+    {                                                                                                                  \
+        expression;                                                                                                    \
+    }                                                                                                                  \
+    catch (const std::exception& _exc)                                                                                 \
+    {                                                                                                                  \
+        std::string _msg("(" #expression ") threw exception: ");                                                       \
+        _msg.append(_exc.what());                                                                                      \
+        UnitTest::CurrentTest::Results()->OnTestFailure(                                                               \
+            UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), _msg.c_str());                         \
+    }                                                                                                                  \
+    catch (...)                                                                                                        \
+    {                                                                                                                  \
+        std::string _msg("(" #expression ") threw exception: <...>");                                                  \
+        UnitTest::CurrentTest::Results()->OnTestFailure(                                                               \
+            UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), _msg.c_str());                         \
+    }                                                                                                                  \
+    UNITTEST_MULTILINE_MACRO_END
+
+#define CHECK_ASSERT(expression)                                                                                       \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    UnitTest::Detail::ExpectAssert(true);                                                                              \
+    CHECK_THROW(expression, UnitTest::AssertException);                                                                \
+    UnitTest::Detail::ExpectAssert(false);                                                                             \
+    UNITTEST_MULTILINE_MACRO_END
+#endif
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/Checks.h b/Release/tests/common/UnitTestpp/src/Checks.h
new file mode 100644 (file)
index 0000000..f449406
--- /dev/null
@@ -0,0 +1,383 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_CHECKS_H
+#define UNITTEST_CHECKS_H
+
+#include "MemoryOutStream.h"
+#include "TestResults.h"
+#include "config.h"
+#include <cstring>
+#include <memory>
+#include <string>
+
+#ifndef _WIN32
+#include <boost/locale/encoding_utf.hpp>
+#endif
+
+namespace UnitTest
+{
+namespace details
+{
+inline std::string utf16_to_utf8(const std::basic_string<utf16char>& w)
+{
+#ifdef _WIN32
+    std::string result;
+    size_t size;
+    wcstombs_s(&size, nullptr, 0, (const wchar_t*)w.c_str(), w.size());
+    result.resize(size);
+    // integr0808: added cast
+    wcstombs_s(&size, &result[0], size, (const wchar_t*)w.c_str(), w.size());
+    return result;
+#else
+    return boost::locale::conv::utf_to_utf<char, utf16char>(w, boost::locale::conv::stop);
+#endif
+}
+
+typedef char yes;
+typedef char (&no)[2];
+
+struct anyx
+{
+    template<typename T>
+    anyx(const T&);
+};
+no operator<<(const anyx&, const anyx&);
+
+template<typename T>
+yes check(T const&);
+no check(no);
+
+template<typename StreamType, typename T1, typename T2>
+struct support_stream_write
+{
+    static StreamType& stream;
+    static T1& x;
+    static T2& y;
+    static const bool value =
+        (sizeof(check(stream << x)) == sizeof(yes)) && (sizeof(check(stream << y)) == sizeof(yes));
+};
+
+template<typename T1, typename T2>
+inline std::string BuildFailureStringWithStream(const char* expectedStr,
+                                                const char* actualStr,
+                                                const T1& expected,
+                                                const T2& actual)
+{
+    UnitTest::MemoryOutStream stream;
+    stream << " where " << expectedStr << "=" << expected << " and " << actualStr << "=" << actual;
+    return stream.GetText();
+}
+
+template<typename T1, typename T2, bool UseStreams>
+struct BuildFailureStringImpl
+{
+    std::string BuildString(const char*, const char*, const T1&, const T2&)
+    {
+        // Don't do anything since operator<< isn't supported.
+        return std::string{};
+    }
+};
+
+template<typename T1, typename T2>
+struct BuildFailureStringImpl<T1, T2, true>
+{
+    std::string BuildString(const char* expectedStr, const char* actualStr, const T1& expected, const T2& actual)
+    {
+        return BuildFailureStringWithStream(expectedStr, actualStr, expected, actual);
+    }
+};
+
+template<typename T1, typename T2>
+inline std::string BuildFailureString(const char* expectedStr,
+                                      const char* actualStr,
+                                      const T1& expected,
+                                      const T2& actual)
+{
+    return BuildFailureStringImpl<T1, T2, support_stream_write<UnitTest::MemoryOutStream, T1, T2>::value>().BuildString(
+        expectedStr, actualStr, expected, actual);
+}
+inline std::string BuildFailureString(const char* expectedStr,
+                                      const char* actualStr,
+                                      const std::basic_string<utf16char>& expected,
+                                      const std::basic_string<utf16char>& actual)
+{
+    return BuildFailureStringWithStream(expectedStr, actualStr, utf16_to_utf8(expected), utf16_to_utf8(actual));
+}
+inline std::string BuildFailureString(const char* expectedStr,
+                                      const char* actualStr,
+                                      const utf16char* expected,
+                                      const utf16char* actual)
+{
+    return BuildFailureStringWithStream(expectedStr, actualStr, utf16_to_utf8(expected), utf16_to_utf8(actual));
+}
+inline std::string BuildFailureString(const char* expectedStr,
+                                      const char* actualStr,
+                                      const std::basic_string<utf16char>& expected,
+                                      const utf16char* actual)
+{
+    return BuildFailureStringWithStream(expectedStr, actualStr, utf16_to_utf8(expected), utf16_to_utf8(actual));
+}
+inline std::string BuildFailureString(const char* expectedStr,
+                                      const char* actualStr,
+                                      const utf16char* expected,
+                                      const std::basic_string<utf16char>& actual)
+{
+    return BuildFailureStringWithStream(expectedStr, actualStr, utf16_to_utf8(expected), utf16_to_utf8(actual));
+}
+} // namespace details
+
+template<typename Value>
+bool Check(Value const value)
+{
+    return !!value; // doing double negative to avoid silly VS warnings
+}
+
+#ifdef _WIN32
+#pragma warning(push)
+#pragma warning(disable : 4389)
+#endif
+template<typename Expected, typename Actual>
+bool CheckEqualImpl(const Expected& expected, const Actual& actual)
+{
+    return !(expected == actual);
+}
+#ifdef _WIN32
+#pragma warning(pop)
+#endif
+
+inline bool CheckEqualImpl(const char* expected, const char* actual) { return !(std::strcmp(expected, actual) == 0); }
+inline bool CheckEqualImpl(char* expected, const char* actual) { return !(std::strcmp(expected, actual) == 0); }
+inline bool CheckEqualImpl(const char* expected, char* actual) { return !(std::strcmp(expected, actual) == 0); }
+inline bool CheckEqualImpl(char* expected, char* actual) { return !(std::strcmp(expected, actual) == 0); }
+inline bool CheckEqualImpl(const wchar_t* expected, const wchar_t* actual) { return !(wcscmp(expected, actual) == 0); }
+inline bool CheckEqualImpl(wchar_t* expected, const wchar_t* actual) { return !(wcscmp(expected, actual) == 0); }
+inline bool CheckEqualImpl(const wchar_t* expected, wchar_t* actual) { return !(wcscmp(expected, actual) == 0); }
+inline bool CheckEqualImpl(wchar_t* expected, wchar_t* actual) { return !(wcscmp(expected, actual) == 0); }
+
+template<typename Expected, typename Actual>
+void CheckEqual(TestResults& results,
+                const char* expectedStr,
+                const char* actualStr,
+                const Expected& expected,
+                const Actual& actual,
+                TestDetails const& details,
+                const char* msg = nullptr)
+{
+    if (CheckEqualImpl(expected, actual))
+    {
+        UnitTest::MemoryOutStream stream;
+        stream << "CHECK_EQUAL(" << expectedStr << ", " << actualStr << ")";
+        stream << details::BuildFailureString(expectedStr, actualStr, expected, actual) << std::endl;
+        if (msg != nullptr)
+        {
+            stream << msg;
+        }
+        results.OnTestFailure(details, stream.GetText());
+    }
+}
+
+template<typename Expected, typename Actual>
+void CheckNotEqual(TestResults& results,
+                   const char* expectedStr,
+                   const char* actualStr,
+                   Expected const& expected,
+                   Actual const& actual,
+                   TestDetails const& details,
+                   const char* msg = nullptr)
+{
+    if (!CheckEqualImpl(expected, actual))
+    {
+        UnitTest::MemoryOutStream stream;
+        stream << "CHECK_NOT_EQUAL(" << expectedStr << ", " << actualStr << ")";
+        stream << details::BuildFailureString(expectedStr, actualStr, expected, actual) << std::endl;
+        if (msg != nullptr)
+        {
+            stream << msg;
+        }
+        results.OnTestFailure(details, stream.GetText());
+    }
+}
+
+template<typename Actual>
+void CheckNull(TestResults& results, const char* actualStr, Actual const& actual, TestDetails const& details)
+{
+    if (actual)
+    {
+        UnitTest::MemoryOutStream stream;
+        stream << "CHECK_NULL(" << actualStr << ")";
+        results.OnTestFailure(details, stream.GetText());
+    }
+}
+
+template<typename Actual>
+void CheckNotNull(TestResults& results, const char* actualStr, Actual const& actual, TestDetails const& details)
+{
+    if (!actual)
+    {
+        UnitTest::MemoryOutStream stream;
+        stream << "CHECK_NOT_NULL(" << actualStr << ")";
+        results.OnTestFailure(details, stream.GetText());
+    }
+}
+
+template<typename Expected, typename Actual, typename Tolerance>
+bool AreClose(Expected const& expected, Actual const& actual, Tolerance const& tolerance)
+{
+    return (actual >= (expected - tolerance)) && (actual <= (expected + tolerance));
+}
+
+template<typename Expected, typename Actual, typename Tolerance>
+void CheckClose(TestResults& results,
+                Expected const& expected,
+                Actual const& actual,
+                Tolerance const& tolerance,
+                TestDetails const& details)
+{
+    if (!AreClose(expected, actual, tolerance))
+    {
+        UnitTest::MemoryOutStream stream;
+        stream << "Expected " << expected << " +/- " << tolerance << " but was " << actual;
+
+        results.OnTestFailure(details, stream.GetText());
+    }
+}
+
+template<typename Expected, typename Actual>
+void CheckArrayEqual(
+    TestResults& results, Expected const& expected, Actual const& actual, int const count, TestDetails const& details)
+{
+    bool equal = true;
+    for (int i = 0; i < count; ++i)
+        equal &= (expected[i] == actual[i]);
+
+    if (!equal)
+    {
+        UnitTest::MemoryOutStream stream;
+
+        stream << "Expected [ ";
+
+        for (int expectedIndex = 0; expectedIndex < count; ++expectedIndex)
+            stream << expected[expectedIndex] << " ";
+
+        stream << "] but was [ ";
+
+        for (int actualIndex = 0; actualIndex < count; ++actualIndex)
+            stream << actual[actualIndex] << " ";
+
+        stream << "]";
+
+        results.OnTestFailure(details, stream.GetText());
+    }
+}
+
+template<typename Expected, typename Actual, typename Tolerance>
+bool ArrayAreClose(Expected const& expected, Actual const& actual, int const count, Tolerance const& tolerance)
+{
+    bool equal = true;
+    for (int i = 0; i < count; ++i)
+        equal &= AreClose(expected[i], actual[i], tolerance);
+    return equal;
+}
+
+template<typename Expected, typename Actual, typename Tolerance>
+void CheckArrayClose(TestResults& results,
+                     Expected const& expected,
+                     Actual const& actual,
+                     int const count,
+                     Tolerance const& tolerance,
+                     TestDetails const& details)
+{
+    bool equal = ArrayAreClose(expected, actual, count, tolerance);
+
+    if (!equal)
+    {
+        UnitTest::MemoryOutStream stream;
+
+        stream << "Expected [ ";
+        for (int expectedIndex = 0; expectedIndex < count; ++expectedIndex)
+            stream << expected[expectedIndex] << " ";
+        stream << "] +/- " << tolerance << " but was [ ";
+
+        for (int actualIndex = 0; actualIndex < count; ++actualIndex)
+            stream << actual[actualIndex] << " ";
+        stream << "]";
+
+        results.OnTestFailure(details, stream.GetText());
+    }
+}
+
+template<typename Expected, typename Actual, typename Tolerance>
+void CheckArray2DClose(TestResults& results,
+                       Expected const& expected,
+                       Actual const& actual,
+                       int const rows,
+                       int const columns,
+                       Tolerance const& tolerance,
+                       TestDetails const& details)
+{
+    bool equal = true;
+    for (int i = 0; i < rows; ++i)
+        equal &= ArrayAreClose(expected[i], actual[i], columns, tolerance);
+
+    if (!equal)
+    {
+        UnitTest::MemoryOutStream stream;
+
+        stream << "Expected [ ";
+
+        for (int expectedRow = 0; expectedRow < rows; ++expectedRow)
+        {
+            stream << "[ ";
+            for (int expectedColumn = 0; expectedColumn < columns; ++expectedColumn)
+                stream << expected[expectedRow][expectedColumn] << " ";
+            stream << "] ";
+        }
+
+        stream << "] +/- " << tolerance << " but was [ ";
+
+        for (int actualRow = 0; actualRow < rows; ++actualRow)
+        {
+            stream << "[ ";
+            for (int actualColumn = 0; actualColumn < columns; ++actualColumn)
+                stream << actual[actualRow][actualColumn] << " ";
+            stream << "] ";
+        }
+
+        stream << "]";
+
+        results.OnTestFailure(details, stream.GetText());
+    }
+}
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/CompositeTestReporter.cpp b/Release/tests/common/UnitTestpp/src/CompositeTestReporter.cpp
new file mode 100644 (file)
index 0000000..c5ed999
--- /dev/null
@@ -0,0 +1,101 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include "CompositeTestReporter.h"
+
+namespace UnitTest
+{
+CompositeTestReporter::CompositeTestReporter() : m_reporterCount(0) {}
+
+int CompositeTestReporter::GetReporterCount() const { return m_reporterCount; }
+
+bool CompositeTestReporter::AddReporter(TestReporter* reporter)
+{
+    if (m_reporterCount == kMaxReporters) return false;
+
+// Safe to ignore we check the size to make sure no buffer overruns before.
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 6386)
+#endif
+    m_reporters[m_reporterCount++] = reporter;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+    return true;
+}
+
+bool CompositeTestReporter::RemoveReporter(TestReporter* reporter)
+{
+    for (int index = 0; index < m_reporterCount; ++index)
+    {
+        if (m_reporters[index] == reporter)
+        {
+            m_reporters[index] = m_reporters[m_reporterCount - 1];
+            --m_reporterCount;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void CompositeTestReporter::ReportFailure(TestDetails const& details, char const* failure)
+{
+    for (int index = 0; index < m_reporterCount; ++index)
+        m_reporters[index]->ReportFailure(details, failure);
+}
+
+void CompositeTestReporter::ReportTestStart(TestDetails const& test)
+{
+    for (int index = 0; index < m_reporterCount; ++index)
+        m_reporters[index]->ReportTestStart(test);
+}
+
+void CompositeTestReporter::ReportTestFinish(TestDetails const& test, bool passed, float secondsElapsed)
+{
+    for (int index = 0; index < m_reporterCount; ++index)
+        m_reporters[index]->ReportTestFinish(test, passed, secondsElapsed);
+}
+
+void CompositeTestReporter::ReportSummary(int totalTestCount,
+                                          int failedTestCount,
+                                          int failureCount,
+                                          float secondsElapsed)
+{
+    for (int index = 0; index < m_reporterCount; ++index)
+        m_reporters[index]->ReportSummary(totalTestCount, failedTestCount, failureCount, secondsElapsed);
+}
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/CompositeTestReporter.h b/Release/tests/common/UnitTestpp/src/CompositeTestReporter.h
new file mode 100644 (file)
index 0000000..08dc2e5
--- /dev/null
@@ -0,0 +1,71 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_COMPOSITETESTREPORTER_H
+#define UNITTEST_COMPOSITETESTREPORTER_H
+
+#include "TestReporter.h"
+
+namespace UnitTest
+{
+class CompositeTestReporter : public TestReporter
+{
+public:
+    UNITTEST_LINKAGE CompositeTestReporter();
+
+    UNITTEST_LINKAGE int GetReporterCount() const;
+    UNITTEST_LINKAGE bool AddReporter(TestReporter* reporter);
+    UNITTEST_LINKAGE bool RemoveReporter(TestReporter* reporter);
+
+    UNITTEST_LINKAGE virtual void ReportTestStart(TestDetails const& test);
+    UNITTEST_LINKAGE virtual void ReportFailure(TestDetails const& test, char const* failure);
+    UNITTEST_LINKAGE virtual void ReportTestFinish(TestDetails const& test, bool passed, float secondsElapsed);
+    UNITTEST_LINKAGE virtual void ReportSummary(int totalTestCount,
+                                                int failedTestCount,
+                                                int failureCount,
+                                                float secondsElapsed);
+
+private:
+    enum
+    {
+        kMaxReporters = 16
+    };
+    TestReporter* m_reporters[kMaxReporters];
+    int m_reporterCount;
+
+    // revoked
+    CompositeTestReporter(const CompositeTestReporter&);
+    CompositeTestReporter& operator=(const CompositeTestReporter&);
+};
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/CurrentTest.cpp b/Release/tests/common/UnitTestpp/src/CurrentTest.cpp
new file mode 100644 (file)
index 0000000..f7e9f2f
--- /dev/null
@@ -0,0 +1,55 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include <atomic>
+
+namespace
+{
+std::atomic<UnitTest::TestResults*> testResults;
+std::atomic<UnitTest::TestDetails*> testDetails; // non-const pointer to avoid VS2013 STL bug
+} // namespace
+
+namespace UnitTest
+{
+UNITTEST_LINKAGE TestResults* CurrentTest::Results() { return testResults; }
+
+UNITTEST_LINKAGE void CurrentTest::SetResults(TestResults* r) { testResults.store(r); }
+
+UNITTEST_LINKAGE const TestDetails* CurrentTest::Details() { return testDetails; }
+
+UNITTEST_LINKAGE void CurrentTest::SetDetails(const UnitTest::TestDetails* d)
+{
+    testDetails.store(const_cast<UnitTest::TestDetails*>(d));
+}
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/CurrentTest.h b/Release/tests/common/UnitTestpp/src/CurrentTest.h
new file mode 100644 (file)
index 0000000..bc92c95
--- /dev/null
@@ -0,0 +1,52 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_CURRENTTESTRESULTS_H
+#define UNITTEST_CURRENTTESTRESULTS_H
+
+#include "HelperMacros.h"
+
+namespace UnitTest
+{
+class TestResults;
+class TestDetails;
+
+namespace CurrentTest
+{
+UNITTEST_LINKAGE TestResults* __cdecl Results();
+UNITTEST_LINKAGE void __cdecl SetResults(TestResults*);
+UNITTEST_LINKAGE const TestDetails* __cdecl Details();
+UNITTEST_LINKAGE void __cdecl SetDetails(const TestDetails*);
+} // namespace CurrentTest
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/DeferredTestReporter.cpp b/Release/tests/common/UnitTestpp/src/DeferredTestReporter.cpp
new file mode 100644 (file)
index 0000000..5a958c4
--- /dev/null
@@ -0,0 +1,59 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#ifndef UNITTEST_NO_DEFERRED_REPORTER
+
+using namespace UnitTest;
+
+void DeferredTestReporter::ReportTestStart(TestDetails const& details)
+{
+    m_results.push_back(DeferredTestResult(details.suiteName, details.testName));
+}
+
+void DeferredTestReporter::ReportFailure(TestDetails const& details, char const* failure)
+{
+    DeferredTestResult& r = m_results.back();
+    r.failed = true;
+    r.failures.push_back(DeferredTestFailure(details.lineNumber, failure));
+    r.failureFile = details.filename;
+}
+
+void DeferredTestReporter::ReportTestFinish(TestDetails const&, bool, float secondsElapsed)
+{
+    DeferredTestResult& r = m_results.back();
+    r.timeElapsed = secondsElapsed;
+}
+
+DeferredTestReporter::DeferredTestResultList& DeferredTestReporter::GetResults() { return m_results; }
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/DeferredTestReporter.h b/Release/tests/common/UnitTestpp/src/DeferredTestReporter.h
new file mode 100644 (file)
index 0000000..b64fb72
--- /dev/null
@@ -0,0 +1,62 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_DEFERREDTESTREPORTER_H
+#define UNITTEST_DEFERREDTESTREPORTER_H
+
+#include "../config.h"
+
+#ifndef UNITTEST_NO_DEFERRED_REPORTER
+
+#include "DeferredTestResult.h"
+#include "TestReporter.h"
+#include <vector>
+
+namespace UnitTest
+{
+class DeferredTestReporter : public TestReporter
+{
+public:
+    UNITTEST_LINKAGE virtual void ReportTestStart(TestDetails const& details);
+    UNITTEST_LINKAGE virtual void ReportFailure(TestDetails const& details, char const* failure);
+    UNITTEST_LINKAGE virtual void ReportTestFinish(TestDetails const& details, bool passed, float secondsElapsed);
+
+    typedef std::vector<DeferredTestResult> DeferredTestResultList;
+    UNITTEST_LINKAGE DeferredTestResultList& GetResults();
+
+private:
+    DeferredTestResultList m_results;
+};
+
+} // namespace UnitTest
+
+#endif
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/DeferredTestResult.cpp b/Release/tests/common/UnitTestpp/src/DeferredTestResult.cpp
new file mode 100644 (file)
index 0000000..703031f
--- /dev/null
@@ -0,0 +1,70 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#ifndef UNITTEST_NO_DEFERRED_REPORTER
+
+#include "DeferredTestResult.h"
+
+namespace UnitTest
+{
+DeferredTestFailure::DeferredTestFailure() : lineNumber(-1) { failureStr[0] = '\0'; }
+
+DeferredTestFailure::DeferredTestFailure(int lineNumber_, const char* failureStr_) : lineNumber(lineNumber_)
+{
+// Ignoring warning about possible overrun because we aren't going to entirely
+// change how unittestpp deals with strings.
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 6204)
+#endif
+    std::strcpy(failureStr, failureStr_);
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+}
+
+DeferredTestResult::DeferredTestResult()
+    : suiteName(), testName(), 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)
+{
+}
+
+DeferredTestResult::~DeferredTestResult() {}
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/DeferredTestResult.h b/Release/tests/common/UnitTestpp/src/DeferredTestResult.h
new file mode 100644 (file)
index 0000000..711b654
--- /dev/null
@@ -0,0 +1,79 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_DEFERREDTESTRESULT_H
+#define UNITTEST_DEFERREDTESTRESULT_H
+
+#include "../config.h"
+#ifndef UNITTEST_NO_DEFERRED_REPORTER
+
+#include "HelperMacros.h"
+#include <string>
+#include <vector>
+
+namespace UnitTest
+{
+class DeferredTestFailure
+{
+public:
+    UNITTEST_LINKAGE DeferredTestFailure();
+    UNITTEST_LINKAGE DeferredTestFailure(int lineNumber_, const char* failureStr_);
+
+    int lineNumber;
+    char failureStr[1024];
+};
+
+} // namespace UnitTest
+
+namespace UnitTest
+{
+class DeferredTestResult
+{
+public:
+    UNITTEST_LINKAGE DeferredTestResult();
+    UNITTEST_LINKAGE DeferredTestResult(char const* suite, char const* test);
+    UNITTEST_LINKAGE ~DeferredTestResult();
+
+    std::string suiteName;
+    std::string testName;
+    std::string failureFile;
+
+    typedef std::vector<DeferredTestFailure> FailureVec;
+    FailureVec failures;
+
+    float timeElapsed;
+    bool failed;
+};
+
+} // namespace UnitTest
+
+#endif
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/ExceptionMacros.h b/Release/tests/common/UnitTestpp/src/ExceptionMacros.h
new file mode 100644 (file)
index 0000000..8c57e43
--- /dev/null
@@ -0,0 +1,51 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_EXCEPTIONMACROS_H
+#define UNITTEST_EXCEPTIONMACROS_H
+
+#include "../config.h"
+
+#ifndef UNITTEST_NO_EXCEPTIONS
+#define UT_TRY(x)                                                                                                      \
+    try                                                                                                                \
+    x
+#define UT_THROW(x) throw x
+#define UT_CATCH(ExceptionType, ExceptionName, CatchBody) catch (ExceptionType & ExceptionName) CatchBody
+#define UT_CATCH_ALL(CatchBody) catch (...) CatchBody
+#else
+#define UT_TRY(x) x
+#define UT_THROW(x)
+#define UT_CATCH(ExceptionType, ExceptionName, CatchBody)
+#define UT_CATCH_ALL(CatchBody)
+#endif
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/ExecuteTest.h b/Release/tests/common/UnitTestpp/src/ExecuteTest.h
new file mode 100644 (file)
index 0000000..274d550
--- /dev/null
@@ -0,0 +1,89 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_EXECUTE_TEST_H
+#define UNITTEST_EXECUTE_TEST_H
+
+#include "../config.h"
+#include "AssertException.h"
+#include "CurrentTest.h"
+#include "ExceptionMacros.h"
+#include "MemoryOutStream.h"
+#include "TestDetails.h"
+#include "TestResults.h"
+
+#ifdef UNITTEST_NO_EXCEPTIONS
+#include "ReportAssertImpl.h"
+#endif
+
+#ifdef UNITTEST_POSIX
+#include "Posix/SignalTranslator.h"
+#endif
+
+#include <iostream>
+
+namespace UnitTest
+{
+template<typename T>
+void ExecuteTest(T& testObject, TestDetails const& details, bool isMockTest)
+{
+    if (isMockTest == false)
+    {
+        CurrentTest::SetDetails(&details);
+    }
+
+#ifdef UNITTEST_NO_EXCEPTIONS
+    if (UNITTEST_SET_ASSERT_JUMP_TARGET() == 0)
+    {
+#endif
+#ifndef UNITTEST_POSIX
+        UT_TRY({ testObject.RunImpl(); })
+#else
+    UT_TRY({
+        UNITTEST_THROW_SIGNALS_POSIX_ONLY
+        testObject.RunImpl();
+    })
+#endif
+        UT_CATCH(AssertException, e, { (void)e; })
+        UT_CATCH(std::exception, e, {
+            MemoryOutStream stream;
+            stream << "Unhandled exception: " << e.what();
+            CurrentTest::Results()->OnTestFailure(details, stream.GetText());
+        })
+        UT_CATCH_ALL({ CurrentTest::Results()->OnTestFailure(details, "Unhandled exception: test crashed"); })
+#ifdef UNITTEST_NO_EXCEPTIONS
+    }
+#endif
+}
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/GlobalSettings.cpp b/Release/tests/common/UnitTestpp/src/GlobalSettings.cpp
new file mode 100644 (file)
index 0000000..2a9f625
--- /dev/null
@@ -0,0 +1,64 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include "GlobalSettings.h"
+
+#include <algorithm>
+#include <map>
+
+namespace UnitTest
+{
+static std::string to_lower(const std::string& str)
+{
+    std::string retVal;
+    retVal.resize(str.size());
+    std::transform(str.begin(), str.end(), retVal.begin(), ::tolower);
+    return retVal;
+}
+
+std::map<std::string, std::string> g_settings;
+
+void GlobalSettings::Add(const std::string& key, const std::string& value) { g_settings[to_lower(key)] = value; }
+
+bool GlobalSettings::Has(const std::string& key) { return g_settings.find(to_lower(key)) != g_settings.end(); }
+
+const std::string& GlobalSettings::Get(const std::string& key)
+{
+    if (!Has(key))
+    {
+        throw std::invalid_argument("Error: property is not found");
+    }
+    return g_settings.find(to_lower(key))->second;
+}
+
+} // namespace UnitTest
\ No newline at end of file
diff --git a/Release/tests/common/UnitTestpp/src/GlobalSettings.h b/Release/tests/common/UnitTestpp/src/GlobalSettings.h
new file mode 100644 (file)
index 0000000..e1de218
--- /dev/null
@@ -0,0 +1,59 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_GLOBAL_PROPERTIES_H
+#define UNITTEST_GLOBAL_PROPERTIES_H
+
+#include "HelperMacros.h"
+#include <string>
+
+namespace UnitTest
+{
+// Simple key value pairs for global properties.
+// Any test case which specifies a 'Requires' TestProperty will only execute if
+// the required property is satisfied as a key in the GlobalSettings.
+class GlobalSettings
+{
+public:
+    UNITTEST_LINKAGE static void __cdecl Add(const std::string& key, const std::string& value);
+
+    UNITTEST_LINKAGE static bool __cdecl Has(const std::string& key);
+
+    UNITTEST_LINKAGE static const std::string& __cdecl Get(const std::string& key);
+
+private:
+    GlobalSettings();
+    GlobalSettings(const GlobalSettings&);
+    GlobalSettings& operator=(const GlobalSettings&);
+};
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/HelperMacros.h b/Release/tests/common/UnitTestpp/src/HelperMacros.h
new file mode 100644 (file)
index 0000000..6cc8559
--- /dev/null
@@ -0,0 +1,85 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_HELPERMACROS_H
+#define UNITTEST_HELPERMACROS_H
+
+#include "../config.h"
+
+#define UNITTEST_MULTILINE_MACRO_BEGIN                                                                                 \
+    do                                                                                                                 \
+    {
+#ifdef UNITTEST_WIN32
+#define UNITTEST_MULTILINE_MACRO_END                                                                                   \
+    }                                                                                                                  \
+    __pragma(warning(push)) __pragma(warning(disable : 4127)) while (0) __pragma(warning(pop))
+#else
+#define UNITTEST_MULTILINE_MACRO_END                                                                                   \
+    }                                                                                                                  \
+    while (0)
+#endif
+
+#ifdef UNITTEST_WIN32_DLL
+#define UNITTEST_IMPORT __declspec(dllimport)
+#define UNITTEST_EXPORT __declspec(dllexport)
+
+#ifdef UNITTEST_DLL_EXPORT
+#define UNITTEST_LINKAGE UNITTEST_EXPORT
+#define UNITTEST_IMPEXP_TEMPLATE
+#else
+#define UNITTEST_LINKAGE UNITTEST_IMPORT
+#define UNITTEST_IMPEXP_TEMPLATE extern
+#endif
+
+#define UNITTEST_STDVECTOR_LINKAGE(T)                                                                                  \
+    __pragma(warning(push)) __pragma(warning(disable : 4231))                                                          \
+        UNITTEST_IMPEXP_TEMPLATE template class UNITTEST_LINKAGE std::allocator<T>;                                    \
+    UNITTEST_IMPEXP_TEMPLATE template class UNITTEST_LINKAGE std::vector<T>;                                           \
+    __pragma(warning(pop))
+#else
+#define UNITTEST_IMPORT
+#define UNITTEST_EXPORT
+#define UNITTEST_LINKAGE
+#define UNITTEST_IMPEXP_TEMPLATE
+#define UNITTEST_STDVECTOR_LINKAGE(T)
+#endif
+
+#ifdef UNITTEST_WIN32
+#define UNITTEST_JMPBUF jmp_buf
+#define UNITTEST_SETJMP setjmp
+#define UNITTEST_LONGJMP longjmp
+#elif defined UNITTEST_POSIX
+#define UNITTEST_JMPBUF std::jmp_buf
+#define UNITTEST_SETJMP setjmp
+#define UNITTEST_LONGJMP std::longjmp
+#endif
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/MemoryOutStream.cpp b/Release/tests/common/UnitTestpp/src/MemoryOutStream.cpp
new file mode 100644 (file)
index 0000000..a0da2e4
--- /dev/null
@@ -0,0 +1,186 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#ifdef UNITTEST_MEMORYOUTSTREAM_IS_STD_OSTRINGSTREAM
+
+namespace UnitTest
+{
+MemoryOutStream::MemoryOutStream() {}
+
+MemoryOutStream::~MemoryOutStream() {}
+
+char const* MemoryOutStream::GetText() const
+{
+    m_text = this->str();
+    return m_text.c_str();
+}
+
+void MemoryOutStream::Clear()
+{
+    this->str(std::string());
+    m_text = this->str();
+}
+
+} // namespace UnitTest
+
+#else
+
+namespace UnitTest
+{
+namespace
+{
+template<typename ValueType>
+void FormatToStream(MemoryOutStream& stream, char const* format, ValueType const& value)
+{
+    using namespace std;
+
+    char txt[32];
+    sprintf(txt, format, value);
+    stream << txt;
+}
+
+int RoundUpToMultipleOfPow2Number(int n, int pow2Number) { return (n + (pow2Number - 1)) & ~(pow2Number - 1); }
+
+} // namespace
+
+MemoryOutStream::MemoryOutStream(int const size) : m_capacity(0), m_buffer(0) { GrowBuffer(size); }
+
+MemoryOutStream::~MemoryOutStream() { delete[] m_buffer; }
+
+void MemoryOutStream::Clear() { m_buffer[0] = '\0'; }
+
+char const* MemoryOutStream::GetText() const { return m_buffer; }
+
+MemoryOutStream& MemoryOutStream::operator<<(char const* txt)
+{
+    using namespace std;
+
+    int const bytesLeft = m_capacity - (int)strlen(m_buffer);
+    int const bytesRequired = (int)strlen(txt) + 1;
+
+    if (bytesRequired > bytesLeft)
+    {
+        int const requiredCapacity = bytesRequired + m_capacity - bytesLeft;
+        GrowBuffer(requiredCapacity);
+    }
+
+    strcat(m_buffer, txt);
+    return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator<<(int const n)
+{
+    FormatToStream(*this, "%i", n);
+    return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator<<(long const n)
+{
+    FormatToStream(*this, "%li", n);
+    return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator<<(unsigned long const n)
+{
+    FormatToStream(*this, "%lu", n);
+    return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator<<(long long const n)
+{
+#ifdef UNITTEST_WIN32
+    FormatToStream(*this, "%I64d", n);
+#else
+    FormatToStream(*this, "%lld", n);
+#endif
+
+    return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator<<(unsigned long long const n)
+{
+#ifdef UNITTEST_WIN32
+    FormatToStream(*this, "%I64u", n);
+#else
+    FormatToStream(*this, "%llu", n);
+#endif
+
+    return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator<<(float const f)
+{
+    FormatToStream(*this, "%ff", f);
+    return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator<<(void const* p)
+{
+    FormatToStream(*this, "%p", p);
+    return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator<<(unsigned int const s)
+{
+    FormatToStream(*this, "%u", s);
+    return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator<<(double const d)
+{
+    FormatToStream(*this, "%f", d);
+    return *this;
+}
+
+int MemoryOutStream::GetCapacity() const { return m_capacity; }
+
+void MemoryOutStream::GrowBuffer(int const desiredCapacity)
+{
+    int const newCapacity = RoundUpToMultipleOfPow2Number(desiredCapacity, GROW_CHUNK_SIZE);
+
+    using namespace std;
+
+    char* buffer = new char[newCapacity];
+    if (m_buffer)
+        strcpy(buffer, m_buffer);
+    else
+        *buffer = '\0';
+
+    delete[] m_buffer;
+    m_buffer = buffer;
+    m_capacity = newCapacity;
+}
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/MemoryOutStream.h b/Release/tests/common/UnitTestpp/src/MemoryOutStream.h
new file mode 100644 (file)
index 0000000..fa11035
--- /dev/null
@@ -0,0 +1,105 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_MEMORYOUTSTREAM_H
+#define UNITTEST_MEMORYOUTSTREAM_H
+
+#include "../config.h"
+#include "HelperMacros.h"
+
+#ifdef UNITTEST_MEMORYOUTSTREAM_IS_STD_OSTRINGSTREAM
+
+#include <sstream>
+
+namespace UnitTest
+{
+class MemoryOutStream : public std::ostringstream
+{
+public:
+    UNITTEST_LINKAGE MemoryOutStream();
+    UNITTEST_LINKAGE ~MemoryOutStream();
+    UNITTEST_LINKAGE void Clear();
+    UNITTEST_LINKAGE char const* GetText() const;
+
+private:
+    MemoryOutStream(MemoryOutStream const&);
+    void operator=(MemoryOutStream const&);
+
+    mutable std::string m_text;
+};
+
+} // namespace UnitTest
+
+#else
+
+#include <cstddef>
+
+namespace UnitTest
+{
+class UNITTEST_LINKAGE MemoryOutStream
+{
+public:
+    explicit MemoryOutStream(int const size = 256);
+    ~MemoryOutStream();
+
+    void Clear();
+    char const* GetText() const;
+
+    MemoryOutStream& operator<<(char const* txt);
+    MemoryOutStream& operator<<(int n);
+    MemoryOutStream& operator<<(long n);
+    MemoryOutStream& operator<<(long long n);
+    MemoryOutStream& operator<<(unsigned long n);
+    MemoryOutStream& operator<<(unsigned long long n);
+    MemoryOutStream& operator<<(float f);
+    MemoryOutStream& operator<<(double d);
+    MemoryOutStream& operator<<(void const* p);
+    MemoryOutStream& operator<<(unsigned int s);
+
+    enum
+    {
+        GROW_CHUNK_SIZE = 32
+    };
+    int GetCapacity() const;
+
+private:
+    void operator=(MemoryOutStream const&);
+    void GrowBuffer(int capacity);
+
+    int m_capacity;
+    char* m_buffer;
+};
+
+} // namespace UnitTest
+
+#endif
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/Posix/SignalTranslator.cpp b/Release/tests/common/UnitTestpp/src/Posix/SignalTranslator.cpp
new file mode 100644 (file)
index 0000000..0456216
--- /dev/null
@@ -0,0 +1,72 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "SignalTranslator.h"
+
+namespace UnitTest
+{
+sigjmp_buf* SignalTranslator::s_jumpTarget = 0;
+
+namespace
+{
+void SignalHandler(int sig) { siglongjmp(*SignalTranslator::s_jumpTarget, sig); }
+
+} // namespace
+
+SignalTranslator::SignalTranslator()
+{
+    m_oldJumpTarget = s_jumpTarget;
+    s_jumpTarget = &m_currentJumpTarget;
+
+    struct sigaction action;
+    action.sa_flags = 0;
+    action.sa_handler = SignalHandler;
+    sigemptyset(&action.sa_mask);
+
+    sigaction(SIGSEGV, &action, &m_old_SIGSEGV_action);
+    sigaction(SIGFPE, &action, &m_old_SIGFPE_action);
+    sigaction(SIGTRAP, &action, &m_old_SIGTRAP_action);
+    sigaction(SIGBUS, &action, &m_old_SIGBUS_action);
+    sigaction(SIGILL, &action, &m_old_SIGBUS_action);
+}
+
+SignalTranslator::~SignalTranslator()
+{
+    sigaction(SIGILL, &m_old_SIGBUS_action, 0);
+    sigaction(SIGBUS, &m_old_SIGBUS_action, 0);
+    sigaction(SIGTRAP, &m_old_SIGTRAP_action, 0);
+    sigaction(SIGFPE, &m_old_SIGFPE_action, 0);
+    sigaction(SIGSEGV, &m_old_SIGSEGV_action, 0);
+
+    s_jumpTarget = m_oldJumpTarget;
+}
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/Posix/SignalTranslator.h b/Release/tests/common/UnitTestpp/src/Posix/SignalTranslator.h
new file mode 100644 (file)
index 0000000..bde4e3a
--- /dev/null
@@ -0,0 +1,72 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_SIGNALTRANSLATOR_H
+#define UNITTEST_SIGNALTRANSLATOR_H
+
+#include <setjmp.h>
+#include <signal.h>
+
+namespace UnitTest
+{
+class SignalTranslator
+{
+public:
+    SignalTranslator();
+    ~SignalTranslator();
+
+    static sigjmp_buf* s_jumpTarget;
+
+    sigjmp_buf m_currentJumpTarget;
+    sigjmp_buf* m_oldJumpTarget;
+
+    struct sigaction m_old_SIGFPE_action;
+    struct sigaction m_old_SIGTRAP_action;
+    struct sigaction m_old_SIGSEGV_action;
+    struct sigaction m_old_SIGBUS_action;
+    struct sigaction m_old_SIGABRT_action;
+    struct sigaction m_old_SIGALRM_action;
+};
+
+#if !defined(__GNUC__)
+#define UNITTEST_EXTENSION
+#else
+#define UNITTEST_EXTENSION __extension__
+#endif
+
+#define UNITTEST_THROW_SIGNALS_POSIX_ONLY                                                                              \
+    UnitTest::SignalTranslator sig;                                                                                    \
+    if (UNITTEST_EXTENSION sigsetjmp(*UnitTest::SignalTranslator::s_jumpTarget, 1) != 0)                               \
+        throw("Unhandled system exception");
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/Posix/TimeHelpers.cpp b/Release/tests/common/UnitTestpp/src/Posix/TimeHelpers.cpp
new file mode 100644 (file)
index 0000000..e271277
--- /dev/null
@@ -0,0 +1,59 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "TimeHelpers.h"
+
+#include <unistd.h>
+
+namespace UnitTest
+{
+Timer::Timer()
+{
+    m_startTime.tv_sec = 0;
+    m_startTime.tv_usec = 0;
+}
+
+void Timer::Start() { gettimeofday(&m_startTime, 0); }
+
+double Timer::GetTimeInMs() const
+{
+    struct timeval currentTime;
+    gettimeofday(&currentTime, 0);
+
+    double const dsecs = currentTime.tv_sec - m_startTime.tv_sec;
+    double const dus = currentTime.tv_usec - m_startTime.tv_usec;
+
+    return (dsecs * 1000.0) + (dus / 1000.0);
+}
+
+void TimeHelpers::SleepMs(int ms) { usleep(ms * 1000); }
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/Posix/TimeHelpers.h b/Release/tests/common/UnitTestpp/src/Posix/TimeHelpers.h
new file mode 100644 (file)
index 0000000..75d368e
--- /dev/null
@@ -0,0 +1,57 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TIMEHELPERS_H
+#define UNITTEST_TIMEHELPERS_H
+
+#include <sys/time.h>
+
+namespace UnitTest
+{
+class Timer
+{
+public:
+    Timer();
+    void Start();
+    double GetTimeInMs() const;
+
+private:
+    struct timeval m_startTime;
+};
+
+namespace TimeHelpers
+{
+void SleepMs(int ms);
+}
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/ReportAssert.cpp b/Release/tests/common/UnitTestpp/src/ReportAssert.cpp
new file mode 100644 (file)
index 0000000..4dba03d
--- /dev/null
@@ -0,0 +1,94 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include "ReportAssert.h"
+
+#include "ReportAssertImpl.h"
+
+#ifdef UNITTEST_NO_EXCEPTIONS
+#include "ReportAssertImpl.h"
+#endif
+
+namespace UnitTest
+{
+namespace
+{
+bool& AssertExpectedFlag()
+{
+    static bool s_assertExpected = false;
+    return s_assertExpected;
+}
+} // namespace
+
+UNITTEST_LINKAGE void ReportAssert(char const* description, char const* filename, int lineNumber)
+{
+    Detail::ReportAssertEx(CurrentTest::Results(), CurrentTest::Details(), description, filename, lineNumber);
+}
+
+namespace Detail
+{
+#ifdef UNITTEST_NO_EXCEPTIONS
+UNITTEST_JMPBUF* GetAssertJmpBuf()
+{
+    static UNITTEST_JMPBUF s_jmpBuf;
+    return &s_jmpBuf;
+}
+#endif
+
+UNITTEST_LINKAGE void ReportAssertEx(TestResults* testResults,
+                                     const TestDetails* testDetails,
+                                     char const* description,
+                                     char const* filename,
+                                     int lineNumber)
+{
+    if (AssertExpectedFlag() == false)
+    {
+        TestDetails assertDetails(testDetails->testName, testDetails->suiteName, filename, lineNumber);
+        testResults->OnTestFailure(assertDetails, description);
+    }
+
+    ExpectAssert(false);
+
+#ifndef UNITTEST_NO_EXCEPTIONS
+    throw AssertException();
+#else
+    UNITTEST_JUMP_TO_ASSERT_JUMP_TARGET();
+#endif
+}
+
+UNITTEST_LINKAGE void ExpectAssert(bool expected) { AssertExpectedFlag() = expected; }
+
+UNITTEST_LINKAGE bool AssertExpected() { return AssertExpectedFlag(); }
+
+} // namespace Detail
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/ReportAssert.h b/Release/tests/common/UnitTestpp/src/ReportAssert.h
new file mode 100644 (file)
index 0000000..858d8a7
--- /dev/null
@@ -0,0 +1,43 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_ASSERT_H
+#define UNITTEST_ASSERT_H
+
+#include "HelperMacros.h"
+
+namespace UnitTest
+{
+UNITTEST_LINKAGE void ReportAssert(char const* description, char const* filename, int lineNumber);
+
+}
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/ReportAssertImpl.h b/Release/tests/common/UnitTestpp/src/ReportAssertImpl.h
new file mode 100644 (file)
index 0000000..d6c1b3f
--- /dev/null
@@ -0,0 +1,76 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_REPORTASSERTIMPL_H
+#define UNITTEST_REPORTASSERTIMPL_H
+
+#include "../config.h"
+#include "HelperMacros.h"
+
+#ifdef UNITTEST_NO_EXCEPTIONS
+#include <csetjmp>
+#endif
+
+namespace UnitTest
+{
+class TestResults;
+class TestDetails;
+
+namespace Detail
+{
+UNITTEST_LINKAGE void ExpectAssert(bool expected);
+
+UNITTEST_LINKAGE void ReportAssertEx(TestResults* testResults,
+                                     const TestDetails* testDetails,
+                                     char const* description,
+                                     char const* filename,
+                                     int lineNumber);
+
+UNITTEST_LINKAGE bool AssertExpected();
+
+#ifdef UNITTEST_NO_EXCEPTIONS
+UNITTEST_LINKAGE UNITTEST_JMPBUF* GetAssertJmpBuf();
+
+#ifdef UNITTEST_WIN32
+#define UNITTEST_SET_ASSERT_JUMP_TARGET()                                                                              \
+    __pragma(warning(push)) __pragma(warning(disable : 4611)) UNITTEST_SETJMP(*UnitTest::Detail::GetAssertJmpBuf())    \
+        __pragma(warning(pop))
+#else
+#define UNITTEST_SET_ASSERT_JUMP_TARGET() UNITTEST_SETJMP(*UnitTest::Detail::GetAssertJmpBuf())
+#endif
+
+#define UNITTEST_JUMP_TO_ASSERT_JUMP_TARGET() UNITTEST_LONGJMP(*UnitTest::Detail::GetAssertJmpBuf(), 1)
+#endif
+
+} // namespace Detail
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/Test.cpp b/Release/tests/common/UnitTestpp/src/Test.cpp
new file mode 100644 (file)
index 0000000..70805b6
--- /dev/null
@@ -0,0 +1,53 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include "ExecuteTest.h"
+
+#ifdef UNITTEST_POSIX
+#include "Posix/SignalTranslator.h"
+#endif
+
+namespace UnitTest
+{
+Test::Test(char const* testName, char const* suiteName, char const* filename, int lineNumber)
+    : m_details(testName, suiteName, filename, lineNumber), m_nextTest(0), m_isMockTest(false)
+{
+}
+
+Test::~Test() {}
+
+void Test::Run() { ExecuteTest(*this, m_details, m_isMockTest); }
+
+void Test::RunImpl() const {}
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/Test.h b/Release/tests/common/UnitTestpp/src/Test.h
new file mode 100644 (file)
index 0000000..9c73551
--- /dev/null
@@ -0,0 +1,67 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TEST_H
+#define UNITTEST_TEST_H
+
+#include "TestDetails.h"
+#include "TestProperties.h"
+
+namespace UnitTest
+{
+class TestResults;
+
+class Test
+{
+public:
+    UNITTEST_LINKAGE explicit Test(char const* testName,
+                                   char const* suiteName = "DefaultSuite",
+                                   char const* filename = "",
+                                   int lineNumber = 0);
+    UNITTEST_LINKAGE virtual ~Test();
+    UNITTEST_LINKAGE void Run();
+
+    TestProperties m_properties;
+
+    TestDetails const m_details;
+    Test* m_nextTest;
+    mutable bool m_isMockTest;
+
+    UNITTEST_LINKAGE virtual void RunImpl() const;
+
+private:
+    Test(Test const&);
+    Test& operator=(Test const&);
+};
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/TestDetails.cpp b/Release/tests/common/UnitTestpp/src/TestDetails.cpp
new file mode 100644 (file)
index 0000000..a060df7
--- /dev/null
@@ -0,0 +1,46 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+namespace UnitTest
+{
+TestDetails::TestDetails(char const* testName_, char const* suiteName_, char const* filename_, int lineNumber_)
+    : suiteName(suiteName_), testName(testName_), filename(filename_), lineNumber(lineNumber_)
+{
+}
+
+TestDetails::TestDetails(const TestDetails& details, int lineNumber_)
+    : suiteName(details.suiteName), testName(details.testName), filename(details.filename), lineNumber(lineNumber_)
+{
+}
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/TestDetails.h b/Release/tests/common/UnitTestpp/src/TestDetails.h
new file mode 100644 (file)
index 0000000..ef88638
--- /dev/null
@@ -0,0 +1,57 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TESTDETAILS_H
+#define UNITTEST_TESTDETAILS_H
+
+#include "HelperMacros.h"
+
+namespace UnitTest
+{
+class TestDetails
+{
+public:
+    UNITTEST_LINKAGE TestDetails(char const* testName, char const* suiteName, char const* filename, int lineNumber);
+    UNITTEST_LINKAGE TestDetails(const TestDetails& details, int lineNumber);
+
+    char const* const suiteName;
+    char const* const testName;
+    char const* const filename;
+    int const lineNumber;
+
+    UNITTEST_LINKAGE TestDetails(TestDetails const&); // Why is it public? --> http://gcc.gnu.org/bugs.html#cxx_rvalbind
+private:
+    TestDetails& operator=(TestDetails const&);
+};
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/TestList.cpp b/Release/tests/common/UnitTestpp/src/TestList.cpp
new file mode 100644 (file)
index 0000000..ab3eaaa
--- /dev/null
@@ -0,0 +1,110 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include <cassert>
+#include <stdarg.h>
+
+namespace UnitTest
+{
+TestList::TestList() : m_head(nullptr), m_tail(nullptr) {}
+
+void TestList::Clear()
+{
+    m_head = nullptr;
+    m_tail = nullptr;
+}
+
+void TestList::Add(Test* test)
+{
+    if (m_tail == 0)
+    {
+        assert(m_head == 0);
+        m_head = test;
+        m_tail = test;
+    }
+    else
+    {
+        m_tail->m_nextTest = test;
+        m_tail = test;
+    }
+}
+
+Test* TestList::GetFirst() const { return m_head; }
+
+bool TestList::IsEmpty() const { return m_head == nullptr; }
+
+ListAdder::ListAdder(TestList& list, Test* test, ...)
+{
+    char* arg;
+    va_list argList;
+    va_start(argList, test);
+    for (arg = va_arg(argList, char*); arg != nullptr; arg = va_arg(argList, char*))
+    {
+        char* key = arg;
+        arg = va_arg(argList, char*);
+        if (arg != nullptr)
+        {
+            char* value = arg;
+            test->m_properties.Add(key, value);
+        }
+    }
+    va_end(argList);
+
+    // If on windows we could be either desktop or winrt. Make a requires property for the correct version.
+    // Only a desktop runner environment can execute a desktop test case and vice versa on winrt.
+    // This starts with visual studio versions after VS 2012.
+#if defined(_MSC_VER) && (_MSC_VER >= 1800)
+#ifdef __cplusplus_winrt
+    test->m_properties.Add("Requires", "winrt");
+#else
+    test->m_properties.Add("Requires", "desktop");
+#endif
+#endif
+
+    list.Add(test);
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
+#endif
+extern "C" UNITTEST_LINKAGE TestList& GetTestList()
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+{
+    static TestList GLOBAL_TESTLIST;
+    return GLOBAL_TESTLIST;
+}
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/TestList.h b/Release/tests/common/UnitTestpp/src/TestList.h
new file mode 100644 (file)
index 0000000..686e446
--- /dev/null
@@ -0,0 +1,66 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TESTLIST_H
+#define UNITTEST_TESTLIST_H
+
+#include "HelperMacros.h"
+
+namespace UnitTest
+{
+class Test;
+
+class TestList
+{
+public:
+    UNITTEST_LINKAGE TestList();
+    UNITTEST_LINKAGE void Add(Test* test);
+
+    UNITTEST_LINKAGE Test* GetFirst() const;
+
+    UNITTEST_LINKAGE bool IsEmpty() const;
+
+    UNITTEST_LINKAGE void Clear();
+
+private:
+    Test* m_head;
+    Test* m_tail;
+};
+
+class UNITTEST_LINKAGE ListAdder
+{
+public:
+    ListAdder(TestList& list, Test* test, ...);
+};
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/TestMacros.h b/Release/tests/common/UnitTestpp/src/TestMacros.h
new file mode 100644 (file)
index 0000000..a73b3f1
--- /dev/null
@@ -0,0 +1,251 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TESTMACROS_H
+#define UNITTEST_TESTMACROS_H
+
+#include "../config.h"
+#include "AssertException.h"
+#include "ExceptionMacros.h"
+#include "ExecuteTest.h"
+#include "MemoryOutStream.h"
+#include "TestDetails.h"
+#include "TestList.h"
+#include "TestSuite.h"
+
+#ifndef UNITTEST_POSIX
+#define UNITTEST_THROW_SIGNALS_POSIX_ONLY
+#else
+#include "Posix/SignalTranslator.h"
+#endif
+
+#ifdef TEST
+#error UnitTest++ redefines TEST
+#endif
+
+#ifdef TEST_EX
+#error UnitTest++ redefines TEST_EX
+#endif
+
+#ifdef TEST_FIXTURE_EX
+#error UnitTest++ redefines TEST_FIXTURE_EX
+#endif
+
+#ifndef CREATED_GET_TEST_LIST
+#define CREATED_GET_TEST_LIST
+
+#ifdef _WIN32
+#define _DLL_EXPORT __declspec(dllexport)
+#elif __APPLE__
+#define _DLL_EXPORT __attribute__((visibility("default")))
+#else
+#define _DLL_EXPORT
+#endif
+
+namespace UnitTest
+{
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
+#endif
+extern "C" _DLL_EXPORT TestList& __cdecl GetTestList();
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+} // namespace UnitTest
+#endif
+
+#define SUITE(Name)                                                                                                    \
+    namespace Suite##Name                                                                                              \
+    {                                                                                                                  \
+        namespace UnitTestSuite                                                                                        \
+        {                                                                                                              \
+        inline char const* GetSuiteName() { return #Name; }                                                            \
+        }                                                                                                              \
+    }                                                                                                                  \
+    namespace Suite##Name
+
+#ifdef _WIN32
+#define TEST_EX(Name, List, ...)                                                                                       \
+    class Test##Name : public UnitTest::Test                                                                           \
+    {                                                                                                                  \
+    public:                                                                                                            \
+        Test##Name() : Test(#Name, UnitTestSuite::GetSuiteName(), __FILE__, __LINE__) {}                               \
+                                                                                                                       \
+    private:                                                                                                           \
+        virtual void RunImpl() const;                                                                                  \
+    } test##Name##Instance;                                                                                            \
+                                                                                                                       \
+    UnitTest::ListAdder adder##Name(List, &test##Name##Instance, __VA_ARGS__, NULL);                                   \
+                                                                                                                       \
+    void Test##Name::RunImpl() const
+
+#else
+#define TEST_EX(Name, List, ...)                                                                                       \
+    class Test##Name : public UnitTest::Test                                                                           \
+    {                                                                                                                  \
+    public:                                                                                                            \
+        Test##Name() : Test(#Name, UnitTestSuite::GetSuiteName(), __FILE__, __LINE__) {}                               \
+                                                                                                                       \
+    private:                                                                                                           \
+        virtual void RunImpl() const;                                                                                  \
+    } test##Name##Instance;                                                                                            \
+                                                                                                                       \
+    UnitTest::ListAdder adder##Name(List, &test##Name##Instance, ##__VA_ARGS__, nullptr);                              \
+                                                                                                                       \
+    void Test##Name::RunImpl() const
+#endif
+
+#ifdef _WIN32
+#define TEST(Name, ...) TEST_EX(Name, UnitTest::GetTestList(), __VA_ARGS__)
+#else
+#define TEST(Name, ...) TEST_EX(Name, UnitTest::GetTestList(), ##__VA_ARGS__)
+#endif
+
+#ifdef _WIN32
+#define TEST_FIXTURE_EX(Fixture, Name, List, ...)                                                                      \
+    class Fixture##Name##Helper : public Fixture                                                                       \
+    {                                                                                                                  \
+    public:                                                                                                            \
+        explicit Fixture##Name##Helper(UnitTest::TestDetails const& details) : m_details(details) {}                   \
+        void RunImpl();                                                                                                \
+        UnitTest::TestDetails const& m_details;                                                                        \
+                                                                                                                       \
+    private:                                                                                                           \
+        Fixture##Name##Helper(Fixture##Name##Helper const&);                                                           \
+        Fixture##Name##Helper& operator=(Fixture##Name##Helper const&);                                                \
+    };                                                                                                                 \
+                                                                                                                       \
+    class Test##Fixture##Name : public UnitTest::Test                                                                  \
+    {                                                                                                                  \
+    public:                                                                                                            \
+        Test##Fixture##Name() : Test(#Name, UnitTestSuite::GetSuiteName(), __FILE__, __LINE__) {}                      \
+                                                                                                                       \
+    private:                                                                                                           \
+        virtual void RunImpl() const;                                                                                  \
+    } test##Fixture##Name##Instance;                                                                                   \
+                                                                                                                       \
+    UnitTest::ListAdder adder##Fixture##Name(List, &test##Fixture##Name##Instance, __VA_ARGS__, NULL);                 \
+                                                                                                                       \
+    void Test##Fixture##Name::RunImpl() const                                                                          \
+    {                                                                                                                  \
+        volatile bool ctorOk = false;                                                                                  \
+        UT_TRY({                                                                                                       \
+            Fixture##Name##Helper fixtureHelper(m_details);                                                            \
+            ctorOk = true;                                                                                             \
+            UnitTest::ExecuteTest(fixtureHelper, m_details, false);                                                    \
+        })                                                                                                             \
+        UT_CATCH(UnitTest::AssertException, e, { (void)e; })                                                           \
+        UT_CATCH(std::exception, e, {                                                                                  \
+            UnitTest::MemoryOutStream stream;                                                                          \
+            stream << "Unhandled exception: " << e.what();                                                             \
+            UnitTest::CurrentTest::Results()->OnTestFailure(m_details, stream.GetText());                              \
+        })                                                                                                             \
+        UT_CATCH_ALL({                                                                                                 \
+            if (ctorOk)                                                                                                \
+            {                                                                                                          \
+                UnitTest::CurrentTest::Results()->OnTestFailure(                                                       \
+                    UnitTest::TestDetails(m_details, __LINE__),                                                        \
+                    "Unhandled exception while destroying fixture " #Fixture);                                         \
+            }                                                                                                          \
+            else                                                                                                       \
+            {                                                                                                          \
+                UnitTest::CurrentTest::Results()->OnTestFailure(                                                       \
+                    UnitTest::TestDetails(m_details, __LINE__),                                                        \
+                    "Unhandled exception while constructing fixture " #Fixture);                                       \
+            }                                                                                                          \
+        })                                                                                                             \
+    }                                                                                                                  \
+    void Fixture##Name##Helper::RunImpl()
+#else
+#define TEST_FIXTURE_EX(Fixture, Name, List, ...)                                                                      \
+    class Fixture##Name##Helper : public Fixture                                                                       \
+    {                                                                                                                  \
+    public:                                                                                                            \
+        explicit Fixture##Name##Helper(UnitTest::TestDetails const& details) : m_details(details) {}                   \
+        void RunImpl();                                                                                                \
+        UnitTest::TestDetails const& m_details;                                                                        \
+                                                                                                                       \
+    private:                                                                                                           \
+        Fixture##Name##Helper(Fixture##Name##Helper const&);                                                           \
+        Fixture##Name##Helper& operator=(Fixture##Name##Helper const&);                                                \
+    };                                                                                                                 \
+                                                                                                                       \
+    class Test##Fixture##Name : public UnitTest::Test                                                                  \
+    {                                                                                                                  \
+    public:                                                                                                            \
+        Test##Fixture##Name() : Test(#Name, UnitTestSuite::GetSuiteName(), __FILE__, __LINE__) {}                      \
+                                                                                                                       \
+    private:                                                                                                           \
+        virtual void RunImpl() const;                                                                                  \
+    } test##Fixture##Name##Instance;                                                                                   \
+                                                                                                                       \
+    UnitTest::ListAdder adder##Fixture##Name(List, &test##Fixture##Name##Instance, ##__VA_ARGS__, NULL);               \
+                                                                                                                       \
+    void Test##Fixture##Name::RunImpl() const                                                                          \
+    {                                                                                                                  \
+        volatile bool ctorOk = false;                                                                                  \
+        UT_TRY({                                                                                                       \
+            Fixture##Name##Helper fixtureHelper(m_details);                                                            \
+            ctorOk = true;                                                                                             \
+            UnitTest::ExecuteTest(fixtureHelper, m_details, false);                                                    \
+        })                                                                                                             \
+        UT_CATCH(UnitTest::AssertException, e, { (void)e; })                                                           \
+        UT_CATCH(std::exception, e, {                                                                                  \
+            UnitTest::MemoryOutStream stream;                                                                          \
+            stream << "Unhandled exception: " << e.what();                                                             \
+            UnitTest::CurrentTest::Results()->OnTestFailure(m_details, stream.GetText());                              \
+        })                                                                                                             \
+        UT_CATCH_ALL({                                                                                                 \
+            if (ctorOk)                                                                                                \
+            {                                                                                                          \
+                UnitTest::CurrentTest::Results()->OnTestFailure(                                                       \
+                    UnitTest::TestDetails(m_details, __LINE__),                                                        \
+                    "Unhandled exception while destroying fixture " #Fixture);                                         \
+            }                                                                                                          \
+            else                                                                                                       \
+            {                                                                                                          \
+                UnitTest::CurrentTest::Results()->OnTestFailure(                                                       \
+                    UnitTest::TestDetails(m_details, __LINE__),                                                        \
+                    "Unhandled exception while constructing fixture " #Fixture);                                       \
+            }                                                                                                          \
+        })                                                                                                             \
+    }                                                                                                                  \
+    void Fixture##Name##Helper::RunImpl()
+#endif
+
+#ifdef _WIN32
+#define TEST_FIXTURE(Fixture, Name, ...) TEST_FIXTURE_EX(Fixture, Name, UnitTest::GetTestList(), __VA_ARGS__)
+#else
+#define TEST_FIXTURE(Fixture, Name, ...) TEST_FIXTURE_EX(Fixture, Name, UnitTest::GetTestList(), ##__VA_ARGS__)
+#endif
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/TestProperties.h b/Release/tests/common/UnitTestpp/src/TestProperties.h
new file mode 100644 (file)
index 0000000..da15a1a
--- /dev/null
@@ -0,0 +1,85 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TEST_PROPERTIES_H
+#define UNITTEST_TEST_PROPERTIES_H
+
+#include <map>
+#include <stdexcept>
+#include <string>
+
+namespace UnitTest
+{
+// Simple key value pairs.
+class TestProperties
+{
+public:
+    TestProperties() {}
+
+    void Add(const std::string& key, const std::string& value)
+    {
+        if (!Has(key))
+        {
+            m_properties[key] = value;
+        }
+        else
+        {
+            m_properties[key] += ";";
+            m_properties[key] += value;
+        }
+    }
+
+    bool Has(const std::string& key) const { return m_properties.find(key) != m_properties.end(); }
+
+    const std::string& Get(const std::string& key) const
+    {
+        if (!Has(key))
+        {
+            throw std::invalid_argument("Error: property is not found");
+        }
+        return m_properties.find(key)->second;
+    }
+
+    const std::string& operator[](const std::string& key) const { return Get(key); }
+
+    std::map<std::string, std::string>::const_iterator begin() const { return m_properties.begin(); }
+
+    std::map<std::string, std::string>::const_iterator end() const { return m_properties.end(); }
+
+private:
+    std::map<std::string, std::string> m_properties;
+    TestProperties(const TestProperties&);
+    TestProperties& operator=(const TestProperties&);
+};
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/TestReporter.cpp b/Release/tests/common/UnitTestpp/src/TestReporter.cpp
new file mode 100644 (file)
index 0000000..13fffbc
--- /dev/null
@@ -0,0 +1,40 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+namespace UnitTest
+{
+TestReporter::TestReporter() {}
+
+TestReporter::~TestReporter() {}
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/TestReporter.h b/Release/tests/common/UnitTestpp/src/TestReporter.h
new file mode 100644 (file)
index 0000000..bb455a6
--- /dev/null
@@ -0,0 +1,57 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TESTREPORTER_H
+#define UNITTEST_TESTREPORTER_H
+
+#include "HelperMacros.h"
+
+namespace UnitTest
+{
+class TestDetails;
+
+class TestReporter
+{
+public:
+    UNITTEST_LINKAGE TestReporter();
+    UNITTEST_LINKAGE virtual ~TestReporter();
+
+    UNITTEST_LINKAGE virtual void ReportTestStart(TestDetails const& test) = 0;
+    UNITTEST_LINKAGE virtual void ReportFailure(TestDetails const& test, char const* failure) = 0;
+    UNITTEST_LINKAGE virtual void ReportTestFinish(TestDetails const& test, bool passed, float secondsElapsed) = 0;
+    UNITTEST_LINKAGE virtual void ReportSummary(int totalTestCount,
+                                                int failedTestCount,
+                                                int failureCount,
+                                                float secondsElapsed) = 0;
+};
+
+} // namespace UnitTest
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/TestReporterStdout.cpp b/Release/tests/common/UnitTestpp/src/TestReporterStdout.cpp
new file mode 100644 (file)
index 0000000..04f210c
--- /dev/null
@@ -0,0 +1,145 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include <stdarg.h>
+#include <vector>
+
+// cstdio doesn't pull in namespace std on VC6, so we do it here.
+#if defined(UNITTEST_WIN32) && (_MSC_VER == 1200)
+namespace std
+{
+}
+#endif
+
+namespace UnitTest
+{
+// Function to work around outputing to the console when under WinRT.
+static void PrintfWrapper(const char* format, ...)
+{
+    va_list args;
+    va_start(args, format);
+
+#ifdef __cplusplus_winrt
+    const auto bufSize = _vscprintf(format, args) + 1; // add 1 for null termination
+    std::vector<char> byteArray;
+    byteArray.resize(bufSize);
+    vsnprintf_s(&byteArray[0], bufSize, bufSize, format, args);
+
+    DWORD bytesWritten;
+    HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
+    WriteFile(h, &byteArray[0], (DWORD)bufSize, &bytesWritten, NULL);
+#else
+#ifdef _WIN32
+    vfprintf_s(stdout, format, args);
+#else
+    vfprintf(stdout, format, args);
+#endif
+#endif
+
+    va_end(args);
+}
+
+static void ChangeConsoleTextColorToRed()
+{
+#if defined(__cplusplus_winrt)
+#elif defined(_WIN32)
+    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0004 | 0x0008);
+#else
+    std::cout << "\033[1;31m";
+#endif
+}
+
+static void ChangeConsoleTextColorToGreen()
+{
+#if defined(__cplusplus_winrt)
+#elif defined(_WIN32)
+    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0002 | 0x0008);
+#else
+    std::cout << "\033[1;32m";
+#endif
+}
+
+static void ChangeConsoleTextColorToGrey()
+{
+#if defined(__cplusplus_winrt)
+#elif defined(_WIN32)
+    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
+#else
+    std::cout << "\033[0m";
+#endif
+}
+
+void TestReporterStdout::ReportFailure(TestDetails const& details, char const* failure)
+{
+#if defined(__APPLE__) || defined(__GNUG__)
+    char const* const errorFormat = "%s:%d: error: Failure in %s: %s FAILED\n";
+#else
+    char const* const errorFormat = "%s(%d): error: Failure in %s: %s FAILED\n";
+#endif
+
+    ChangeConsoleTextColorToRed();
+    PrintfWrapper(errorFormat, details.filename, details.lineNumber, details.testName, failure);
+    ChangeConsoleTextColorToGrey();
+    std::fflush(stdout);
+}
+
+void TestReporterStdout::ReportTestStart(TestDetails const& test)
+{
+    const char* format = "Starting test case %s:%s...\n";
+    PrintfWrapper(format, test.suiteName, test.testName);
+    std::fflush(stdout);
+}
+
+void TestReporterStdout::ReportTestFinish(TestDetails const& test, bool passed, float)
+{
+    if (passed)
+    {
+        const char* format = "Test case %s:%s ";
+        PrintfWrapper(format, test.suiteName, test.testName);
+        ChangeConsoleTextColorToGreen();
+        PrintfWrapper("PASSED\n");
+        ChangeConsoleTextColorToGrey();
+    }
+    else
+    {
+        ChangeConsoleTextColorToRed();
+        const char* format = "Test case %s:%s FAILED\n";
+        PrintfWrapper(format, test.suiteName, test.testName);
+        ChangeConsoleTextColorToGrey();
+    }
+    std::fflush(stdout);
+}
+
+void TestReporterStdout::ReportSummary(int const, int const, int const, float) {}
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/TestReporterStdout.h b/Release/tests/common/UnitTestpp/src/TestReporterStdout.h
new file mode 100644 (file)
index 0000000..2e48e74
--- /dev/null
@@ -0,0 +1,53 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TESTREPORTERSTDOUT_H
+#define UNITTEST_TESTREPORTERSTDOUT_H
+
+#include "TestReporter.h"
+
+namespace UnitTest
+{
+class TestReporterStdout : public TestReporter
+{
+private:
+    UNITTEST_LINKAGE virtual void ReportTestStart(TestDetails const& test);
+    UNITTEST_LINKAGE virtual void ReportFailure(TestDetails const& test, char const* failure);
+    UNITTEST_LINKAGE virtual void ReportTestFinish(TestDetails const& test, bool passed, float secondsElapsed);
+    UNITTEST_LINKAGE virtual void ReportSummary(int totalTestCount,
+                                                int failedTestCount,
+                                                int failureCount,
+                                                float secondsElapsed);
+};
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/TestResults.cpp b/Release/tests/common/UnitTestpp/src/TestResults.cpp
new file mode 100644 (file)
index 0000000..b65611c
--- /dev/null
@@ -0,0 +1,99 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#ifndef WIN32
+#include "signal.h"
+#endif
+
+namespace UnitTest
+{
+TestResults::TestResults(TestReporter* testReporter, bool breakOnError)
+    : m_testReporter(testReporter)
+    , m_totalTestCount(0)
+    , m_failedTestCount(0)
+    , m_failureCount(0)
+    , m_currentTestFailed(false)
+    , m_breakOnError(breakOnError)
+{
+}
+
+void TestResults::OnTestStart(TestDetails const& test)
+{
+    ++m_totalTestCount;
+    m_currentTestFailed = false;
+    if (m_testReporter) m_testReporter->ReportTestStart(test);
+}
+
+#ifdef WIN32
+#define DEBUG_BREAK() __debugbreak()
+#else
+#define DEBUG_BREAK() raise(SIGTRAP)
+#endif
+
+void TestResults::OnTestFailure(TestDetails const& test, char const* failure)
+{
+    ++m_failureCount;
+    if (!m_currentTestFailed)
+    {
+        ++m_failedTestCount;
+        std::string fullTestName(test.suiteName);
+        fullTestName.append(":");
+        fullTestName.append(test.testName);
+        m_failedTests.push_back(fullTestName);
+        m_currentTestFailed = true;
+    }
+
+    if (m_testReporter)
+    {
+        m_testReporter->ReportFailure(test, failure);
+        if (m_breakOnError)
+        {
+            DEBUG_BREAK();
+        }
+    }
+}
+
+void TestResults::OnTestFinish(TestDetails const& test, float secondsElapsed)
+{
+    if (m_testReporter) m_testReporter->ReportTestFinish(test, !m_currentTestFailed, secondsElapsed);
+}
+
+int TestResults::GetTotalTestCount() const { return m_totalTestCount; }
+
+int TestResults::GetFailedTestCount() const { return m_failedTestCount; }
+
+int TestResults::GetFailureCount() const { return m_failureCount; }
+
+const std::vector<std::string>& TestResults::GetFailedTests() const { return m_failedTests; }
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/TestResults.h b/Release/tests/common/UnitTestpp/src/TestResults.h
new file mode 100644 (file)
index 0000000..6a88b1c
--- /dev/null
@@ -0,0 +1,76 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TESTRESULTS_H
+#define UNITTEST_TESTRESULTS_H
+
+#include "HelperMacros.h"
+#include <string>
+#include <vector>
+
+namespace UnitTest
+{
+class TestReporter;
+class TestDetails;
+
+class TestResults
+{
+public:
+    UNITTEST_LINKAGE explicit TestResults(TestReporter* reporter = 0, bool breakOnError = false);
+
+    UNITTEST_LINKAGE void OnTestStart(TestDetails const& test);
+    UNITTEST_LINKAGE void OnTestFailure(TestDetails const& test, char const* failure);
+    UNITTEST_LINKAGE void OnTestFinish(TestDetails const& test, float secondsElapsed);
+
+    UNITTEST_LINKAGE int GetTotalTestCount() const;
+    UNITTEST_LINKAGE int GetFailedTestCount() const;
+    UNITTEST_LINKAGE int GetFailureCount() const;
+
+    UNITTEST_LINKAGE const std::vector<std::string>& GetFailedTests() const;
+
+private:
+    TestReporter* m_testReporter;
+    int m_totalTestCount;
+    int m_failedTestCount;
+    int m_failureCount;
+
+    bool m_currentTestFailed;
+    const bool m_breakOnError;
+
+    std::vector<std::string> m_failedTests;
+
+    TestResults(TestResults const&);
+    TestResults& operator=(TestResults const&);
+};
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/TestRunner.cpp b/Release/tests/common/UnitTestpp/src/TestRunner.cpp
new file mode 100644 (file)
index 0000000..807a0e3
--- /dev/null
@@ -0,0 +1,199 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include "TestRunner.h"
+
+#include "TestMacros.h"
+
+#if _MSC_VER == 1600
+#include <agents.h>
+#include <functional>
+#else
+#include <future>
+#endif
+
+#if (defined(ANDROID) || defined(__ANDROID__))
+#include <boost/scope_exit.hpp>
+#include <jni.h>
+#endif
+
+#include <cstdlib>
+
+#if (defined(ANDROID) || defined(__ANDROID__))
+namespace crossplat
+{
+extern std::atomic<JavaVM*> JVM;
+}
+#endif
+
+namespace UnitTest
+{
+TestRunner::TestRunner(TestReporter& reporter, bool breakOnError)
+    : m_reporter(&reporter), m_result(new TestResults(&reporter, breakOnError)), m_timer(new Timer)
+{
+    m_timer->Start();
+}
+
+TestRunner::~TestRunner()
+{
+    delete m_result;
+    delete m_timer;
+}
+
+TestResults* TestRunner::GetTestResults() { return m_result; }
+
+int TestRunner::Finish() const
+{
+    float const secondsElapsed = static_cast<float>(m_timer->GetTimeInMs() / 1000.0);
+    m_reporter->ReportSummary(
+        m_result->GetTotalTestCount(), m_result->GetFailedTestCount(), m_result->GetFailureCount(), secondsElapsed);
+
+    return m_result->GetFailureCount();
+}
+
+bool TestRunner::IsTestInSuite(const Test* const curTest, char const* suiteName) const
+{
+    using namespace std;
+    return (suiteName == NULL) || !strcmp(curTest->m_details.suiteName, suiteName);
+}
+
+#if _MSC_VER == 1600
+// std::future and std::thread doesn't exist on Visual Studio 2010 so fall back
+// to use agent.
+class TestRunnerAgent : public Concurrency::agent
+{
+public:
+    TestRunnerAgent(std::tr1::function<void()> func) : m_func(func) {}
+
+protected:
+    void run()
+    {
+        Concurrency::Context::Oversubscribe(true);
+        m_func();
+        Concurrency::Context::Oversubscribe(false);
+        done();
+    }
+
+private:
+    std::tr1::function<void()> m_func;
+};
+#endif
+
+// Logic to decide the timeout for individual test
+// 1. If /testtimeout is specified with testrunner arguments, use that timeout.
+// 2. Else, if the test has a Timeout property set, use that timeout.
+// 3. If both the above properties are not specified, use the default timeout value.
+int TestRunner::GetTestTimeout(Test* const curTest, int const defaultTestTimeInMs) const
+{
+    std::stringstream timeoutstream;
+    int timeout = defaultTestTimeInMs;
+    if (UnitTest::GlobalSettings::Has("testtimeout"))
+    {
+        timeoutstream << UnitTest::GlobalSettings::Get("testtimeout");
+        timeoutstream >> timeout;
+    }
+    else if (curTest->m_properties.Has("Timeout"))
+    {
+        timeoutstream << curTest->m_properties.Get("Timeout");
+        timeoutstream >> timeout;
+    }
+    return timeout;
+}
+
+void TestRunner::RunTest(TestResults* const result, Test* const curTest, int const defaultTestTimeInMs) const
+{
+    if (curTest->m_isMockTest == false) CurrentTest::SetResults(result);
+
+    int maxTestTimeInMs = GetTestTimeout(curTest, defaultTestTimeInMs);
+
+    Timer testTimer;
+    testTimer.Start();
+
+    result->OnTestStart(curTest->m_details);
+
+    if (maxTestTimeInMs > 0)
+    {
+        bool timedOut = false;
+#if _MSC_VER == 1600
+        TestRunnerAgent testRunnerAgent([&]() { curTest->Run(); });
+        testRunnerAgent.start();
+        try
+        {
+            Concurrency::agent::wait(&testRunnerAgent, maxTestTimeInMs);
+        }
+        catch (const Concurrency::operation_timed_out&)
+        {
+            timedOut = true;
+        }
+#else
+        // Timed wait requires async execution.
+        auto testRunnerFuture = std::async(std::launch::async, [&]() {
+#if (defined(ANDROID) || defined(__ANDROID__))
+            JNIEnv* env = nullptr;
+            auto result = crossplat::JVM.load()->AttachCurrentThread(&env, nullptr);
+            if (result != JNI_OK)
+            {
+                throw std::runtime_error("Could not attach to JVM");
+            }
+            BOOST_SCOPE_EXIT(void) { crossplat::JVM.load()->DetachCurrentThread(); }
+            BOOST_SCOPE_EXIT_END
+#endif
+            curTest->Run();
+        });
+        std::chrono::system_clock::time_point totalTime =
+            std::chrono::system_clock::now() + std::chrono::milliseconds(maxTestTimeInMs);
+        if (testRunnerFuture.wait_until(totalTime) == std::future_status::timeout)
+        {
+            timedOut = true;
+        }
+#endif
+        if (timedOut)
+        {
+            MemoryOutStream stream;
+            stream << "Test case timed out and is hung. Aborting all remaining test cases. ";
+            stream << "Expected under " << maxTestTimeInMs << "ms.";
+            result->OnTestFailure(curTest->m_details, stream.GetText());
+
+            abort();
+        }
+    }
+    else
+    {
+        curTest->Run();
+    }
+
+    double const testTimeInMs = testTimer.GetTimeInMs();
+    result->OnTestFinish(curTest->m_details, static_cast<float>(testTimeInMs / 1000.0));
+}
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/TestRunner.h b/Release/tests/common/UnitTestpp/src/TestRunner.h
new file mode 100644 (file)
index 0000000..160b36a
--- /dev/null
@@ -0,0 +1,107 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TESTRUNNER_H
+#define UNITTEST_TESTRUNNER_H
+
+#include "CurrentTest.h"
+#include "GlobalSettings.h"
+#include "Test.h"
+#include "TestList.h"
+
+namespace UnitTest
+{
+class TestReporter;
+class TestResults;
+class Timer;
+
+struct True
+{
+    bool operator()(const Test* const) const { return true; }
+};
+
+class TestRunner
+{
+public:
+    UNITTEST_LINKAGE explicit TestRunner(TestReporter& reporter, bool breakOnError = false);
+    UNITTEST_LINKAGE ~TestRunner();
+
+    template<class Predicate>
+    int RunTestsIf(TestList const& list, const Predicate& predicate, int defaultTestTimeInMs) const
+    {
+        return RunTestsIf(list, nullptr, predicate, defaultTestTimeInMs);
+    }
+
+    template<class Predicate>
+    int RunTestsIf(TestList const& list,
+                   char const* suiteName,
+                   const Predicate& predicate,
+                   int defaultTestTimeInMs) const
+    {
+        Test* curTest = list.GetFirst();
+
+        while (curTest != 0)
+        {
+            if (IsTestInSuite(curTest, suiteName) && predicate(curTest))
+                RunTest(m_result, curTest, defaultTestTimeInMs);
+
+            curTest = curTest->m_nextTest;
+        }
+
+        return Finish();
+    }
+
+    int RunTests(TestList const& list, char const* suiteName, int defaultTestTimeInMs) const
+    {
+        return RunTestsIf(list, suiteName, True(), defaultTestTimeInMs);
+    }
+    int RunTests(TestList const& list, int defaultTestTimeInMs) const
+    {
+        return RunTestsIf(list, nullptr, True(), defaultTestTimeInMs);
+    }
+
+    UNITTEST_LINKAGE TestResults* GetTestResults();
+
+private:
+    TestReporter* m_reporter;
+    TestResults* m_result;
+    Timer* m_timer;
+
+    int GetTestTimeout(Test* const curTest, int const defaultTestTimeInMs) const;
+
+    UNITTEST_LINKAGE int Finish() const;
+    UNITTEST_LINKAGE bool IsTestInSuite(const Test* const curTest, char const* suiteName) const;
+    UNITTEST_LINKAGE void RunTest(TestResults* const result, Test* const curTest, int const defaultTestTimeInMs) const;
+};
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/TestSuite.h b/Release/tests/common/UnitTestpp/src/TestSuite.h
new file mode 100644 (file)
index 0000000..877b8fe
--- /dev/null
@@ -0,0 +1,40 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TESTSUITE_H
+#define UNITTEST_TESTSUITE_H
+
+namespace UnitTestSuite
+{
+inline char const* GetSuiteName() { return "DefaultSuite"; }
+} // namespace UnitTestSuite
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/TimeHelpers.h b/Release/tests/common/UnitTestpp/src/TimeHelpers.h
new file mode 100644 (file)
index 0000000..a9d964a
--- /dev/null
@@ -0,0 +1,38 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "../config.h"
+
+#if defined UNITTEST_POSIX
+#include "Posix/TimeHelpers.h"
+#else
+#include "Win32/TimeHelpers.h"
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/Win32/TimeHelpers.cpp b/Release/tests/common/UnitTestpp/src/Win32/TimeHelpers.cpp
new file mode 100644 (file)
index 0000000..e20bb66
--- /dev/null
@@ -0,0 +1,69 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+namespace UnitTest
+{
+Timer::Timer() : m_threadHandle(::GetCurrentThread()), m_startTime(0)
+{
+#if defined(UNITTEST_WIN32) && (_MSC_VER == 1200) // VC6 doesn't have DWORD_PTR
+    typedef unsigned long DWORD_PTR;
+#endif
+
+    DWORD_PTR systemMask;
+    ::GetProcessAffinityMask(GetCurrentProcess(), &m_processAffinityMask, &systemMask);
+    ::SetThreadAffinityMask(m_threadHandle, 1);
+    ::QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&m_frequency));
+    ::SetThreadAffinityMask(m_threadHandle, m_processAffinityMask);
+}
+
+void Timer::Start() { m_startTime = GetTime(); }
+
+double Timer::GetTimeInMs() const
+{
+    __int64 const elapsedTime = GetTime() - m_startTime;
+    double const seconds = double(elapsedTime) / double(m_frequency);
+    return seconds * 1000.0;
+}
+
+__int64 Timer::GetTime() const
+{
+    LARGE_INTEGER curTime;
+    ::SetThreadAffinityMask(m_threadHandle, 1);
+    ::QueryPerformanceCounter(&curTime);
+    ::SetThreadAffinityMask(m_threadHandle, m_processAffinityMask);
+    return curTime.QuadPart;
+}
+
+void TimeHelpers::SleepMs(int ms) { ::Sleep(ms); }
+
+} // namespace UnitTest
diff --git a/Release/tests/common/UnitTestpp/src/Win32/TimeHelpers.h b/Release/tests/common/UnitTestpp/src/Win32/TimeHelpers.h
new file mode 100644 (file)
index 0000000..7c8edb6
--- /dev/null
@@ -0,0 +1,75 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_TIMEHELPERS_H
+#define UNITTEST_TIMEHELPERS_H
+
+#include "../../config.h"
+#include "../HelperMacros.h"
+
+#ifdef UNITTEST_MINGW
+#ifndef __int64
+#define __int64 long long
+#endif
+#endif
+
+namespace UnitTest
+{
+class Timer
+{
+public:
+    UNITTEST_LINKAGE Timer();
+    UNITTEST_LINKAGE void Start();
+    UNITTEST_LINKAGE double GetTimeInMs() const;
+
+private:
+    __int64 GetTime() const;
+
+    void* m_threadHandle;
+
+#if defined(_WIN64)
+    unsigned __int64 m_processAffinityMask;
+#else
+    unsigned long m_processAffinityMask;
+#endif
+
+    __int64 m_startTime;
+    __int64 m_frequency;
+};
+
+namespace TimeHelpers
+{
+UNITTEST_LINKAGE void SleepMs(int ms);
+}
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/XmlTestReporter.cpp b/Release/tests/common/UnitTestpp/src/XmlTestReporter.cpp
new file mode 100644 (file)
index 0000000..19c796f
--- /dev/null
@@ -0,0 +1,152 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#ifndef UNITTEST_NO_DEFERRED_REPORTER
+
+#include "XmlTestReporter.h"
+#include <iostream>
+#include <sstream>
+
+using std::ostream;
+using std::ostringstream;
+using std::string;
+
+namespace
+{
+void ReplaceChar(string& str, char c, string const& replacement)
+{
+    for (size_t pos = str.find(c); pos != string::npos; pos = str.find(c, pos + 1))
+        str.replace(pos, 1, replacement);
+}
+
+string XmlEscape(string const& value)
+{
+    string escaped = value;
+
+    ReplaceChar(escaped, '&', "&amp;");
+    ReplaceChar(escaped, '<', "&lt;");
+    ReplaceChar(escaped, '>', "&gt;");
+    ReplaceChar(escaped, '\'', "&apos;");
+    ReplaceChar(escaped, '\"', "&quot;");
+
+    return escaped;
+}
+
+string BuildFailureMessage(string const& file, int line, string const& message)
+{
+    ostringstream failureMessage;
+    failureMessage << file << "(" << line << ") : " << message;
+    return failureMessage.str();
+}
+
+} // namespace
+
+namespace UnitTest
+{
+XmlTestReporter::XmlTestReporter(ostream& ostream) : m_ostream(ostream) {}
+
+void XmlTestReporter::ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed)
+{
+    AddXmlElement(m_ostream, NULL);
+
+    BeginResults(m_ostream, totalTestCount, failedTestCount, failureCount, secondsElapsed);
+
+    DeferredTestResultList const& results = GetResults();
+    for (DeferredTestResultList::const_iterator i = results.begin(); i != results.end(); ++i)
+    {
+        BeginTest(m_ostream, *i);
+
+        if (i->failed) AddFailure(m_ostream, *i);
+
+        EndTest(m_ostream, *i);
+    }
+
+    EndResults(m_ostream);
+}
+
+void XmlTestReporter::AddXmlElement(ostream& os, char const* encoding)
+{
+    os << "<?xml version=\"1.0\"";
+
+    if (encoding != NULL) os << " encoding=\"" << encoding << "\"";
+
+    os << "?>";
+}
+
+void XmlTestReporter::BeginResults(
+    std::ostream& os, int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed)
+{
+    os << "<unittest-results"
+       << " tests=\"" << totalTestCount << "\""
+       << " failedtests=\"" << failedTestCount << "\""
+       << " failures=\"" << failureCount << "\""
+       << " time=\"" << secondsElapsed << "\""
+       << ">";
+}
+
+void XmlTestReporter::EndResults(std::ostream& os) { os << "</unittest-results>"; }
+
+void XmlTestReporter::BeginTest(std::ostream& os, DeferredTestResult const& result)
+{
+    os << "<test"
+       << " suite=\"" << result.suiteName << "\""
+       << " name=\"" << result.testName << "\""
+       << " time=\"" << result.timeElapsed << "\"";
+}
+
+void XmlTestReporter::EndTest(std::ostream& os, DeferredTestResult const& result)
+{
+    if (result.failed)
+        os << "</test>";
+    else
+        os << "/>";
+}
+
+void XmlTestReporter::AddFailure(std::ostream& os, DeferredTestResult const& result)
+{
+    os << ">"; // close <test> element
+
+    for (DeferredTestResult::FailureVec::const_iterator it = result.failures.begin(); it != result.failures.end(); ++it)
+    {
+        string const escapedMessage = XmlEscape(std::string(it->failureStr));
+        string const message = BuildFailureMessage(result.failureFile, it->lineNumber, escapedMessage);
+
+        os << "<failure"
+           << " message=\"" << message << "\""
+           << "/>";
+    }
+}
+
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/XmlTestReporter.h b/Release/tests/common/UnitTestpp/src/XmlTestReporter.h
new file mode 100644 (file)
index 0000000..6b72874
--- /dev/null
@@ -0,0 +1,71 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_XMLTESTREPORTER_H
+#define UNITTEST_XMLTESTREPORTER_H
+
+#include "../config.h"
+#ifndef UNITTEST_NO_DEFERRED_REPORTER
+
+#include "DeferredTestReporter.h"
+#include <iosfwd>
+
+namespace UnitTest
+{
+class XmlTestReporter : public DeferredTestReporter
+{
+public:
+    explicit UNITTEST_LINKAGE XmlTestReporter(std::ostream& ostream);
+
+    virtual UNITTEST_LINKAGE void ReportSummary(int totalTestCount,
+                                                int failedTestCount,
+                                                int failureCount,
+                                                float secondsElapsed);
+
+private:
+    XmlTestReporter(XmlTestReporter const&);
+    XmlTestReporter& operator=(XmlTestReporter const&);
+
+    void AddXmlElement(std::ostream& os, char const* encoding);
+    void BeginResults(
+        std::ostream& os, int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed);
+    void EndResults(std::ostream& os);
+    void BeginTest(std::ostream& os, DeferredTestResult const& result);
+    void AddFailure(std::ostream& os, DeferredTestResult const& result);
+    void EndTest(std::ostream& os, DeferredTestResult const& result);
+
+    std::ostream& m_ostream;
+};
+
+} // namespace UnitTest
+
+#endif
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/stdafx.cpp b/Release/tests/common/UnitTestpp/src/stdafx.cpp
new file mode 100644 (file)
index 0000000..82fd96b
--- /dev/null
@@ -0,0 +1,35 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+// stdafx.cpp :
+// Include the standard header and generate the precompiled header.
+
+#include "stdafx.h"
\ No newline at end of file
diff --git a/Release/tests/common/UnitTestpp/src/stdafx.h b/Release/tests/common/UnitTestpp/src/stdafx.h
new file mode 100644 (file)
index 0000000..99e91a1
--- /dev/null
@@ -0,0 +1,53 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#pragma once
+
+#include "../config.h"
+#include "AssertException.h"
+#include "CurrentTest.h"
+#include "DeferredTestReporter.h"
+#include "MemoryOutStream.h"
+#include "Test.h"
+#include "TestDetails.h"
+#include "TestList.h"
+#include "TestReporter.h"
+#include "TestReporterStdout.h"
+#include "TestResults.h"
+#include "TimeHelpers.h"
+#include <cstddef>
+#include <cstdio>
+#include <cstring>
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#endif
\ No newline at end of file
diff --git a/Release/tests/common/UnitTestpp/src/tests/RecordingReporter.h b/Release/tests/common/UnitTestpp/src/tests/RecordingReporter.h
new file mode 100644 (file)
index 0000000..c54ab29
--- /dev/null
@@ -0,0 +1,130 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_RECORDINGREPORTER_H
+#define UNITTEST_RECORDINGREPORTER_H
+
+#include "../TestDetails.h"
+#include "../TestReporter.h"
+#include <cstring>
+
+struct RecordingReporter : public UnitTest::TestReporter
+{
+private:
+    enum
+    {
+        kMaxStringLength = 256
+    };
+
+public:
+    RecordingReporter()
+        : testRunCount(0)
+        , testFailedCount(0)
+        , lastFailedLine(0)
+        , testFinishedCount(0)
+        , lastFinishedTestTime(0)
+        , summaryTotalTestCount(0)
+        , summaryFailedTestCount(0)
+        , summaryFailureCount(0)
+        , summarySecondsElapsed(0)
+    {
+        lastStartedSuite[0] = '\0';
+        lastStartedTest[0] = '\0';
+        lastFailedFile[0] = '\0';
+        lastFailedSuite[0] = '\0';
+        lastFailedTest[0] = '\0';
+        lastFailedMessage[0] = '\0';
+        lastFinishedSuite[0] = '\0';
+        lastFinishedTest[0] = '\0';
+    }
+
+    virtual void ReportTestStart(UnitTest::TestDetails const& test)
+    {
+        using namespace std;
+
+        ++testRunCount;
+        strcpy(lastStartedSuite, test.suiteName);
+        strcpy(lastStartedTest, test.testName);
+    }
+
+    virtual void ReportFailure(UnitTest::TestDetails const& test, char const* failure)
+    {
+        using namespace std;
+
+        ++testFailedCount;
+        strcpy(lastFailedFile, test.filename);
+        lastFailedLine = test.lineNumber;
+        strcpy(lastFailedSuite, test.suiteName);
+        strcpy(lastFailedTest, test.testName);
+        strcpy(lastFailedMessage, failure);
+    }
+
+    virtual void ReportTestFinish(UnitTest::TestDetails const& test, bool, float testDuration)
+    {
+        using namespace std;
+
+        ++testFinishedCount;
+        strcpy(lastFinishedSuite, test.suiteName);
+        strcpy(lastFinishedTest, test.testName);
+        lastFinishedTestTime = testDuration;
+    }
+
+    virtual void ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed)
+    {
+        summaryTotalTestCount = totalTestCount;
+        summaryFailedTestCount = failedTestCount;
+        summaryFailureCount = failureCount;
+        summarySecondsElapsed = secondsElapsed;
+    }
+
+    int testRunCount;
+    char lastStartedSuite[kMaxStringLength];
+    char lastStartedTest[kMaxStringLength];
+
+    int testFailedCount;
+    char lastFailedFile[kMaxStringLength];
+    int lastFailedLine;
+    char lastFailedSuite[kMaxStringLength];
+    char lastFailedTest[kMaxStringLength];
+    char lastFailedMessage[kMaxStringLength];
+
+    int testFinishedCount;
+    char lastFinishedSuite[kMaxStringLength];
+    char lastFinishedTest[kMaxStringLength];
+    float lastFinishedTestTime;
+
+    int summaryTotalTestCount;
+    int summaryFailedTestCount;
+    int summaryFailureCount;
+    float summarySecondsElapsed;
+};
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/tests/ScopedCurrentTest.h b/Release/tests/common/UnitTestpp/src/tests/ScopedCurrentTest.h
new file mode 100644 (file)
index 0000000..399c27c
--- /dev/null
@@ -0,0 +1,65 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTEST_SCOPEDCURRENTTEST_H
+#define UNITTEST_SCOPEDCURRENTTEST_H
+
+#include "../CurrentTest.h"
+#include <cstddef>
+
+class ScopedCurrentTest
+{
+public:
+    ScopedCurrentTest()
+        : m_oldTestResults(UnitTest::CurrentTest::Results()), m_oldTestDetails(UnitTest::CurrentTest::Details())
+    {
+    }
+
+    explicit ScopedCurrentTest(UnitTest::TestResults& newResults, const UnitTest::TestDetails* newDetails = NULL)
+        : m_oldTestResults(UnitTest::CurrentTest::Results()), m_oldTestDetails(UnitTest::CurrentTest::Details())
+    {
+        UnitTest::CurrentTest::Results() = &newResults;
+
+        if (newDetails != NULL) UnitTest::CurrentTest::Details() = newDetails;
+    }
+
+    ~ScopedCurrentTest()
+    {
+        UnitTest::CurrentTest::Results() = m_oldTestResults;
+        UnitTest::CurrentTest::Details() = m_oldTestDetails;
+    }
+
+private:
+    UnitTest::TestResults* m_oldTestResults;
+    const UnitTest::TestDetails* m_oldTestDetails;
+};
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestAssertHandler.cpp b/Release/tests/common/UnitTestpp/src/tests/TestAssertHandler.cpp
new file mode 100644 (file)
index 0000000..855e4a6
--- /dev/null
@@ -0,0 +1,159 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include "../AssertException.h"
+#include <csetjmp>
+
+using namespace UnitTest;
+
+namespace
+{
+TEST(CanSetAssertExpected)
+{
+    Detail::ExpectAssert(true);
+    CHECK(Detail::AssertExpected());
+
+    Detail::ExpectAssert(false);
+    CHECK(!Detail::AssertExpected());
+}
+
+#ifndef UNITTEST_NO_EXCEPTIONS
+
+TEST(ReportAssertThrowsAssertException)
+{
+    bool caught = false;
+
+    try
+    {
+        TestResults testResults;
+        TestDetails testDetails("", "", "", 0);
+        Detail::ReportAssertEx(&testResults, &testDetails, "", "", 0);
+    }
+    catch (AssertException const&)
+    {
+        caught = true;
+    }
+
+    CHECK(true == caught);
+}
+
+TEST(ReportAssertClearsExpectAssertFlag)
+{
+    RecordingReporter reporter;
+    TestResults testResults(&reporter);
+    TestDetails testDetails("", "", "", 0);
+
+    try
+    {
+        Detail::ExpectAssert(true);
+        Detail::ReportAssertEx(&testResults, &testDetails, "", "", 0);
+    }
+    catch (AssertException const&)
+    {
+    }
+
+    CHECK(Detail::AssertExpected() == false);
+    CHECK_EQUAL(0, reporter.testFailedCount);
+}
+
+TEST(ReportAssertWritesFailureToResultsAndDetailsWhenAssertIsNotExpected)
+{
+    const int lineNumber = 12345;
+    const char* description = "description";
+    const char* filename = "filename";
+
+    RecordingReporter reporter;
+    TestResults testResults(&reporter);
+    TestDetails testDetails("", "", "", 0);
+
+    try
+    {
+        Detail::ReportAssertEx(&testResults, &testDetails, description, filename, lineNumber);
+    }
+    catch (AssertException const&)
+    {
+    }
+
+    CHECK_EQUAL(description, reporter.lastFailedMessage);
+    CHECK_EQUAL(filename, reporter.lastFailedFile);
+    CHECK_EQUAL(lineNumber, reporter.lastFailedLine);
+}
+
+TEST(ReportAssertReportsNoErrorsWhenAssertIsExpected)
+{
+    Detail::ExpectAssert(true);
+
+    RecordingReporter reporter;
+    TestResults testResults(&reporter);
+    TestDetails testDetails("", "", "", 0);
+
+    try
+    {
+        Detail::ReportAssertEx(&testResults, &testDetails, "", "", 0);
+    }
+    catch (AssertException const&)
+    {
+    }
+
+    CHECK_EQUAL(0, reporter.testFailedCount);
+}
+
+TEST(CheckAssertMacroSetsAssertExpectationToFalseAfterRunning)
+{
+    Detail::ExpectAssert(true);
+    CHECK_ASSERT(ReportAssert("", "", 0));
+    CHECK(!Detail::AssertExpected());
+    Detail::ExpectAssert(false);
+}
+
+#else
+
+TEST(SetAssertJumpTargetReturnsFalseWhenSettingJumpTarget) { CHECK(UNITTEST_SET_ASSERT_JUMP_TARGET() == false); }
+
+TEST(JumpToAssertJumpTarget_JumpsToSetPoint_ReturnsTrue)
+{
+    const volatile bool taken = !!UNITTEST_SET_ASSERT_JUMP_TARGET();
+
+    volatile bool set = false;
+    if (taken == false)
+    {
+        UNITTEST_JUMP_TO_ASSERT_JUMP_TARGET();
+        set = true;
+    }
+
+    CHECK(set == false);
+}
+
+#endif
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestCheckMacros.cpp b/Release/tests/common/UnitTestpp/src/tests/TestCheckMacros.cpp
new file mode 100644 (file)
index 0000000..ec774e2
--- /dev/null
@@ -0,0 +1,550 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+using namespace std;
+
+namespace
+{
+TEST(CheckSucceedsOnTrue)
+{
+    bool failure = true;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK(true);
+
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(!failure);
+}
+
+TEST(CheckFailsOnFalse)
+{
+    bool failure = false;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK(false);
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(failure);
+}
+
+TEST(FailureReportsCorrectTestName)
+{
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK(false);
+    }
+
+    CHECK_EQUAL(m_details.testName, reporter.lastFailedTest);
+}
+
+TEST(CheckFailureIncludesCheckContents)
+{
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+        const bool yaddayadda = false;
+        CHECK(yaddayadda);
+    }
+
+    CHECK(strstr(reporter.lastFailedMessage, "yaddayadda"));
+}
+
+TEST(CheckEqualSucceedsOnEqual)
+{
+    bool failure = true;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK_EQUAL(1, 1);
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(!failure);
+}
+
+TEST(CheckEqualFailsOnNotEqual)
+{
+    bool failure = false;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK_EQUAL(1, 2);
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(failure);
+}
+
+TEST(CheckEqualFailureContainsCorrectDetails)
+{
+    int line = 0;
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        UnitTest::TestDetails const testDetails("testName", "suiteName", "filename", -1);
+        ScopedCurrentTest scopedResults(testResults, &testDetails);
+
+        CHECK_EQUAL(1, 123);
+        line = __LINE__;
+    }
+
+    CHECK_EQUAL("testName", reporter.lastFailedTest);
+    CHECK_EQUAL("suiteName", reporter.lastFailedSuite);
+    CHECK_EQUAL("filename", reporter.lastFailedFile);
+    CHECK_EQUAL(line, reporter.lastFailedLine);
+}
+
+int g_sideEffect = 0;
+int FunctionWithSideEffects()
+{
+    ++g_sideEffect;
+    return 1;
+}
+
+TEST(CheckEqualDoesNotHaveSideEffectsWhenPassing)
+{
+    g_sideEffect = 0;
+    {
+        UnitTest::TestResults testResults;
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK_EQUAL(1, FunctionWithSideEffects());
+    }
+    CHECK_EQUAL(1, g_sideEffect);
+}
+
+TEST(CheckEqualDoesNotHaveSideEffectsWhenFailing)
+{
+    g_sideEffect = 0;
+    {
+        UnitTest::TestResults testResults;
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK_EQUAL(2, FunctionWithSideEffects());
+    }
+    CHECK_EQUAL(1, g_sideEffect);
+}
+
+TEST(CheckCloseSucceedsOnEqual)
+{
+    bool failure = true;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK_CLOSE(1.0f, 1.001f, 0.01f);
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(!failure);
+}
+
+TEST(CheckCloseFailsOnNotEqual)
+{
+    bool failure = false;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK_CLOSE(1.0f, 1.1f, 0.01f);
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(failure);
+}
+
+TEST(CheckCloseFailureContainsCorrectDetails)
+{
+    int line = 0;
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        UnitTest::TestDetails testDetails("test", "suite", "filename", -1);
+        ScopedCurrentTest scopedResults(testResults, &testDetails);
+
+        CHECK_CLOSE(1.0f, 1.1f, 0.01f);
+        line = __LINE__;
+    }
+
+    CHECK_EQUAL("test", reporter.lastFailedTest);
+    CHECK_EQUAL("suite", reporter.lastFailedSuite);
+    CHECK_EQUAL("filename", reporter.lastFailedFile);
+    CHECK_EQUAL(line, reporter.lastFailedLine);
+}
+
+TEST(CheckCloseDoesNotHaveSideEffectsWhenPassing)
+{
+    g_sideEffect = 0;
+    {
+        UnitTest::TestResults testResults;
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK_CLOSE(1, FunctionWithSideEffects(), 0.1f);
+    }
+    CHECK_EQUAL(1, g_sideEffect);
+}
+
+TEST(CheckCloseDoesNotHaveSideEffectsWhenFailing)
+{
+    g_sideEffect = 0;
+    {
+        UnitTest::TestResults testResults;
+        ScopedCurrentTest scopedResults(testResults);
+        CHECK_CLOSE(2, FunctionWithSideEffects(), 0.1f);
+    }
+    CHECK_EQUAL(1, g_sideEffect);
+}
+
+TEST(CheckArrayCloseSucceedsOnEqual)
+{
+    bool failure = true;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+        const float data[4] = {0, 1, 2, 3};
+        CHECK_ARRAY_CLOSE(data, data, 4, 0.01f);
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(!failure);
+}
+
+TEST(CheckArrayCloseFailsOnNotEqual)
+{
+    bool failure = false;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        int const data1[4] = {0, 1, 2, 3};
+        int const data2[4] = {0, 1, 3, 3};
+        CHECK_ARRAY_CLOSE(data1, data2, 4, 0.01f);
+
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(failure);
+}
+
+TEST(CheckArrayCloseFailureIncludesCheckExpectedAndActual)
+{
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        int const data1[4] = {0, 1, 2, 3};
+        int const data2[4] = {0, 1, 3, 3};
+        CHECK_ARRAY_CLOSE(data1, data2, 4, 0.01f);
+    }
+
+    CHECK(strstr(reporter.lastFailedMessage, "xpected [ 0 1 2 3 ]"));
+    CHECK(strstr(reporter.lastFailedMessage, "was [ 0 1 3 3 ]"));
+}
+
+TEST(CheckArrayCloseFailureContainsCorrectDetails)
+{
+    int line = 0;
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        UnitTest::TestDetails testDetails("arrayCloseTest", "arrayCloseSuite", "filename", -1);
+        ScopedCurrentTest scopedResults(testResults, &testDetails);
+
+        int const data1[4] = {0, 1, 2, 3};
+        int const data2[4] = {0, 1, 3, 3};
+        CHECK_ARRAY_CLOSE(data1, data2, 4, 0.01f);
+        line = __LINE__;
+    }
+
+    CHECK_EQUAL("arrayCloseTest", reporter.lastFailedTest);
+    CHECK_EQUAL("arrayCloseSuite", reporter.lastFailedSuite);
+    CHECK_EQUAL("filename", reporter.lastFailedFile);
+    CHECK_EQUAL(line, reporter.lastFailedLine);
+}
+
+TEST(CheckArrayCloseFailureIncludesTolerance)
+{
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        float const data1[4] = {0, 1, 2, 3};
+        float const data2[4] = {0, 1, 3, 3};
+        CHECK_ARRAY_CLOSE(data1, data2, 4, 0.01f);
+    }
+
+    CHECK(strstr(reporter.lastFailedMessage, "0.01"));
+}
+
+TEST(CheckArrayEqualSuceedsOnEqual)
+{
+    bool failure = true;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        const float data[4] = {0, 1, 2, 3};
+        CHECK_ARRAY_EQUAL(data, data, 4);
+
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(!failure);
+}
+
+TEST(CheckArrayEqualFailsOnNotEqual)
+{
+    bool failure = false;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        int const data1[4] = {0, 1, 2, 3};
+        int const data2[4] = {0, 1, 3, 3};
+        CHECK_ARRAY_EQUAL(data1, data2, 4);
+
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(failure);
+}
+
+TEST(CheckArrayEqualFailureIncludesCheckExpectedAndActual)
+{
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        int const data1[4] = {0, 1, 2, 3};
+        int const data2[4] = {0, 1, 3, 3};
+        CHECK_ARRAY_EQUAL(data1, data2, 4);
+    }
+
+    CHECK(strstr(reporter.lastFailedMessage, "xpected [ 0 1 2 3 ]"));
+    CHECK(strstr(reporter.lastFailedMessage, "was [ 0 1 3 3 ]"));
+}
+
+TEST(CheckArrayEqualFailureContainsCorrectInfo)
+{
+    int line = 0;
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        int const data1[4] = {0, 1, 2, 3};
+        int const data2[4] = {0, 1, 3, 3};
+        CHECK_ARRAY_EQUAL(data1, data2, 4);
+        line = __LINE__;
+    }
+
+    CHECK_EQUAL("CheckArrayEqualFailureContainsCorrectInfo", reporter.lastFailedTest);
+    CHECK_EQUAL(__FILE__, reporter.lastFailedFile);
+    CHECK_EQUAL(line, reporter.lastFailedLine);
+}
+
+float const* FunctionWithSideEffects2()
+{
+    ++g_sideEffect;
+    static float const data[] = {1, 2, 3, 4};
+    return data;
+}
+
+TEST(CheckArrayCloseDoesNotHaveSideEffectsWhenPassing)
+{
+    g_sideEffect = 0;
+    {
+        UnitTest::TestResults testResults;
+        ScopedCurrentTest scopedResults(testResults);
+
+        const float data[] = {0, 1, 2, 3};
+        CHECK_ARRAY_CLOSE(data, FunctionWithSideEffects2(), 4, 0.01f);
+    }
+    CHECK_EQUAL(1, g_sideEffect);
+}
+
+TEST(CheckArrayCloseDoesNotHaveSideEffectsWhenFailing)
+{
+    g_sideEffect = 0;
+    {
+        UnitTest::TestResults testResults;
+        ScopedCurrentTest scopedResults(testResults);
+
+        const float data[] = {0, 1, 3, 3};
+        CHECK_ARRAY_CLOSE(data, FunctionWithSideEffects2(), 4, 0.01f);
+    }
+
+    CHECK_EQUAL(1, g_sideEffect);
+}
+
+TEST(CheckArray2DCloseSucceedsOnEqual)
+{
+    bool failure = true;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        const float data[2][2] = {{0, 1}, {2, 3}};
+        CHECK_ARRAY2D_CLOSE(data, data, 2, 2, 0.01f);
+
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(!failure);
+}
+
+TEST(CheckArray2DCloseFailsOnNotEqual)
+{
+    bool failure = false;
+    {
+        RecordingReporter reporter;
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        int const data1[2][2] = {{0, 1}, {2, 3}};
+        int const data2[2][2] = {{0, 1}, {3, 3}};
+        CHECK_ARRAY2D_CLOSE(data1, data2, 2, 2, 0.01f);
+
+        failure = (testResults.GetFailureCount() > 0);
+    }
+
+    CHECK(failure);
+}
+
+TEST(CheckArray2DCloseFailureIncludesCheckExpectedAndActual)
+{
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        int const data1[2][2] = {{0, 1}, {2, 3}};
+        int const data2[2][2] = {{0, 1}, {3, 3}};
+
+        CHECK_ARRAY2D_CLOSE(data1, data2, 2, 2, 0.01f);
+    }
+
+    CHECK(strstr(reporter.lastFailedMessage, "xpected [ [ 0 1 ] [ 2 3 ] ]"));
+    CHECK(strstr(reporter.lastFailedMessage, "was [ [ 0 1 ] [ 3 3 ] ]"));
+}
+
+TEST(CheckArray2DCloseFailureContainsCorrectDetails)
+{
+    int line = 0;
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        UnitTest::TestDetails testDetails("array2DCloseTest", "array2DCloseSuite", "filename", -1);
+        ScopedCurrentTest scopedResults(testResults, &testDetails);
+
+        int const data1[2][2] = {{0, 1}, {2, 3}};
+        int const data2[2][2] = {{0, 1}, {3, 3}};
+        CHECK_ARRAY2D_CLOSE(data1, data2, 2, 2, 0.01f);
+        line = __LINE__;
+    }
+
+    CHECK_EQUAL("array2DCloseTest", reporter.lastFailedTest);
+    CHECK_EQUAL("array2DCloseSuite", reporter.lastFailedSuite);
+    CHECK_EQUAL("filename", reporter.lastFailedFile);
+    CHECK_EQUAL(line, reporter.lastFailedLine);
+}
+
+TEST(CheckArray2DCloseFailureIncludesTolerance)
+{
+    RecordingReporter reporter;
+    {
+        UnitTest::TestResults testResults(&reporter);
+        ScopedCurrentTest scopedResults(testResults);
+
+        float const data1[2][2] = {{0, 1}, {2, 3}};
+        float const data2[2][2] = {{0, 1}, {3, 3}};
+        CHECK_ARRAY2D_CLOSE(data1, data2, 2, 2, 0.01f);
+    }
+
+    CHECK(strstr(reporter.lastFailedMessage, "0.01"));
+}
+
+float const* const* FunctionWithSideEffects3()
+{
+    ++g_sideEffect;
+    static float const data1[] = {0, 1};
+    static float const data2[] = {2, 3};
+    static const float* const data[] = {data1, data2};
+    return data;
+}
+
+TEST(CheckArray2DCloseDoesNotHaveSideEffectsWhenPassing)
+{
+    g_sideEffect = 0;
+    {
+        UnitTest::TestResults testResults;
+        ScopedCurrentTest scopedResults(testResults);
+
+        const float data[2][2] = {{0, 1}, {2, 3}};
+        CHECK_ARRAY2D_CLOSE(data, FunctionWithSideEffects3(), 2, 2, 0.01f);
+    }
+    CHECK_EQUAL(1, g_sideEffect);
+}
+
+TEST(CheckArray2DCloseDoesNotHaveSideEffectsWhenFailing)
+{
+    g_sideEffect = 0;
+    {
+        UnitTest::TestResults testResults;
+        ScopedCurrentTest scopedResults(testResults);
+
+        const float data[2][2] = {{0, 1}, {3, 3}};
+        CHECK_ARRAY2D_CLOSE(data, FunctionWithSideEffects3(), 2, 2, 0.01f);
+    }
+    CHECK_EQUAL(1, g_sideEffect);
+}
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestChecks.cpp b/Release/tests/common/UnitTestpp/src/tests/TestChecks.cpp
new file mode 100644 (file)
index 0000000..077aaec
--- /dev/null
@@ -0,0 +1,300 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+using namespace UnitTest;
+
+namespace
+{
+TEST(CheckEqualWithUnsignedLong)
+{
+    TestResults results;
+    unsigned long something = 2;
+    CHECK_EQUAL(something, something);
+}
+
+TEST(CheckEqualsWithStringsFailsOnDifferentStrings)
+{
+    char txt1[] = "Hello";
+    char txt2[] = "Hallo";
+    TestResults results;
+    CheckEqual(results, "txt1", "txt2", txt1, txt2, TestDetails("", "", "", 0));
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+char txt1[] = "Hello"; // non-const on purpose so no folding of duplicate data
+char txt2[] = "Hello";
+
+TEST(CheckEqualsWithStringsWorksOnContentsNonConstNonConst)
+{
+    char const* const p1 = txt1;
+    char const* const p2 = txt2;
+    TestResults results;
+    CheckEqual(results, "p1", "p2", p1, p2, TestDetails("", "", "", 0));
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(CheckEqualsWithStringsWorksOnContentsConstConst)
+{
+    char* const p1 = txt1;
+    char* const p2 = txt2;
+    TestResults results;
+    CheckEqual(results, "p1", "p2", p1, p2, TestDetails("", "", "", 0));
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(CheckEqualsWithStringsWorksOnContentsNonConstConst)
+{
+    char* const p1 = txt1;
+    char const* const p2 = txt2;
+    TestResults results;
+    CheckEqual(results, "p1", "p2", p1, p2, TestDetails("", "", "", 0));
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(CheckEqualsWithStringsWorksOnContentsConstNonConst)
+{
+    char const* const p1 = txt1;
+    char* const p2 = txt2;
+    TestResults results;
+    CheckEqual(results, "p1", "p2", p1, p2, TestDetails("", "", "", 0));
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(CheckEqualsWithStringsWorksOnContentsWithALiteral)
+{
+    char const* const p1 = txt1;
+    TestResults results;
+    CheckEqual(results, "Hello", "p1", "Hello", p1, TestDetails("", "", "", 0));
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(CheckEqualFailureIncludesCheckExpectedAndActual)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+    const int something = 2;
+    CheckEqual(results, "1", "something", 1, something, TestDetails("", "", "", 0));
+
+    using namespace std;
+    CHECK(strstr(reporter.lastFailedMessage, "1=1"));
+    CHECK(strstr(reporter.lastFailedMessage, "something=2"));
+}
+
+TEST(CheckEqualFailureIncludesDetails)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+    TestDetails const details("mytest", "mysuite", "file.h", 101);
+
+    CheckEqual(results, "1", "2", 1, 2, details);
+
+    CHECK_EQUAL("mytest", reporter.lastFailedTest);
+    CHECK_EQUAL("mysuite", reporter.lastFailedSuite);
+    CHECK_EQUAL("file.h", reporter.lastFailedFile);
+    CHECK_EQUAL(101, reporter.lastFailedLine);
+}
+
+TEST(CheckCloseTrue)
+{
+    TestResults results;
+    CheckClose(results, 3.001f, 3.0f, 0.1f, TestDetails("", "", "", 0));
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(CheckCloseFalse)
+{
+    TestResults results;
+    CheckClose(results, 3.12f, 3.0f, 0.1f, TestDetails("", "", "", 0));
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+TEST(CheckCloseWithZeroEpsilonWorksForSameNumber)
+{
+    TestResults results;
+    CheckClose(results, 0.1f, 0.1f, 0, TestDetails("", "", "", 0));
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(CheckCloseWithNaNFails)
+{
+    const unsigned int bitpattern = 0xFFFFFFFF;
+    float nan;
+    std::memcpy(&nan, &bitpattern, sizeof(bitpattern));
+
+    TestResults results;
+    CheckClose(results, 3.0f, nan, 0.1f, TestDetails("", "", "", 0));
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+TEST(CheckCloseWithNaNAgainstItselfFails)
+{
+    const unsigned int bitpattern = 0xFFFFFFFF;
+    float nan;
+    std::memcpy(&nan, &bitpattern, sizeof(bitpattern));
+
+    TestResults results;
+    CheckClose(results, nan, nan, 0.1f, TestDetails("", "", "", 0));
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+TEST(CheckCloseFailureIncludesCheckExpectedAndActual)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+    const float expected = 0.9f;
+    const float actual = 1.1f;
+    CheckClose(results, expected, actual, 0.01f, TestDetails("", "", "", 0));
+
+    using namespace std;
+    CHECK(strstr(reporter.lastFailedMessage, "xpected 0.9"));
+    CHECK(strstr(reporter.lastFailedMessage, "was 1.1"));
+}
+
+TEST(CheckCloseFailureIncludesTolerance)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+    CheckClose(results, 2, 3, 0.01f, TestDetails("", "", "", 0));
+
+    using namespace std;
+    CHECK(strstr(reporter.lastFailedMessage, "0.01"));
+}
+
+TEST(CheckCloseFailureIncludesDetails)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+    TestDetails const details("mytest", "mysuite", "header.h", 10);
+
+    CheckClose(results, 2, 3, 0.01f, details);
+
+    CHECK_EQUAL("mytest", reporter.lastFailedTest);
+    CHECK_EQUAL("mysuite", reporter.lastFailedSuite);
+    CHECK_EQUAL("header.h", reporter.lastFailedFile);
+    CHECK_EQUAL(10, reporter.lastFailedLine);
+}
+
+TEST(CheckArrayEqualTrue)
+{
+    TestResults results;
+
+    int const array[3] = {1, 2, 3};
+    CheckArrayEqual(results, array, array, 3, TestDetails("", "", "", 0));
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(CheckArrayEqualFalse)
+{
+    TestResults results;
+
+    int const array1[3] = {1, 2, 3};
+    int const array2[3] = {1, 2, 2};
+    CheckArrayEqual(results, array1, array2, 3, TestDetails("", "", "", 0));
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+TEST(CheckArrayCloseTrue)
+{
+    TestResults results;
+
+    float const array1[3] = {1.0f, 1.5f, 2.0f};
+    float const array2[3] = {1.01f, 1.51f, 2.01f};
+    CheckArrayClose(results, array1, array2, 3, 0.02f, TestDetails("", "", "", 0));
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(CheckArrayCloseFalse)
+{
+    TestResults results;
+
+    float const array1[3] = {1.0f, 1.5f, 2.0f};
+    float const array2[3] = {1.01f, 1.51f, 2.01f};
+    CheckArrayClose(results, array1, array2, 3, 0.001f, TestDetails("", "", "", 0));
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+TEST(CheckArrayCloseFailureIncludesDetails)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+    TestDetails const details("arrayCloseTest", "arrayCloseSuite", "file", 1337);
+
+    float const array1[3] = {1.0f, 1.5f, 2.0f};
+    float const array2[3] = {1.01f, 1.51f, 2.01f};
+    CheckArrayClose(results, array1, array2, 3, 0.001f, details);
+
+    CHECK_EQUAL("arrayCloseTest", reporter.lastFailedTest);
+    CHECK_EQUAL("arrayCloseSuite", reporter.lastFailedSuite);
+    CHECK_EQUAL("file", reporter.lastFailedFile);
+    CHECK_EQUAL(1337, reporter.lastFailedLine);
+}
+
+TEST(CheckArray2DCloseTrue)
+{
+    TestResults results;
+
+    float const array1[3][3] = {{1.0f, 1.5f, 2.0f}, {2.0f, 2.5f, 3.0f}, {3.0f, 3.5f, 4.0f}};
+    float const array2[3][3] = {{1.01f, 1.51f, 2.01f}, {2.01f, 2.51f, 3.01f}, {3.01f, 3.51f, 4.01f}};
+    CheckArray2DClose(results, array1, array2, 3, 3, 0.02f, TestDetails("", "", "", 0));
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(CheckArray2DCloseFalse)
+{
+    TestResults results;
+
+    float const array1[3][3] = {{1.0f, 1.5f, 2.0f}, {2.0f, 2.5f, 3.0f}, {3.0f, 3.5f, 4.0f}};
+    float const array2[3][3] = {{1.01f, 1.51f, 2.01f}, {2.01f, 2.51f, 3.01f}, {3.01f, 3.51f, 4.01f}};
+    CheckArray2DClose(results, array1, array2, 3, 3, 0.001f, TestDetails("", "", "", 0));
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+TEST(CheckCloseWithDoublesSucceeds) { CHECK_CLOSE(0.5, 0.5, 0.0001); }
+
+TEST(CheckArray2DCloseFailureIncludesDetails)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+    TestDetails const details("array2DCloseTest", "array2DCloseSuite", "file", 1234);
+
+    float const array1[3][3] = {{1.0f, 1.5f, 2.0f}, {2.0f, 2.5f, 3.0f}, {3.0f, 3.5f, 4.0f}};
+    float const array2[3][3] = {{1.01f, 1.51f, 2.01f}, {2.01f, 2.51f, 3.01f}, {3.01f, 3.51f, 4.01f}};
+    CheckArray2DClose(results, array1, array2, 3, 3, 0.001f, details);
+
+    CHECK_EQUAL("array2DCloseTest", reporter.lastFailedTest);
+    CHECK_EQUAL("array2DCloseSuite", reporter.lastFailedSuite);
+    CHECK_EQUAL("file", reporter.lastFailedFile);
+    CHECK_EQUAL(1234, reporter.lastFailedLine);
+}
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestCompositeTestReporter.cpp b/Release/tests/common/UnitTestpp/src/tests/TestCompositeTestReporter.cpp
new file mode 100644 (file)
index 0000000..9681092
--- /dev/null
@@ -0,0 +1,202 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include "../CompositeTestReporter.h"
+
+using namespace UnitTest;
+
+namespace
+{
+TEST(ZeroReportersByDefault) { CHECK_EQUAL(0, CompositeTestReporter().GetReporterCount()); }
+
+struct MockReporter : TestReporter
+{
+    MockReporter()
+        : testStartCalled(false)
+        , testStartDetails(NULL)
+        , failureCalled(false)
+        , failureDetails(NULL)
+        , failureStr(NULL)
+        , testFinishCalled(false)
+        , testFinishDetails(NULL)
+        , testFinishSecondsElapsed(-1.0f)
+        , summaryCalled(false)
+        , summaryTotalTestCount(-1)
+        , summaryFailureCount(-1)
+        , summarySecondsElapsed(-1.0f)
+    {
+    }
+
+    virtual void ReportTestStart(TestDetails const& test)
+    {
+        testStartCalled = true;
+        testStartDetails = &test;
+    }
+
+    virtual void ReportFailure(TestDetails const& test, char const* failure)
+    {
+        failureCalled = true;
+        failureDetails = &test;
+        failureStr = failure;
+    }
+
+    virtual void ReportTestFinish(TestDetails const& test, bool, float secondsElapsed)
+    {
+        testFinishCalled = true;
+        testFinishDetails = &test;
+        testFinishSecondsElapsed = secondsElapsed;
+    }
+
+    virtual void ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed)
+    {
+        summaryCalled = true;
+        summaryTotalTestCount = totalTestCount;
+        summaryFailedTestCount = failedTestCount;
+        summaryFailureCount = failureCount;
+        summarySecondsElapsed = secondsElapsed;
+    }
+
+    bool testStartCalled;
+    TestDetails const* testStartDetails;
+
+    bool failureCalled;
+    TestDetails const* failureDetails;
+    const char* failureStr;
+
+    bool testFinishCalled;
+    TestDetails const* testFinishDetails;
+    float testFinishSecondsElapsed;
+
+    bool summaryCalled;
+    int summaryTotalTestCount;
+    int summaryFailedTestCount;
+    int summaryFailureCount;
+    float summarySecondsElapsed;
+};
+
+TEST(AddReporter)
+{
+    MockReporter r;
+    CompositeTestReporter c;
+
+    CHECK(c.AddReporter(&r));
+    CHECK_EQUAL(1, c.GetReporterCount());
+}
+
+TEST(RemoveReporter)
+{
+    MockReporter r;
+    CompositeTestReporter c;
+
+    c.AddReporter(&r);
+    CHECK(c.RemoveReporter(&r));
+    CHECK_EQUAL(0, c.GetReporterCount());
+}
+
+struct Fixture
+{
+    Fixture()
+    {
+        c.AddReporter(&r0);
+        c.AddReporter(&r1);
+    }
+
+    MockReporter r0, r1;
+    CompositeTestReporter c;
+};
+
+TEST_FIXTURE(Fixture, ReportTestStartCallsReportTestStartOnAllAggregates)
+{
+    TestDetails t("", "", "", 0);
+    c.ReportTestStart(t);
+
+    CHECK(r0.testStartCalled);
+    CHECK_EQUAL(&t, r0.testStartDetails);
+    CHECK(r1.testStartCalled);
+    CHECK_EQUAL(&t, r1.testStartDetails);
+}
+
+TEST_FIXTURE(Fixture, ReportFailureCallsReportFailureOnAllAggregates)
+{
+    TestDetails t("", "", "", 0);
+    const char* failStr = "fail";
+    c.ReportFailure(t, failStr);
+
+    CHECK(r0.failureCalled);
+    CHECK_EQUAL(&t, r0.failureDetails);
+    CHECK_EQUAL(failStr, r0.failureStr);
+
+    CHECK(r1.failureCalled);
+    CHECK_EQUAL(&t, r1.failureDetails);
+    CHECK_EQUAL(failStr, r1.failureStr);
+}
+
+TEST_FIXTURE(Fixture, ReportTestFinishCallsReportTestFinishOnAllAggregates)
+{
+    TestDetails t("", "", "", 0);
+    const float s = 1.2345f;
+    c.ReportTestFinish(t, true, s);
+
+    CHECK(r0.testFinishCalled);
+    CHECK_EQUAL(&t, r0.testFinishDetails);
+    CHECK_CLOSE(s, r0.testFinishSecondsElapsed, 0.00001f);
+
+    CHECK(r1.testFinishCalled);
+    CHECK_EQUAL(&t, r1.testFinishDetails);
+    CHECK_CLOSE(s, r1.testFinishSecondsElapsed, 0.00001f);
+}
+
+TEST_FIXTURE(Fixture, ReportSummaryCallsReportSummaryOnAllAggregates)
+{
+    TestDetails t("", "", "", 0);
+    const int testCount = 3;
+    const int failedTestCount = 4;
+    const int failureCount = 5;
+    const float secondsElapsed = 3.14159f;
+
+    c.ReportSummary(testCount, failedTestCount, failureCount, secondsElapsed);
+
+    CHECK(r0.summaryCalled);
+    CHECK_EQUAL(testCount, r0.summaryTotalTestCount);
+    CHECK_EQUAL(failedTestCount, r0.summaryFailedTestCount);
+    CHECK_EQUAL(failureCount, r0.summaryFailureCount);
+    CHECK_CLOSE(secondsElapsed, r0.summarySecondsElapsed, 0.00001f);
+
+    CHECK(r1.summaryCalled);
+    CHECK_EQUAL(testCount, r1.summaryTotalTestCount);
+    CHECK_EQUAL(failedTestCount, r1.summaryFailedTestCount);
+    CHECK_EQUAL(failureCount, r1.summaryFailureCount);
+    CHECK_CLOSE(secondsElapsed, r1.summarySecondsElapsed, 0.00001f);
+}
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestCurrentTest.cpp b/Release/tests/common/UnitTestpp/src/tests/TestCurrentTest.cpp
new file mode 100644 (file)
index 0000000..e0f2076
--- /dev/null
@@ -0,0 +1,66 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+namespace
+{
+TEST(CanSetandGetDetails)
+{
+    bool ok = false;
+    {
+        ScopedCurrentTest scopedTest;
+
+        const UnitTest::TestDetails* details = reinterpret_cast<const UnitTest::TestDetails*>(12345);
+        UnitTest::CurrentTest::Details() = details;
+
+        ok = (UnitTest::CurrentTest::Details() == details);
+    }
+
+    CHECK(ok);
+}
+
+TEST(CanSetAndGetResults)
+{
+    bool ok = false;
+    {
+        ScopedCurrentTest scopedTest;
+
+        UnitTest::TestResults results;
+        UnitTest::CurrentTest::Results() = &results;
+
+        ok = (UnitTest::CurrentTest::Results() == &results);
+    }
+
+    CHECK(ok);
+}
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestDeferredTestReporter.cpp b/Release/tests/common/UnitTestpp/src/tests/TestDeferredTestReporter.cpp
new file mode 100644 (file)
index 0000000..6e8bfe8
--- /dev/null
@@ -0,0 +1,148 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#ifndef UNITTEST_NO_DEFERRED_REPORTER
+
+#include "../DeferredTestReporter.h"
+
+namespace UnitTest
+{
+namespace
+{
+#ifndef UNITTEST_MEMORYOUTSTREAM_IS_STD_OSTRINGSTREAM
+MemoryOutStream& operator<<(MemoryOutStream& lhs, const std::string& rhs)
+{
+    lhs << rhs.c_str();
+    return lhs;
+}
+#endif
+
+struct MockDeferredTestReporter : public DeferredTestReporter
+{
+    virtual void ReportSummary(int, int, int, float) {}
+};
+
+struct DeferredTestReporterFixture
+{
+    DeferredTestReporterFixture()
+        : testName("UniqueTestName")
+        , testSuite("UniqueTestSuite")
+        , fileName("filename.h")
+        , lineNumber(12)
+        , details(testName.c_str(), testSuite.c_str(), fileName.c_str(), lineNumber)
+    {
+    }
+
+    MockDeferredTestReporter reporter;
+    std::string const testName;
+    std::string const testSuite;
+    std::string const fileName;
+    int const lineNumber;
+    TestDetails const details;
+};
+
+TEST_FIXTURE(DeferredTestReporterFixture, ReportTestStartCreatesANewDeferredTest)
+{
+    reporter.ReportTestStart(details);
+    CHECK_EQUAL(1, (int)reporter.GetResults().size());
+}
+
+TEST_FIXTURE(DeferredTestReporterFixture, ReportTestStartCapturesTestNameAndSuite)
+{
+    reporter.ReportTestStart(details);
+
+    DeferredTestResult const& result = reporter.GetResults().at(0);
+    CHECK_EQUAL(testName.c_str(), result.testName);
+    CHECK_EQUAL(testSuite.c_str(), result.suiteName);
+}
+
+TEST_FIXTURE(DeferredTestReporterFixture, ReportTestEndCapturesTestTime)
+{
+    float const elapsed = 123.45f;
+    reporter.ReportTestStart(details);
+    reporter.ReportTestFinish(details, true, elapsed);
+
+    DeferredTestResult const& result = reporter.GetResults().at(0);
+    CHECK_CLOSE(elapsed, result.timeElapsed, 0.0001f);
+}
+
+TEST_FIXTURE(DeferredTestReporterFixture, ReportFailureSavesFailureDetails)
+{
+    char const* failure = "failure";
+
+    reporter.ReportTestStart(details);
+    reporter.ReportFailure(details, failure);
+
+    DeferredTestResult const& result = reporter.GetResults().at(0);
+    CHECK(result.failed == true);
+    CHECK_EQUAL(fileName.c_str(), result.failureFile);
+}
+
+TEST_FIXTURE(DeferredTestReporterFixture, ReportFailureSavesFailureDetailsForMultipleFailures)
+{
+    char const* failure1 = "failure 1";
+    char const* failure2 = "failure 2";
+
+    reporter.ReportTestStart(details);
+    reporter.ReportFailure(details, failure1);
+    reporter.ReportFailure(details, failure2);
+
+    DeferredTestResult const& result = reporter.GetResults().at(0);
+    CHECK_EQUAL(2, (int)result.failures.size());
+    CHECK_EQUAL(failure1, result.failures[0].failureStr);
+    CHECK_EQUAL(failure2, result.failures[1].failureStr);
+}
+
+TEST_FIXTURE(DeferredTestReporterFixture, DeferredTestReporterTakesCopyOfFailureMessage)
+{
+    reporter.ReportTestStart(details);
+
+    char failureMessage[128];
+    char const* goodStr = "Real failure message";
+    char const* badStr = "Bogus failure message";
+
+    using namespace std;
+
+    strcpy(failureMessage, goodStr);
+    reporter.ReportFailure(details, failureMessage);
+    strcpy(failureMessage, badStr);
+
+    DeferredTestResult const& result = reporter.GetResults().at(0);
+    DeferredTestFailure const& failure = result.failures.at(0);
+    CHECK_EQUAL(goodStr, failure.failureStr);
+}
+
+} // namespace
+} // namespace UnitTest
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestMemoryOutStream.cpp b/Release/tests/common/UnitTestpp/src/tests/TestMemoryOutStream.cpp
new file mode 100644 (file)
index 0000000..79aa88c
--- /dev/null
@@ -0,0 +1,208 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include "../MemoryOutStream.h"
+#include <climits>
+#include <cstdlib>
+
+using namespace UnitTest;
+using namespace std;
+
+namespace
+{
+TEST(DefaultIsEmptyString)
+{
+    MemoryOutStream const stream;
+    CHECK(stream.GetText() != 0);
+    CHECK_EQUAL("", stream.GetText());
+}
+
+TEST(StreamingTextCopiesCharacters)
+{
+    MemoryOutStream stream;
+    stream << "Lalala";
+    CHECK_EQUAL("Lalala", stream.GetText());
+}
+
+TEST(StreamingMultipleTimesConcatenatesResult)
+{
+    MemoryOutStream stream;
+    stream << "Bork"
+           << "To"
+           << "Fred";
+    CHECK_EQUAL("BorkToFred", stream.GetText());
+}
+
+TEST(StreamingIntWritesCorrectCharacters)
+{
+    MemoryOutStream stream;
+    stream << (int)123;
+    CHECK_EQUAL("123", stream.GetText());
+}
+
+TEST(StreamingUnsignedIntWritesCorrectCharacters)
+{
+    MemoryOutStream stream;
+    stream << (unsigned int)123;
+    CHECK_EQUAL("123", stream.GetText());
+}
+
+TEST(StreamingLongWritesCorrectCharacters)
+{
+    MemoryOutStream stream;
+    stream << (long)(-123);
+    CHECK_EQUAL("-123", stream.GetText());
+}
+
+TEST(StreamingUnsignedLongWritesCorrectCharacters)
+{
+    MemoryOutStream stream;
+    stream << (unsigned long)123;
+    CHECK_EQUAL("123", stream.GetText());
+}
+
+TEST(StreamingLongLongWritesCorrectCharacters)
+{
+    MemoryOutStream stream;
+    stream << (long long)(ULONG_MAX)*2;
+    CHECK_EQUAL("8589934590", stream.GetText());
+}
+
+TEST(StreamingUnsignedLongLongWritesCorrectCharacters)
+{
+    MemoryOutStream stream;
+    stream << (unsigned long long)(ULONG_MAX)*2;
+    CHECK_EQUAL("8589934590", stream.GetText());
+}
+
+TEST(StreamingFloatWritesCorrectCharacters)
+{
+    MemoryOutStream stream;
+    stream << 3.1415f;
+    CHECK(strstr(stream.GetText(), "3.1415"));
+}
+
+TEST(StreamingDoubleWritesCorrectCharacters)
+{
+    MemoryOutStream stream;
+    stream << 3.1415;
+    CHECK(strstr(stream.GetText(), "3.1415"));
+}
+
+TEST(StreamingPointerWritesCorrectCharacters)
+{
+    MemoryOutStream stream;
+    int* p = (int*)0x1234;
+    stream << p;
+    CHECK(strstr(stream.GetText(), "1234"));
+}
+
+TEST(StreamingSizeTWritesCorrectCharacters)
+{
+    MemoryOutStream stream;
+    size_t const s = 53124;
+    stream << s;
+    CHECK_EQUAL("53124", stream.GetText());
+}
+
+TEST(ClearEmptiesMemoryOutStreamContents)
+{
+    MemoryOutStream stream;
+    stream << "Hello world";
+    stream.Clear();
+    CHECK_EQUAL("", stream.GetText());
+}
+
+#ifndef UNITTEST_MEMORYOUTSTREAM_IS_STD_OSTRINGSTREAM
+
+TEST(StreamInitialCapacityIsCorrect)
+{
+    MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE);
+    CHECK_EQUAL((int)MemoryOutStream::GROW_CHUNK_SIZE, stream.GetCapacity());
+}
+
+TEST(StreamInitialCapacityIsMultipleOfGrowChunkSize)
+{
+    MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE + 1);
+    CHECK_EQUAL((int)MemoryOutStream::GROW_CHUNK_SIZE * 2, stream.GetCapacity());
+}
+
+TEST(ExceedingCapacityGrowsBuffer)
+{
+    MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE);
+    stream << "012345678901234567890123456789";
+    char const* const oldBuffer = stream.GetText();
+    stream << "0123456789";
+    CHECK(oldBuffer != stream.GetText());
+}
+
+TEST(ExceedingCapacityGrowsBufferByGrowChunk)
+{
+    MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE);
+    stream << "0123456789012345678901234567890123456789";
+    CHECK_EQUAL(MemoryOutStream::GROW_CHUNK_SIZE * 2, stream.GetCapacity());
+}
+
+TEST(WritingStringLongerThanCapacityFitsInNewBuffer)
+{
+    MemoryOutStream stream(8);
+    stream << "0123456789ABCDEF";
+    CHECK_EQUAL("0123456789ABCDEF", stream.GetText());
+}
+
+TEST(WritingIntLongerThanCapacityFitsInNewBuffer)
+{
+    MemoryOutStream stream(8);
+    stream << "aaaa" << 123456;
+    ;
+    CHECK_EQUAL("aaaa123456", stream.GetText());
+}
+
+TEST(WritingFloatLongerThanCapacityFitsInNewBuffer)
+{
+    MemoryOutStream stream(8);
+    stream << "aaaa" << 123456.0f;
+    ;
+    CHECK_EQUAL("aaaa123456.000000f", stream.GetText());
+}
+
+TEST(WritingSizeTLongerThanCapacityFitsInNewBuffer)
+{
+    MemoryOutStream stream(8);
+    stream << "aaaa" << size_t(32145);
+    CHECK_EQUAL("aaaa32145", stream.GetText());
+}
+
+#endif
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestTest.cpp b/Release/tests/common/UnitTestpp/src/tests/TestTest.cpp
new file mode 100644 (file)
index 0000000..82394d2
--- /dev/null
@@ -0,0 +1,145 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+using namespace UnitTest;
+
+namespace
+{
+TEST(PassingTestHasNoFailures)
+{
+    class PassingTest : public Test
+    {
+    public:
+        PassingTest() : Test("passing") {}
+        virtual void RunImpl() const { CHECK(true); }
+    };
+
+    TestResults results;
+    {
+        ScopedCurrentTest scopedResults(results);
+        PassingTest().Run();
+    }
+
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(FailingTestHasFailures)
+{
+    class FailingTest : public Test
+    {
+    public:
+        FailingTest() : Test("failing") {}
+        virtual void RunImpl() const { CHECK(false); }
+    };
+
+    TestResults results;
+    {
+        ScopedCurrentTest scopedResults(results);
+        FailingTest().Run();
+    }
+
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+#ifndef UNITTEST_NO_EXCEPTIONS
+TEST(ThrowingTestsAreReportedAsFailures)
+{
+    class CrashingTest : public Test
+    {
+    public:
+        CrashingTest() : Test("throwing") {}
+        virtual void RunImpl() const { throw "Blah"; }
+    };
+
+    TestResults results;
+    {
+        ScopedCurrentTest scopedResult(results);
+        CrashingTest().Run();
+    }
+
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+/*
+#ifndef UNITTEST_MINGW
+TEST(CrashingTestsAreReportedAsFailures)
+{
+    class CrashingTest : public Test
+    {
+    public:
+        CrashingTest() : Test("crashing") {}
+        virtual void RunImpl() const
+        {
+            reinterpret_cast< void (*)() >(0)();
+        }
+    };
+
+    TestResults results;
+        {
+                ScopedCurrentTest scopedResult(results);
+                CrashingTest().Run();
+        }
+
+        CHECK_EQUAL(1, results.GetFailureCount());
+}
+#endif
+*/
+#endif
+
+TEST(TestWithUnspecifiedSuiteGetsDefaultSuite)
+{
+    Test test("test");
+    CHECK(test.m_details.suiteName != NULL);
+    CHECK_EQUAL("DefaultSuite", test.m_details.suiteName);
+}
+
+TEST(TestReflectsSpecifiedSuiteName)
+{
+    Test test("test", "testSuite");
+    CHECK(test.m_details.suiteName != NULL);
+    CHECK_EQUAL("testSuite", test.m_details.suiteName);
+}
+
+void Fail() { CHECK(false); }
+
+TEST(OutOfCoreCHECKMacrosCanFailTests)
+{
+    TestResults results;
+    {
+        ScopedCurrentTest scopedResult(results);
+        Fail();
+    }
+
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestTestList.cpp b/Release/tests/common/UnitTestpp/src/tests/TestTestList.cpp
new file mode 100644 (file)
index 0000000..9987dd0
--- /dev/null
@@ -0,0 +1,79 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+using namespace UnitTest;
+
+namespace
+{
+TEST(TestListIsEmptyByDefault)
+{
+    TestList list;
+    CHECK(list.GetFirst() == 0);
+}
+
+TEST(AddingTestSetsHeadToTest)
+{
+    Test test("test");
+    TestList list;
+    list.Add(&test);
+
+    CHECK(list.GetFirst() == &test);
+    CHECK(test.m_nextTest == 0);
+}
+
+TEST(AddingSecondTestAddsItToEndOfList)
+{
+    Test test1("test1");
+    Test test2("test2");
+
+    TestList list;
+    list.Add(&test1);
+    list.Add(&test2);
+
+    CHECK(list.GetFirst() == &test1);
+    CHECK(test1.m_nextTest == &test2);
+    CHECK(test2.m_nextTest == 0);
+}
+
+TEST(ListAdderAddsTestToList)
+{
+    TestList list;
+
+    Test test("");
+    ListAdder adder(list, &test, nullptr);
+
+    CHECK(list.GetFirst() == &test);
+    CHECK(test.m_nextTest == 0);
+}
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestTestMacros.cpp b/Release/tests/common/UnitTestpp/src/tests/TestTestMacros.cpp
new file mode 100644 (file)
index 0000000..6f7e415
--- /dev/null
@@ -0,0 +1,201 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+using namespace UnitTest;
+using namespace std;
+
+#ifdef __APPLE__
+extern "C" UnitTest::TestList& UnitTest::GetTestList()
+{
+    static TestList s_list;
+    return s_list;
+}
+#endif
+
+namespace
+{
+TestList list1;
+TEST_EX(DummyTest, list1) {}
+
+TEST(TestsAreAddedToTheListThroughMacro)
+{
+    CHECK(list1.GetFirst() != 0);
+    CHECK(list1.GetFirst()->m_nextTest == 0);
+}
+
+#ifndef UNITTEST_NO_EXCEPTIONS
+
+struct ThrowingThingie
+{
+    ThrowingThingie() : dummy(false)
+    {
+        if (!dummy) throw "Oops";
+    }
+
+    bool dummy;
+};
+
+TestList list2;
+TEST_FIXTURE_EX(ThrowingThingie, DummyTestName, list2) {}
+
+TEST(ExceptionsInFixtureAreReportedAsHappeningInTheFixture)
+{
+    RecordingReporter reporter;
+    TestResults result(&reporter);
+    {
+        ScopedCurrentTest scopedResults(result);
+        list2.GetFirst()->Run();
+    }
+
+    CHECK(strstr(reporter.lastFailedMessage, "xception"));
+    CHECK(strstr(reporter.lastFailedMessage, "fixture"));
+    CHECK(strstr(reporter.lastFailedMessage, "ThrowingThingie"));
+}
+
+#endif
+
+struct DummyFixture
+{
+    int x;
+};
+
+// We're really testing the macros so we just want them to compile and link
+SUITE(TestSuite1)
+{
+    TEST(SimilarlyNamedTestsInDifferentSuitesWork) {}
+
+    TEST_FIXTURE(DummyFixture, SimilarlyNamedFixtureTestsInDifferentSuitesWork) {}
+}
+
+SUITE(TestSuite2)
+{
+    TEST(SimilarlyNamedTestsInDifferentSuitesWork) {}
+
+    TEST_FIXTURE(DummyFixture, SimilarlyNamedFixtureTestsInDifferentSuitesWork) {}
+}
+
+TestList macroTestList1;
+TEST_EX(MacroTestHelper1, macroTestList1) {}
+
+TEST(TestAddedWithTEST_EXMacroGetsDefaultSuite)
+{
+    CHECK(macroTestList1.GetFirst() != NULL);
+    CHECK_EQUAL("MacroTestHelper1", macroTestList1.GetFirst()->m_details.testName);
+    CHECK_EQUAL("DefaultSuite", macroTestList1.GetFirst()->m_details.suiteName);
+}
+
+TestList macroTestList2;
+TEST_FIXTURE_EX(DummyFixture, MacroTestHelper2, macroTestList2) {}
+
+TEST(TestAddedWithTEST_FIXTURE_EXMacroGetsDefaultSuite)
+{
+    CHECK(macroTestList2.GetFirst() != NULL);
+    CHECK_EQUAL("MacroTestHelper2", macroTestList2.GetFirst()->m_details.testName);
+    CHECK_EQUAL("DefaultSuite", macroTestList2.GetFirst()->m_details.suiteName);
+}
+
+#ifndef UNITTEST_NO_EXCEPTIONS
+
+struct FixtureCtorThrows
+{
+    FixtureCtorThrows() { throw "exception"; }
+};
+
+TestList throwingFixtureTestList1;
+TEST_FIXTURE_EX(FixtureCtorThrows, FixtureCtorThrowsTestName, throwingFixtureTestList1) {}
+
+TEST(FixturesWithThrowingCtorsAreFailures)
+{
+    CHECK(throwingFixtureTestList1.GetFirst() != NULL);
+    RecordingReporter reporter;
+    TestResults result(&reporter);
+    {
+        ScopedCurrentTest scopedResult(result);
+        throwingFixtureTestList1.GetFirst()->Run();
+    }
+
+    int const failureCount = result.GetFailedTestCount();
+    CHECK_EQUAL(1, failureCount);
+    CHECK(strstr(reporter.lastFailedMessage, "while constructing fixture"));
+}
+
+const int FailingLine = 123;
+
+struct FixtureCtorAsserts
+{
+    FixtureCtorAsserts() { UnitTest::ReportAssert("assert failure", "file", FailingLine); }
+};
+
+TestList ctorAssertFixtureTestList;
+TEST_FIXTURE_EX(FixtureCtorAsserts, CorrectlyReportsAssertFailureInCtor, ctorAssertFixtureTestList) {}
+
+TEST(CorrectlyReportsFixturesWithCtorsThatAssert)
+{
+    RecordingReporter reporter;
+    TestResults result(&reporter);
+    {
+        ScopedCurrentTest scopedResults(result);
+        ctorAssertFixtureTestList.GetFirst()->Run();
+    }
+
+    const int failureCount = result.GetFailedTestCount();
+    CHECK_EQUAL(1, failureCount);
+    CHECK_EQUAL(FailingLine, reporter.lastFailedLine);
+    CHECK(strstr(reporter.lastFailedMessage, "assert failure"));
+}
+
+#endif
+
+} // namespace
+
+// We're really testing if it's possible to use the same suite in two files
+// to compile and link successfuly (TestTestSuite.cpp has suite with the same name)
+// Note: we are outside of the anonymous namespace
+SUITE(SameTestSuite)
+{
+    TEST(DummyTest1) {}
+}
+
+#define CUR_TEST_NAME CurrentTestDetailsContainCurrentTestInfo
+#define INNER_STRINGIFY(X) #X
+#define STRINGIFY(X) INNER_STRINGIFY(X)
+
+TEST(CUR_TEST_NAME)
+{
+    const UnitTest::TestDetails* details = CurrentTest::Details();
+    CHECK_EQUAL(STRINGIFY(CUR_TEST_NAME), details->testName);
+}
+
+#undef CUR_TEST_NAME
+#undef INNER_STRINGIFY
+#undef STRINGIFY
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestTestResults.cpp b/Release/tests/common/UnitTestpp/src/tests/TestTestResults.cpp
new file mode 100644 (file)
index 0000000..6609a08
--- /dev/null
@@ -0,0 +1,138 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+using namespace UnitTest;
+
+namespace
+{
+TestDetails const g_testdetails("testname", "suitename", "filename", 123);
+
+TEST(StartsWithNoTestsRun)
+{
+    TestResults results;
+    CHECK_EQUAL(0, results.GetTotalTestCount());
+}
+
+TEST(RecordsNumbersOfTests)
+{
+    TestResults results;
+    results.OnTestStart(g_testdetails);
+    results.OnTestStart(g_testdetails);
+    results.OnTestStart(g_testdetails);
+    CHECK_EQUAL(3, results.GetTotalTestCount());
+}
+
+TEST(StartsWithNoTestsFailing)
+{
+    TestResults results;
+    CHECK_EQUAL(0, results.GetFailureCount());
+}
+
+TEST(RecordsNumberOfFailures)
+{
+    TestResults results;
+    results.OnTestFailure(g_testdetails, "");
+    results.OnTestFailure(g_testdetails, "");
+    CHECK_EQUAL(2, results.GetFailureCount());
+}
+
+TEST(RecordsNumberOfFailedTests)
+{
+    TestResults results;
+
+    results.OnTestStart(g_testdetails);
+    results.OnTestFailure(g_testdetails, "");
+    results.OnTestFinish(g_testdetails, 0);
+
+    results.OnTestStart(g_testdetails);
+    results.OnTestFailure(g_testdetails, "");
+    results.OnTestFailure(g_testdetails, "");
+    results.OnTestFailure(g_testdetails, "");
+    results.OnTestFinish(g_testdetails, 0);
+
+    CHECK_EQUAL(2, results.GetFailedTestCount());
+}
+
+TEST(NotifiesReporterOfTestStartWithCorrectInfo)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+    results.OnTestStart(g_testdetails);
+
+    CHECK_EQUAL(1, reporter.testRunCount);
+    CHECK_EQUAL("suitename", reporter.lastStartedSuite);
+    CHECK_EQUAL("testname", reporter.lastStartedTest);
+}
+
+TEST(NotifiesReporterOfTestFailureWithCorrectInfo)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+
+    results.OnTestFailure(g_testdetails, "failurestring");
+    CHECK_EQUAL(1, reporter.testFailedCount);
+    CHECK_EQUAL("filename", reporter.lastFailedFile);
+    CHECK_EQUAL(123, reporter.lastFailedLine);
+    CHECK_EQUAL("suitename", reporter.lastFailedSuite);
+    CHECK_EQUAL("testname", reporter.lastFailedTest);
+    CHECK_EQUAL("failurestring", reporter.lastFailedMessage);
+}
+
+TEST(NotifiesReporterOfCheckFailureWithCorrectInfo)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+
+    results.OnTestFailure(g_testdetails, "failurestring");
+    CHECK_EQUAL(1, reporter.testFailedCount);
+
+    CHECK_EQUAL("filename", reporter.lastFailedFile);
+    CHECK_EQUAL(123, reporter.lastFailedLine);
+    CHECK_EQUAL("testname", reporter.lastFailedTest);
+    CHECK_EQUAL("suitename", reporter.lastFailedSuite);
+    CHECK_EQUAL("failurestring", reporter.lastFailedMessage);
+}
+
+TEST(NotifiesReporterOfTestEnd)
+{
+    RecordingReporter reporter;
+    TestResults results(&reporter);
+
+    results.OnTestFinish(g_testdetails, 0.1234f);
+    CHECK_EQUAL(1, reporter.testFinishedCount);
+    CHECK_EQUAL("testname", reporter.lastFinishedTest);
+    CHECK_EQUAL("suitename", reporter.lastFinishedSuite);
+    CHECK_CLOSE(0.1234f, reporter.lastFinishedTestTime, 0.0001f);
+}
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestTestRunner.cpp b/Release/tests/common/UnitTestpp/src/tests/TestTestRunner.cpp
new file mode 100644 (file)
index 0000000..ac9f02f
--- /dev/null
@@ -0,0 +1,289 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#include "../Test.h"
+
+using namespace UnitTest;
+
+namespace
+{
+struct TestRunnerFixture
+{
+    TestRunnerFixture() : runner(reporter) { s_testRunnerFixtureTestResults = runner.GetTestResults(); }
+
+    static TestResults* s_testRunnerFixtureTestResults;
+
+    RecordingReporter reporter;
+    TestList list;
+    TestRunner runner;
+};
+
+TestResults* TestRunnerFixture::s_testRunnerFixtureTestResults = NULL;
+
+struct MockTest : public Test
+{
+    MockTest(char const* testName, bool const success_, bool const assert_, int const count_ = 1)
+        : Test(testName), success(success_), asserted(assert_), count(count_)
+    {
+        m_isMockTest = true;
+    }
+
+    virtual void RunImpl() const
+    {
+        TestResults* testResults = TestRunnerFixture::s_testRunnerFixtureTestResults;
+
+        for (int i = 0; i < count; ++i)
+        {
+            if (asserted)
+                Detail::ReportAssertEx(testResults, &m_details, "desc", "file", 0);
+            else if (!success)
+                testResults->OnTestFailure(m_details, "message");
+        }
+    }
+
+    bool const success;
+    bool const asserted;
+    int const count;
+};
+
+TEST_FIXTURE(TestRunnerFixture, TestStartIsReportedCorrectly)
+{
+    MockTest test("goodtest", true, false);
+    list.Add(&test);
+
+    runner.RunTestsIf(list, NULL, True(), 0);
+    CHECK_EQUAL(1, reporter.testRunCount);
+    CHECK_EQUAL("goodtest", reporter.lastStartedTest);
+}
+
+TEST_FIXTURE(TestRunnerFixture, TestFinishIsReportedCorrectly)
+{
+    MockTest test("goodtest", true, false);
+    list.Add(&test);
+
+    runner.RunTestsIf(list, NULL, True(), 0);
+    CHECK_EQUAL(1, reporter.testFinishedCount);
+    CHECK_EQUAL("goodtest", reporter.lastFinishedTest);
+}
+
+class SlowTest : public Test
+{
+public:
+    SlowTest() : Test("slow", "somesuite", "filename", 123) {}
+    virtual void RunImpl() const { TimeHelpers::SleepMs(20); }
+};
+
+TEST_FIXTURE(TestRunnerFixture, TestFinishIsCalledWithCorrectTime)
+{
+    SlowTest test;
+    list.Add(&test);
+
+    runner.RunTestsIf(list, NULL, True(), 0);
+    CHECK(reporter.lastFinishedTestTime >= 0.005f && reporter.lastFinishedTestTime <= 0.050f);
+}
+
+TEST_FIXTURE(TestRunnerFixture, FailureCountIsZeroWhenNoTestsAreRun)
+{
+    CHECK_EQUAL(0, runner.RunTestsIf(list, NULL, True(), 0));
+    CHECK_EQUAL(0, reporter.testRunCount);
+    CHECK_EQUAL(0, reporter.testFailedCount);
+}
+
+TEST_FIXTURE(TestRunnerFixture, CallsReportFailureOncePerFailingTest)
+{
+    MockTest test1("test", false, false);
+    list.Add(&test1);
+    MockTest test2("test", true, false);
+    list.Add(&test2);
+    MockTest test3("test", false, false);
+    list.Add(&test3);
+
+    CHECK_EQUAL(2, runner.RunTestsIf(list, NULL, True(), 0));
+    CHECK_EQUAL(2, reporter.testFailedCount);
+}
+
+TEST_FIXTURE(TestRunnerFixture, TestsThatAssertAreReportedAsFailing)
+{
+    MockTest test("test", true, true);
+    list.Add(&test);
+
+    runner.RunTestsIf(list, NULL, True(), 0);
+    CHECK_EQUAL(1, reporter.testFailedCount);
+}
+
+TEST_FIXTURE(TestRunnerFixture, AssertingTestAbortsAsSoonAsAssertIsHit)
+{
+    MockTest test("test", false, true, 3);
+    list.Add(&test);
+    runner.RunTestsIf(list, NULL, True(), 0);
+    CHECK_EQUAL(1, reporter.summaryFailureCount);
+}
+
+TEST_FIXTURE(TestRunnerFixture, ReporterNotifiedOfTestCount)
+{
+    MockTest test1("test", true, false);
+    MockTest test2("test", true, false);
+    MockTest test3("test", true, false);
+    list.Add(&test1);
+    list.Add(&test2);
+    list.Add(&test3);
+
+    runner.RunTestsIf(list, NULL, True(), 0);
+    CHECK_EQUAL(3, reporter.summaryTotalTestCount);
+}
+
+TEST_FIXTURE(TestRunnerFixture, ReporterNotifiedOfFailedTests)
+{
+    MockTest test1("test", false, false, 2);
+    MockTest test2("test", true, false);
+    MockTest test3("test", false, false, 3);
+    list.Add(&test1);
+    list.Add(&test2);
+    list.Add(&test3);
+
+    runner.RunTestsIf(list, NULL, True(), 0);
+    CHECK_EQUAL(2, reporter.summaryFailedTestCount);
+}
+
+TEST_FIXTURE(TestRunnerFixture, ReporterNotifiedOfFailures)
+{
+    MockTest test1("test", false, false, 2);
+    MockTest test2("test", true, false);
+    MockTest test3("test", false, false, 3);
+    list.Add(&test1);
+    list.Add(&test2);
+    list.Add(&test3);
+
+    runner.RunTestsIf(list, NULL, True(), 0);
+    CHECK_EQUAL(5, reporter.summaryFailureCount);
+}
+
+TEST_FIXTURE(TestRunnerFixture, SlowTestPassesForHighTimeThreshold)
+{
+    SlowTest test;
+    list.Add(&test);
+
+    runner.RunTestsIf(list, NULL, True(), 0);
+    CHECK_EQUAL(0, reporter.testFailedCount);
+}
+
+struct TestSuiteFixture
+{
+    TestSuiteFixture()
+        : test1("TestInDefaultSuite")
+        , test2("TestInOtherSuite", "OtherSuite")
+        , test3("SecondTestInDefaultSuite")
+        , runner(reporter)
+    {
+        list.Add(&test1);
+        list.Add(&test2);
+    }
+
+    Test test1;
+    Test test2;
+    Test test3;
+    RecordingReporter reporter;
+    TestList list;
+    TestRunner runner;
+};
+
+TEST_FIXTURE(TestSuiteFixture, TestRunnerRunsAllSuitesIfNullSuiteIsPassed)
+{
+    runner.RunTestsIf(list, NULL, True(), 0);
+    CHECK_EQUAL(2, reporter.summaryTotalTestCount);
+}
+
+TEST_FIXTURE(TestSuiteFixture, TestRunnerRunsOnlySpecifiedSuite)
+{
+    runner.RunTestsIf(list, "OtherSuite", True(), 0);
+    CHECK_EQUAL(1, reporter.summaryTotalTestCount);
+    CHECK_EQUAL("TestInOtherSuite", reporter.lastFinishedTest);
+}
+
+struct RunTestIfNameIs
+{
+    RunTestIfNameIs(char const* name_) : name(name_) {}
+
+    bool operator()(const Test* const test) const
+    {
+        using namespace std;
+        return (0 == strcmp(test->m_details.testName, name));
+    }
+
+    char const* name;
+};
+
+TEST(TestMockPredicateBehavesCorrectly)
+{
+    RunTestIfNameIs predicate("pass");
+
+    Test pass("pass");
+    Test fail("fail");
+
+    CHECK(predicate(&pass));
+    CHECK(!predicate(&fail));
+}
+
+TEST_FIXTURE(TestRunnerFixture, TestRunnerRunsTestsThatPassPredicate)
+{
+    Test should_run("goodtest");
+    list.Add(&should_run);
+
+    Test should_not_run("badtest");
+    list.Add(&should_not_run);
+
+    runner.RunTestsIf(list, NULL, RunTestIfNameIs("goodtest"), 0);
+    CHECK_EQUAL(1, reporter.testRunCount);
+    CHECK_EQUAL("goodtest", reporter.lastStartedTest);
+}
+
+TEST_FIXTURE(TestRunnerFixture, TestRunnerOnlyRunsTestsInSpecifiedSuiteAndThatPassPredicate)
+{
+    Test runningTest1("goodtest", "suite");
+    Test skippedTest2("goodtest");
+    Test skippedTest3("badtest", "suite");
+    Test skippedTest4("badtest");
+
+    list.Add(&runningTest1);
+    list.Add(&skippedTest2);
+    list.Add(&skippedTest3);
+    list.Add(&skippedTest4);
+
+    runner.RunTestsIf(list, "suite", RunTestIfNameIs("goodtest"), 0);
+
+    CHECK_EQUAL(1, reporter.testRunCount);
+    CHECK_EQUAL("goodtest", reporter.lastStartedTest);
+    CHECK_EQUAL("suite", reporter.lastStartedSuite);
+}
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestTestSuite.cpp b/Release/tests/common/UnitTestpp/src/tests/TestTestSuite.cpp
new file mode 100644 (file)
index 0000000..ba97d9d
--- /dev/null
@@ -0,0 +1,40 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+// We're really testing if it's possible to use the same suite in two files
+// to compile and link successfuly (TestTestSuite.cpp has suite with the same name)
+// Note: we are outside of the anonymous namespace
+SUITE(SameTestSuite)
+{
+    TEST(DummyTest2) {}
+}
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestUnitTestPP.cpp b/Release/tests/common/UnitTestpp/src/tests/TestUnitTestPP.cpp
new file mode 100644 (file)
index 0000000..ac4cf66
--- /dev/null
@@ -0,0 +1,151 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+// These are sample tests that show the different features of the framework
+
+namespace
+{
+TEST(ValidCheckSucceeds)
+{
+    bool const b = true;
+    CHECK(b);
+}
+
+TEST(CheckWorksWithPointers)
+{
+    void* p = (void*)0x100;
+    CHECK(p);
+    CHECK(p != 0);
+}
+
+TEST(ValidCheckEqualSucceeds)
+{
+    int const x = 3;
+    int const y = 3;
+    CHECK_EQUAL(x, y);
+}
+
+TEST(CheckEqualWorksWithPointers)
+{
+    void* p = (void*)0;
+    CHECK_EQUAL((void*)0, p);
+}
+
+TEST(ValidCheckCloseSucceeds)
+{
+    CHECK_CLOSE(2.0f, 2.001f, 0.01f);
+    CHECK_CLOSE(2.001f, 2.0f, 0.01f);
+}
+
+TEST(ArrayCloseSucceeds)
+{
+    float const a1[] = {1, 2, 3};
+    float const a2[] = {1, 2.01f, 3};
+    CHECK_ARRAY_CLOSE(a1, a2, 3, 0.1f);
+}
+
+#ifndef UNITTEST_NO_EXCEPTIONS
+
+TEST(CheckThrowMacroSucceedsOnCorrectException)
+{
+    struct TestException
+    {
+    };
+    CHECK_THROW(throw TestException(), TestException);
+}
+
+TEST(CheckAssertSucceeds) { CHECK_ASSERT(UnitTest::ReportAssert("desc", "file", 0)); }
+
+TEST(CheckThrowMacroFailsOnMissingException)
+{
+    class NoThrowTest : public UnitTest::Test
+    {
+    public:
+        NoThrowTest() : Test("nothrow") {}
+        void DontThrow() const {}
+
+        virtual void RunImpl() const { CHECK_THROW(DontThrow(), int); }
+    };
+
+    UnitTest::TestResults results;
+    {
+        ScopedCurrentTest scopedResults(results);
+
+        NoThrowTest test;
+        test.Run();
+    }
+
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+TEST(CheckThrowMacroFailsOnWrongException)
+{
+    class WrongThrowTest : public UnitTest::Test
+    {
+    public:
+        WrongThrowTest() : Test("wrongthrow") {}
+        virtual void RunImpl() const { CHECK_THROW(throw "oops", int); }
+    };
+
+    UnitTest::TestResults results;
+    {
+        ScopedCurrentTest scopedResults(results);
+
+        WrongThrowTest test;
+        test.Run();
+    }
+
+    CHECK_EQUAL(1, results.GetFailureCount());
+}
+
+#endif
+
+struct SimpleFixture
+{
+    SimpleFixture() { ++instanceCount; }
+    ~SimpleFixture() { --instanceCount; }
+
+    static int instanceCount;
+};
+
+int SimpleFixture::instanceCount = 0;
+
+TEST_FIXTURE(SimpleFixture, DefaultFixtureCtorIsCalled) { CHECK(SimpleFixture::instanceCount > 0); }
+
+TEST_FIXTURE(SimpleFixture, OnlyOneFixtureAliveAtATime) { CHECK_EQUAL(1, SimpleFixture::instanceCount); }
+
+void CheckBool(const bool b) { CHECK(b); }
+
+TEST(CanCallCHECKOutsideOfTestFunction) { CheckBool(true); }
+
+} // namespace
diff --git a/Release/tests/common/UnitTestpp/src/tests/TestXmlTestReporter.cpp b/Release/tests/common/UnitTestpp/src/tests/TestXmlTestReporter.cpp
new file mode 100644 (file)
index 0000000..de78002
--- /dev/null
@@ -0,0 +1,207 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#include "stdafx.h"
+
+#ifndef UNITTEST_NO_DEFERRED_REPORTER
+
+#include "../XmlTestReporter.h"
+#include <sstream>
+
+using namespace UnitTest;
+using std::ostringstream;
+
+namespace
+{
+#ifndef UNITTEST_MEMORYOUTSTREAM_IS_STD_OSTRINGSTREAM
+
+// Overload to let MemoryOutStream accept std::string
+MemoryOutStream& operator<<(MemoryOutStream& s, const std::string& value)
+{
+    s << value.c_str();
+    return s;
+}
+
+#endif
+
+struct XmlTestReporterFixture
+{
+    XmlTestReporterFixture() : reporter(output) {}
+
+    ostringstream output;
+    XmlTestReporter reporter;
+};
+
+TEST_FIXTURE(XmlTestReporterFixture, MultipleCharactersAreEscaped)
+{
+    TestDetails const details("TestName", "suite", "filename.h", 4321);
+
+    reporter.ReportTestStart(details);
+    reporter.ReportFailure(details, "\"\"\'\'&&<<>>");
+    reporter.ReportTestFinish(details, false, 0.1f);
+    reporter.ReportSummary(1, 2, 3, 0.1f);
+
+    char const* expected = "<?xml version=\"1.0\"?>"
+                           "<unittest-results tests=\"1\" failedtests=\"2\" failures=\"3\" time=\"0.1\">"
+                           "<test suite=\"suite\" name=\"TestName\" time=\"0.1\">"
+                           "<failure message=\"filename.h(4321) : "
+                           "&quot;&quot;&apos;&apos;&amp;&amp;&lt;&lt;&gt;&gt;\"/>"
+                           "</test>"
+                           "</unittest-results>";
+
+    CHECK_EQUAL(expected, output.str());
+}
+
+TEST_FIXTURE(XmlTestReporterFixture, OutputIsCachedUntilReportSummaryIsCalled)
+{
+    TestDetails const details("", "", "", 0);
+
+    reporter.ReportTestStart(details);
+    reporter.ReportFailure(details, "message");
+    reporter.ReportTestFinish(details, false, 1.0F);
+    CHECK(output.str().empty());
+
+    reporter.ReportSummary(1, 1, 1, 1.0f);
+    CHECK(!output.str().empty());
+}
+
+TEST_FIXTURE(XmlTestReporterFixture, EmptyReportSummaryFormat)
+{
+    reporter.ReportSummary(0, 0, 0, 0.1f);
+
+    const char* expected = "<?xml version=\"1.0\"?>"
+                           "<unittest-results tests=\"0\" failedtests=\"0\" failures=\"0\" time=\"0.1\">"
+                           "</unittest-results>";
+
+    CHECK_EQUAL(expected, output.str());
+}
+
+TEST_FIXTURE(XmlTestReporterFixture, SingleSuccessfulTestReportSummaryFormat)
+{
+    TestDetails const details("TestName", "DefaultSuite", "", 0);
+
+    reporter.ReportTestStart(details);
+    reporter.ReportSummary(1, 0, 0, 0.1f);
+
+    const char* expected = "<?xml version=\"1.0\"?>"
+                           "<unittest-results tests=\"1\" failedtests=\"0\" failures=\"0\" time=\"0.1\">"
+                           "<test suite=\"DefaultSuite\" name=\"TestName\" time=\"0\"/>"
+                           "</unittest-results>";
+
+    CHECK_EQUAL(expected, output.str());
+}
+
+TEST_FIXTURE(XmlTestReporterFixture, SingleFailedTestReportSummaryFormat)
+{
+    TestDetails const details("A Test", "suite", "A File", 4321);
+
+    reporter.ReportTestStart(details);
+    reporter.ReportFailure(details, "A Failure");
+    reporter.ReportSummary(1, 1, 1, 0.1f);
+
+    const char* expected = "<?xml version=\"1.0\"?>"
+                           "<unittest-results tests=\"1\" failedtests=\"1\" failures=\"1\" time=\"0.1\">"
+                           "<test suite=\"suite\" name=\"A Test\" time=\"0\">"
+                           "<failure message=\"A File(4321) : A Failure\"/>"
+                           "</test>"
+                           "</unittest-results>";
+
+    CHECK_EQUAL(expected, output.str());
+}
+
+TEST_FIXTURE(XmlTestReporterFixture, FailureMessageIsXMLEscaped)
+{
+    TestDetails const details("TestName", "suite", "filename.h", 4321);
+
+    reporter.ReportTestStart(details);
+    reporter.ReportFailure(details, "\"\'&<>");
+    reporter.ReportTestFinish(details, false, 0.1f);
+    reporter.ReportSummary(1, 1, 1, 0.1f);
+
+    char const* expected = "<?xml version=\"1.0\"?>"
+                           "<unittest-results tests=\"1\" failedtests=\"1\" failures=\"1\" time=\"0.1\">"
+                           "<test suite=\"suite\" name=\"TestName\" time=\"0.1\">"
+                           "<failure message=\"filename.h(4321) : &quot;&apos;&amp;&lt;&gt;\"/>"
+                           "</test>"
+                           "</unittest-results>";
+
+    CHECK_EQUAL(expected, output.str());
+}
+
+TEST_FIXTURE(XmlTestReporterFixture, OneFailureAndOneSuccess)
+{
+    TestDetails const failedDetails("FailedTest", "suite", "fail.h", 1);
+    reporter.ReportTestStart(failedDetails);
+    reporter.ReportFailure(failedDetails, "expected 1 but was 2");
+    reporter.ReportTestFinish(failedDetails, false, 0.1f);
+
+    TestDetails const succeededDetails("SucceededTest", "suite", "", 0);
+    reporter.ReportTestStart(succeededDetails);
+    reporter.ReportTestFinish(succeededDetails, true, 1.0f);
+    reporter.ReportSummary(2, 1, 1, 1.1f);
+
+    char const* expected = "<?xml version=\"1.0\"?>"
+                           "<unittest-results tests=\"2\" failedtests=\"1\" failures=\"1\" time=\"1.1\">"
+                           "<test suite=\"suite\" name=\"FailedTest\" time=\"0.1\">"
+                           "<failure message=\"fail.h(1) : expected 1 but was 2\"/>"
+                           "</test>"
+                           "<test suite=\"suite\" name=\"SucceededTest\" time=\"1\"/>"
+                           "</unittest-results>";
+
+    CHECK_EQUAL(expected, output.str());
+}
+
+TEST_FIXTURE(XmlTestReporterFixture, MultipleFailures)
+{
+    TestDetails const failedDetails1("FailedTest", "suite", "fail.h", 1);
+    TestDetails const failedDetails2("FailedTest", "suite", "fail.h", 31);
+
+    reporter.ReportTestStart(failedDetails1);
+    reporter.ReportFailure(failedDetails1, "expected 1 but was 2");
+    reporter.ReportFailure(failedDetails2, "expected one but was two");
+    reporter.ReportTestFinish(failedDetails1, false, 0.1f);
+
+    reporter.ReportSummary(1, 1, 2, 1.1f);
+
+    char const* expected = "<?xml version=\"1.0\"?>"
+                           "<unittest-results tests=\"1\" failedtests=\"1\" failures=\"2\" time=\"1.1\">"
+                           "<test suite=\"suite\" name=\"FailedTest\" time=\"0.1\">"
+                           "<failure message=\"fail.h(1) : expected 1 but was 2\"/>"
+                           "<failure message=\"fail.h(31) : expected one but was two\"/>"
+                           "</test>"
+                           "</unittest-results>";
+
+    CHECK_EQUAL(expected, output.str());
+}
+
+} // namespace
+
+#endif
diff --git a/Release/tests/common/UnitTestpp/src/tests/stdafx.cpp b/Release/tests/common/UnitTestpp/src/tests/stdafx.cpp
new file mode 100644 (file)
index 0000000..82fd96b
--- /dev/null
@@ -0,0 +1,35 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+// stdafx.cpp :
+// Include the standard header and generate the precompiled header.
+
+#include "stdafx.h"
\ No newline at end of file
diff --git a/Release/tests/common/UnitTestpp/src/tests/stdafx.h b/Release/tests/common/UnitTestpp/src/tests/stdafx.h
new file mode 100644 (file)
index 0000000..cd587dd
--- /dev/null
@@ -0,0 +1,45 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#pragma once
+
+#include "../../config.h"
+#include "../../unittestpp.h"
+#include "../CurrentTest.h"
+#include "../ReportAssert.h"
+#include "../ReportAssertImpl.h"
+#include "../TestMacros.h"
+#include "../TestReporter.h"
+#include "../TestResults.h"
+#include "../TimeHelpers.h"
+#include "RecordingReporter.h"
+#include "ScopedCurrentTest.h"
+#include <cstring>
\ No newline at end of file
diff --git a/Release/tests/common/UnitTestpp/unittestpp.h b/Release/tests/common/UnitTestpp/unittestpp.h
new file mode 100644 (file)
index 0000000..9df892e
--- /dev/null
@@ -0,0 +1,42 @@
+/***
+ * This file is based on or incorporates material from the UnitTest++ r30 open source project.
+ * Microsoft is not the original author of this code but has modified it and is licensing the code under
+ * the MIT License. Microsoft reserves all other rights not expressly granted under the MIT License,
+ * whether by implication, estoppel or otherwise.
+ *
+ * UnitTest++ r30
+ *
+ * Copyright (c) 2006 Noel Llopis and Charles Nicholson
+ * Portions Copyright (c) Microsoft Corporation
+ *
+ * All Rights Reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ***/
+
+#ifndef UNITTESTPP_H
+#define UNITTESTPP_H
+
+#include "config.h"
+#include "src/CheckMacros.h"
+#include "src/GlobalSettings.h"
+#include "src/ReportAssert.h"
+#include "src/TestMacros.h"
+#include "src/TestRunner.h"
+
+#endif
diff --git a/Release/tests/common/utilities/CMakeLists.txt b/Release/tests/common/utilities/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a2d2306
--- /dev/null
@@ -0,0 +1,18 @@
+include_directories(include)
+
+if(WIN32)
+  add_definitions(-DCOMMONUTILITIES_EXPORTS)
+endif()
+
+add_library(common_utilities
+  os_utilities.cpp
+  )
+
+if(NOT BUILD_SHARED_LIBS)
+  target_compile_definitions(common_utilities INTERFACE -DTEST_UTILITY_API=)
+endif()
+
+target_link_libraries(common_utilities
+  cpprest
+  unittestpp
+)
diff --git a/Release/tests/common/utilities/include/common_utilities_public.h b/Release/tests/common/utilities/include/common_utilities_public.h
new file mode 100644 (file)
index 0000000..67abfb8
--- /dev/null
@@ -0,0 +1,24 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * common_utilities.h -- Common definitions for public test utility headers
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if !defined(_WIN32) && !defined(__cplusplus_winrt)
+#define TEST_UTILITY_API
+#endif // !_WIN32 && !__cplusplus_winrt
+
+#ifndef TEST_UTILITY_API
+#ifdef COMMONUTILITIES_EXPORTS
+#define TEST_UTILITY_API __declspec(dllexport)
+#else // COMMONUTILITIES_EXPORTS
+#define TEST_UTILITY_API __declspec(dllimport)
+#endif // COMMONUTILITIES_EXPORTS
+#endif // TEST_UTILITY_API
diff --git a/Release/tests/common/utilities/include/locale_guard.h b/Release/tests/common/utilities/include/locale_guard.h
new file mode 100644 (file)
index 0000000..ea3eeb8
--- /dev/null
@@ -0,0 +1,34 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Defines an RAII container for setting global locale.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include <locale>
+
+namespace tests
+{
+namespace common
+{
+namespace utilities
+{
+class locale_guard
+{
+public:
+    locale_guard(std::locale const& loc) { m_prev = std::locale::global(loc); }
+    ~locale_guard() { std::locale::global(m_prev); }
+
+private:
+    std::locale m_prev;
+    locale_guard(locale_guard const&);
+    locale_guard& operator=(locale_guard const&);
+};
+
+} // namespace utilities
+} // namespace common
+} // namespace tests
diff --git a/Release/tests/common/utilities/include/os_utilities.h b/Release/tests/common/utilities/include/os_utilities.h
new file mode 100644 (file)
index 0000000..51f103b
--- /dev/null
@@ -0,0 +1,40 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * os_utilities.h - defines an abstraction for common OS functions like Sleep, hiding the underlying platform.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "common_utilities_public.h"
+#include "cpprest/details/cpprest_compat.h"
+
+namespace tests
+{
+namespace common
+{
+namespace utilities
+{
+class os_utilities
+{
+public:
+    static TEST_UTILITY_API void __cdecl sleep(unsigned long ms);
+
+    // Could use std::atomics but VS 2010 doesn't support it yet.
+    static TEST_UTILITY_API unsigned long __cdecl interlocked_increment(volatile unsigned long* addend);
+    static TEST_UTILITY_API long __cdecl interlocked_exchange(volatile long* target, long value);
+
+private:
+    os_utilities();
+    os_utilities(const os_utilities&);
+    os_utilities& operator=(const os_utilities&);
+};
+
+} // namespace utilities
+} // namespace common
+} // namespace tests
diff --git a/Release/tests/common/utilities/os_utilities.cpp b/Release/tests/common/utilities/os_utilities.cpp
new file mode 100644 (file)
index 0000000..12aa4be
--- /dev/null
@@ -0,0 +1,62 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * os_utilities.cpp - defines an abstraction for common OS functions like Sleep, hiding the underlying platform.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "os_utilities.h"
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#include <SDKDDKVer.h>
+
+#include <Windows.h>
+#else
+#include <unistd.h>
+#endif
+
+namespace tests
+{
+namespace common
+{
+namespace utilities
+{
+void os_utilities::sleep(unsigned long ms)
+{
+#ifdef WIN32
+    Sleep(ms);
+#else
+    usleep(ms * 1000);
+#endif
+}
+
+unsigned long os_utilities::interlocked_increment(volatile unsigned long* addend)
+{
+#ifdef WIN32
+    return InterlockedIncrement(addend);
+#elif defined(__GNUC__)
+    return __sync_add_and_fetch(addend, 1);
+#else
+#error Need to implement interlocked_increment
+#endif
+}
+
+long os_utilities::interlocked_exchange(volatile long* target, long value)
+{
+#ifdef WIN32
+    return InterlockedExchange(target, value);
+#elif defined(__GNUC__)
+    return __sync_lock_test_and_set(target, value);
+#else
+#error Need to implement interlocked_exchange
+#endif
+}
+
+} // namespace utilities
+} // namespace common
+} // namespace tests
diff --git a/Release/tests/functional/CMakeLists.txt b/Release/tests/functional/CMakeLists.txt
new file mode 100644 (file)
index 0000000..cc89052
--- /dev/null
@@ -0,0 +1,7 @@
+add_subdirectory(http)
+add_subdirectory(json)
+add_subdirectory(pplx)
+add_subdirectory(streams)
+add_subdirectory(uri)
+add_subdirectory(utils)
+add_subdirectory(websockets)
\ No newline at end of file
diff --git a/Release/tests/functional/http/CMakeLists.txt b/Release/tests/functional/http/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e355af8
--- /dev/null
@@ -0,0 +1,3 @@
+add_subdirectory(utilities)
+add_subdirectory(client)
+add_subdirectory(listener)
diff --git a/Release/tests/functional/http/client/CMakeLists.txt b/Release/tests/functional/http/client/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3e1a936
--- /dev/null
@@ -0,0 +1,41 @@
+set(SOURCES
+  authentication_tests.cpp
+  building_request_tests.cpp
+  client_construction.cpp
+  compression_tests.cpp
+  connection_pool_tests.cpp
+  connections_and_errors.cpp
+  header_tests.cpp
+  http_client_fuzz_tests.cpp
+  http_client_tests.cpp
+  http_methods_tests.cpp
+  multiple_requests.cpp
+  oauth1_tests.cpp
+  oauth2_tests.cpp
+  outside_tests.cpp
+  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
+  response_extract_tests.cpp
+  response_stream_tests.cpp
+  status_code_reason_phrase_tests.cpp
+  to_string_tests.cpp
+)
+
+add_casablanca_test(httpclient_test SOURCES)
+if(TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT")
+  target_include_directories(httpclient_test PRIVATE ../utilities/include)
+else()
+  target_link_libraries(httpclient_test PRIVATE httptest_utilities)
+endif()
+
+configure_pch(httpclient_test stdafx.h stdafx.cpp)
+
+if(NOT WIN32)
+  cpprest_find_boost()
+  target_link_libraries(httpclient_test PRIVATE cpprestsdk_boost_internal)
+endif()
diff --git a/Release/tests/functional/http/client/authentication_tests.cpp b/Release/tests/functional/http/client/authentication_tests.cpp
new file mode 100644 (file)
index 0000000..c0440fe
--- /dev/null
@@ -0,0 +1,725 @@
+/***
+ * 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 authentication with http_clients.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <stdexcept>
+
+#ifdef _WIN32
+#ifdef __cplusplus_winrt
+#if !defined(__WRL_NO_DEFAULT_LIB__)
+#define __WRL_NO_DEFAULT_LIB__
+#endif
+#include <msxml6.h>
+#include <wrl.h>
+#else
+#include <windows.h>
+
+#include <winhttp.h>
+#pragma comment(lib, "winhttp")
+#endif
+#endif
+
+#if !defined(_WIN32)
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+#endif
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+#endif
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(authentication_tests)
+{
+    TEST_FIXTURE(uri_address, auth_no_data, "Ignore:Linux", "89", "Ignore:Apple", "89")
+    {
+        pplx::task<void> t, t2;
+        {
+            test_http_server::scoped_server scoped(m_uri);
+            http_client_config client_config;
+            web::credentials cred(U("some_user"), U("some_password")); // WinHTTP requires non-empty password
+            client_config.set_credentials(cred);
+            http_client client(m_uri, client_config);
+            const method mtd = methods::POST;
+
+            http_request msg(mtd);
+
+            t = scoped.server()->next_request().then([&](test_request* p_request) {
+                http_asserts::assert_test_request_equals(p_request, mtd, U("/"));
+
+                // Auth header
+                std::map<utility::string_t, utility::string_t> headers;
+                headers[U("WWW-Authenticate")] = U("Basic realm = \"WallyWorld\"");
+
+                // unauthorized
+                p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers);
+            });
+            t2 = scoped.server()->next_request().then([&](test_request* p_request) {
+                http_asserts::assert_test_request_equals(p_request, methods::POST, U("/"));
+                p_request->reply(200);
+            });
+
+            try
+            {
+                http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+            }
+            catch (...)
+            {
+                VERIFY_ARE_EQUAL(0, 1);
+            }
+        }
+        try
+        {
+            t.get();
+        }
+        catch (...)
+        {
+            VERIFY_ARE_EQUAL(0, 1);
+        }
+        try
+        {
+            t2.get();
+        }
+        catch (...)
+        {
+            VERIFY_ARE_EQUAL(0, 1);
+        }
+    }
+
+// TFS 648783
+#ifndef __cplusplus_winrt
+    TEST_FIXTURE(uri_address, proxy_auth_known_contentlength, "Ignore:Linux", "88", "Ignore:Apple", "88")
+    {
+        pplx::task<void> t, t2;
+        {
+            test_http_server::scoped_server scoped(m_uri);
+            http_client_config client_config;
+            web::credentials cred(U("some_user"), U("some_password")); // WinHTTP requires non-empty password
+            client_config.set_credentials(cred);
+            http_client client(m_uri, client_config);
+            const method mtd = methods::POST;
+            utility::string_t contents(U("Hello World"));
+
+            http_request msg(mtd);
+            msg.set_body(contents);
+
+            t = scoped.server()->next_request().then([&](test_request* p_request) {
+                http_asserts::assert_test_request_equals(p_request, mtd, U("/"));
+
+                // Auth header
+                std::map<utility::string_t, utility::string_t> headers;
+                headers[U("WWW-Authenticate")] = U("Basic realm = \"WallyWorld\"");
+
+                // unauthorized
+                p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers);
+            });
+
+            t2 = scoped.server()->next_request().then([&](test_request* p_request) {
+                http_asserts::assert_test_request_equals(
+                    p_request, methods::POST, U("/"), U("text/plain; charset=utf-8"), contents);
+
+                p_request->reply(200);
+            });
+
+            http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+        }
+        try
+        {
+            t.get();
+        }
+        catch (...)
+        {
+            VERIFY_ARE_EQUAL(0, 1);
+        }
+        try
+        {
+            t2.get();
+        }
+        catch (...)
+        {
+            VERIFY_ARE_EQUAL(0, 1);
+        }
+    }
+#endif
+
+    TEST_FIXTURE(uri_address, proxy_auth_noseek, "Ignore:Linux", "88", "Ignore:Apple", "88")
+    {
+        web::http::uri uri(U("http://localhost:34567/"));
+        test_http_server::scoped_server scoped(uri);
+        http_client client(
+            uri); // In this test, the request cannot be resent, so the username and password are not required
+        const method mtd = methods::POST;
+
+        auto buf = streams::producer_consumer_buffer<unsigned char>();
+        buf.putc('a').get();
+        buf.close(std::ios_base::out).get();
+
+        http_request msg(mtd);
+        msg.set_body(buf.create_istream(), 1);
+
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, mtd, U("/"));
+
+            // Auth header
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("WWW-Authenticate")] = U("Basic realm = \"WallyWorld\"");
+
+            // unauthorized
+            p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers);
+        });
+
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::Unauthorized);
+    }
+
+// Must specify content length with winrt client, so this test case isn't possible.
+#ifndef __cplusplus_winrt
+    TEST_FIXTURE(uri_address, proxy_auth_unknown_contentlength, "Ignore:Linux", "88", "Ignore:Apple", "88")
+    {
+        pplx::task<void> t;
+        {
+            test_http_server::scoped_server scoped(m_uri);
+            http_client_config client_config;
+            web::credentials cred(U("some_user"), U("some_password")); // WinHTTP requires non-empty password
+            client_config.set_credentials(cred);
+            http_client client(m_uri, client_config);
+            const method mtd = methods::POST;
+
+            std::vector<uint8_t> msg_body;
+            msg_body.push_back('a');
+
+            http_request msg(mtd);
+            msg.set_body(streams::container_stream<std::vector<uint8_t>>::open_istream(std::move(msg_body)));
+
+            auto replyFunc = [&](test_request* p_request) {
+                utility::string_t contents(U("a"));
+                http_asserts::assert_test_request_equals(
+                    p_request, methods::POST, U("/"), U("application/octet-stream"), contents);
+
+                p_request->reply(200);
+            };
+
+            t = scoped.server()
+                    ->next_request()
+                    .then([&](test_request* p_request) {
+                        http_asserts::assert_test_request_equals(p_request, mtd, U("/"));
+
+                        // Auth header
+                        std::map<utility::string_t, utility::string_t> headers;
+                        headers[U("WWW-Authenticate")] = U("Basic realm = \"WallyWorld\"");
+
+                        // unauthorized
+                        p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers);
+                    })
+                    .then([&scoped, replyFunc]() {
+                        // Client resent the request
+                        return scoped.server()->next_request().then(replyFunc);
+                    });
+
+            http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+        }
+        t.get();
+    }
+
+    // Accessing a server that returns 401 with an empty user name should not resend the request with an empty password
+    TEST_FIXTURE(uri_address, empty_username_password)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        auto t = scoped.server()->next_request().then([&](test_request* p_request) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("h1")] = U("data1");
+            // Auth header
+            headers[U("WWW-Authenticate")] = U("Basic realm = \"myRealm\"");
+            // unauthorized
+            p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers, "a");
+        });
+
+        http_response response = client.request(methods::GET).get();
+        auto str_body = response.extract_vector().get();
+        auto h1 = response.headers()[U("h1")];
+        VERIFY_ARE_EQUAL(status_codes::Unauthorized, response.status_code());
+        VERIFY_ARE_EQUAL(str_body[0], 'a');
+        VERIFY_ARE_EQUAL(h1, U("data1"));
+        t.get();
+    }
+#endif
+
+    // Fails on WinRT due to TFS 648278
+    // Accessing a server that supports auth, but returns 401, even after the user has provided valid creds
+    // We're making sure the error is reported properly, and the response data from the second response is received
+    TEST_FIXTURE(uri_address, error_after_valid_credentials, "Ignore:Linux", "89", "Ignore:Apple", "89")
+    {
+        pplx::task<void> t;
+        {
+            web::http::uri uri(U("http://localhost:34569/"));
+            test_http_server::scoped_server scoped(uri);
+            http_client_config client_config;
+            web::credentials cred(U("some_user"), U("some_password"));
+            client_config.set_credentials(cred);
+            http_client client(uri, client_config);
+
+            auto replyFunc = [&](test_request* p_request) {
+                std::map<utility::string_t, utility::string_t> headers;
+                // Auth header
+                headers[U("WWW-Authenticate")] = U("Basic realm = \"WallyWorld\"");
+                headers[U("h1")] = U("data2");
+                // still unauthorized after the user has resent the request with the credentials
+                p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers, "def");
+            };
+
+            t = scoped.server()
+                    ->next_request()
+                    .then([&](test_request* p_request) {
+                        std::map<utility::string_t, utility::string_t> headers;
+                        headers[U("h1")] = U("data1");
+                        // Auth header
+                        headers[U("WWW-Authenticate")] = U("Basic realm = \"myRealm\"");
+                        // unauthorized
+                        p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers, "abc");
+                    })
+                    .then([&scoped, &replyFunc]() {
+                        // Client resent the request
+                        return scoped.server()->next_request().then(replyFunc);
+                    })
+#ifdef __cplusplus_winrt
+                    .then([&scoped, &replyFunc]() {
+                        // in winrt, client resent the request again
+                        return scoped.server()->next_request().then(replyFunc);
+                    })
+#endif
+                ;
+
+            http_response response = client.request(methods::GET).get();
+            auto str_body = response.extract_vector().get();
+            auto h1 = response.headers()[U("h1")];
+            VERIFY_ARE_EQUAL(status_codes::Unauthorized, response.status_code());
+            VERIFY_ARE_EQUAL(str_body[0], 'd');
+            VERIFY_ARE_EQUAL(str_body[1], 'e');
+            VERIFY_ARE_EQUAL(str_body[2], 'f');
+            VERIFY_ARE_EQUAL(h1, U("data2"));
+        }
+        t.get();
+    }
+
+    // These tests are disabled since they require a server with authentication running.
+    // The server portion to use is the C# AuthenticationListener.
+
+    class server_properties
+    {
+    public:
+        server_properties() {}
+
+        // Helper function to retrieve all parameters necessary for setup tests.
+        void load_parameters()
+        {
+            m_uri = uri(utility::conversions::to_string_t(UnitTest::GlobalSettings::Get("Server")));
+            if (UnitTest::GlobalSettings::Has("UserName"))
+            {
+                m_username = utility::conversions::to_string_t(UnitTest::GlobalSettings::Get("UserName"));
+            }
+            if (UnitTest::GlobalSettings::Has("Password"))
+            {
+                m_password = utility::conversions::to_string_t(UnitTest::GlobalSettings::Get("Password"));
+            }
+        }
+
+        web::http::uri m_uri;
+        string_t m_username;
+        string_t m_password;
+    };
+
+    // This test should be executed for NTLM, Negotiate, IntegratedWindowsAuth, and Anonymous.
+    TEST_FIXTURE(server_properties, successful_auth_no_cred, "Requires", "Server")
+    {
+        load_parameters();
+
+        http_client client(m_uri);
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+    }
+
+    TEST_FIXTURE(server_properties, digest_basic_auth_no_cred, "Requires", "Server")
+    {
+        load_parameters();
+
+        http_client client(m_uri);
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(status_codes::Unauthorized, response.status_code());
+    }
+
+    TEST_FIXTURE(server_properties, none_auth_no_cred, "Requires", "Server")
+    {
+        load_parameters();
+
+        http_client client(m_uri);
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(status_codes::Forbidden, response.status_code());
+    }
+
+    // This test should be executed for NTLM, Negotiate, IntegratedWindowsAuth, and Digest.
+    TEST_FIXTURE(server_properties, unsuccessful_auth_with_basic_cred, "Requires", "Server;UserName;Password")
+    {
+        load_parameters();
+
+        http_client_config config;
+        config.set_credentials(web::credentials(m_username, m_password));
+
+        http_client client(m_uri, config);
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(status_codes::Unauthorized, response.status_code());
+    }
+
+    TEST_FIXTURE(server_properties, basic_anonymous_auth_with_basic_cred, "Requires", "Server;UserName;Password")
+    {
+        load_parameters();
+
+        http_client_config config;
+        config.set_credentials(web::credentials(m_username, m_password));
+        http_client client(m_uri, config);
+        http_request req(methods::GET);
+        req.headers().add(U("UserName"), m_username);
+        req.headers().add(U("Password"), m_password);
+        http_response response = client.request(req).get();
+        VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+    }
+
+    TEST_FIXTURE(server_properties, none_auth_with_cred, "Requires", "Server;UserName;Password")
+    {
+        load_parameters();
+
+        http_client_config config;
+        config.set_credentials(web::credentials(m_username, m_password));
+        http_client client(m_uri, config);
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(status_codes::Forbidden, response.status_code());
+    }
+
+    // This test should be executed for all authentication schemes except None.
+    TEST_FIXTURE(server_properties, successful_auth_with_domain_cred, "Requires", "Server;UserName;Password")
+    {
+        load_parameters();
+
+        http_client_config config;
+        config.set_credentials(web::credentials(m_username, m_password));
+        http_client client(m_uri, config);
+        http_request req(methods::GET);
+        req.headers().add(U("UserName"), m_username);
+        req.headers().add(U("Password"), m_password);
+        http_response response = client.request(req).get();
+        VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+    }
+
+#ifndef __cplusplus_winrt // WinRT implementation doesn't support request buffer caching.
+    TEST_FIXTURE(server_properties, failed_authentication_resend_request_error, "Requires", "Server;UserName;Password")
+    {
+        load_parameters();
+
+        http_client_config config;
+        config.set_credentials(web::credentials(m_username, m_password));
+        http_client client(m_uri, config);
+
+        const size_t rawDataSize = 8;
+
+        std::vector<unsigned char> data(rawDataSize);
+        memcpy(&data[0], "raw data", rawDataSize);
+
+        http_request request;
+        request.set_method(methods::POST);
+        request.set_body(data);
+        http_response response = client.request(request).get();
+
+        VERIFY_ARE_EQUAL(200, response.status_code());
+    }
+#endif
+
+#ifdef __cplusplus_winrt
+    TEST_FIXTURE(uri_address, set_user_options_winrt)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        scoped.server()->next_request().then([](test_request* p_request) { p_request->reply(status_codes::OK); });
+
+        http_client_config config;
+        config.set_nativehandle_options([](native_handle handle) -> void {
+            auto hr = handle->SetProperty(XHR_PROP_TIMEOUT, 1000);
+            if (!SUCCEEDED(hr)) throw std::runtime_error("The Test Exception");
+        });
+        http_client client(m_uri, config);
+        auto response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(200, response.status_code());
+    }
+#endif // __cplusplus_winrt
+
+#ifdef _WIN32
+#if !defined(__cplusplus_winrt)
+    TEST_FIXTURE(server_properties, set_user_options, "Requires", "Server;UserName;Password")
+    {
+        load_parameters();
+
+        http_client_config config;
+        config.set_credentials(web::credentials(m_username, m_password));
+
+        config.set_nativehandle_options([&](native_handle handle) -> void {
+            DWORD policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
+            if (!WinHttpSetOption(handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &policy, sizeof(policy)))
+            {
+                throw std::runtime_error("The Test Error");
+            }
+        });
+
+        http_client client(m_uri, config);
+
+        const size_t rawDataSize = 8;
+
+        std::vector<unsigned char> data(rawDataSize);
+        memcpy(&data[0], "raw data", rawDataSize);
+
+        http_request request;
+        request.set_method(methods::POST);
+        request.set_body(data);
+
+        VERIFY_ARE_EQUAL(200, client.request(request).get().status_code());
+    }
+
+    TEST_FIXTURE(uri_address, auth_producer_consumer_buffer)
+    {
+        auto buf = streams::producer_consumer_buffer<unsigned char>();
+        buf.putc('a').get();
+        buf.putc('a').get();
+        buf.putc('a').get();
+        buf.putc('a').get();
+        buf.close(std::ios_base::out).get();
+        http_request msg(methods::POST);
+        msg.set_body(buf.create_istream());
+
+        http_client_config config;
+        VERIFY_IS_FALSE(config.buffer_request());
+        config.set_buffer_request(true);
+        VERIFY_IS_TRUE(config.buffer_request());
+        config.set_credentials(web::credentials(U("USERNAME"), U("PASSWORD")));
+
+        http_client client(m_uri, config);
+
+        pplx::task<void> t, t2;
+        test_http_server::scoped_server scoped(m_uri);
+
+        t = scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(
+                p_request, methods::POST, U("/"), U("application/octet-stream"), U("aaaa"));
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("WWW-Authenticate")] = U("Basic realm = \"WallyWorld\"");
+
+            p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers);
+        });
+        t2 = scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(
+                p_request, methods::POST, U("/"), U("application/octet-stream"), U("aaaa"));
+            p_request->reply(200);
+        });
+
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+        scoped.server()->close();
+        VERIFY_NO_THROWS(t.get());
+        VERIFY_NO_THROWS(t2.get());
+    }
+
+    TEST_FIXTURE(uri_address, auth_producer_comsumer_buffer_fail_no_cred)
+    {
+        auto buf = streams::producer_consumer_buffer<unsigned char>();
+        buf.putc('a').get();
+        buf.putc('a').get();
+        buf.putc('a').get();
+        buf.putc('a').get();
+        buf.close(std::ios_base::out).get();
+        http_request msg(methods::POST);
+        msg.set_body(buf.create_istream());
+
+        http_client client(m_uri);
+
+        pplx::task<void> t;
+        {
+            test_http_server::scoped_server scoped(m_uri);
+            t = scoped.server()->next_request().then([&](test_request* p_request) {
+                http_asserts::assert_test_request_equals(
+                    p_request, methods::POST, U("/"), U("application/octet-stream"), U("aaaa"));
+                std::map<utility::string_t, utility::string_t> headers;
+                headers[U("WWW-Authenticate")] = U("Basic realm = \"WallyWorld\"");
+
+                p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers);
+            });
+
+            http_asserts::assert_response_equals(client.request(msg).get(), status_codes::Unauthorized);
+        }
+        t.get();
+    }
+
+    TEST_FIXTURE(uri_address, auth_producer_comsumer_buffer_fail)
+    {
+        auto buf = streams::producer_consumer_buffer<unsigned char>();
+        buf.putc('a').get();
+        buf.close(std::ios_base::out).get();
+        http_request msg(methods::POST);
+        msg.set_body(buf.create_istream());
+
+        http_client_config config;
+        config.set_buffer_request(true);
+        config.set_credentials(web::credentials(U("USERNAME"), U("PASSWORD")));
+
+        http_client client(m_uri, config);
+        pplx::task<void> t;
+        {
+            test_http_server::scoped_server scoped(m_uri);
+
+            auto replyFunc = [&](test_request* p_request) {
+                http_asserts::assert_test_request_equals(
+                    p_request, methods::POST, U("/"), U("application/octet-stream"), U("a"));
+                std::map<utility::string_t, utility::string_t> headers;
+                headers[U("WWW-Authenticate")] = U("Basic realm = \"WallyWorld2\"");
+
+                p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers);
+            };
+
+            t = scoped.server()
+                    ->next_request()
+                    .then([&](test_request* p_request) {
+                        http_asserts::assert_test_request_equals(
+                            p_request, methods::POST, U("/"), U("application/octet-stream"), U("a"));
+                        std::map<utility::string_t, utility::string_t> headers;
+                        headers[U("WWW-Authenticate")] = U("Basic realm = \"WallyWorld\"");
+
+                        p_request->reply(status_codes::Unauthorized, U("Authentication Failed"), headers);
+                    })
+                    .then([&scoped, replyFunc]() { return scoped.server()->next_request().then(replyFunc); });
+
+            http_asserts::assert_response_equals(client.request(msg).get(), status_codes::Unauthorized);
+        }
+        VERIFY_NO_THROWS(t.get());
+    }
+#endif
+
+    TEST_FIXTURE(uri_address, set_user_options_exceptions)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client_config config;
+        class TestException;
+        config.set_nativehandle_options([](native_handle) { throw std::runtime_error("The Test exception"); });
+        http_client client(m_uri, config);
+        VERIFY_THROWS(client.request(methods::GET).get(), std::runtime_error);
+    }
+#endif // _WIN32
+
+    // Fix for 522831 AV after failed authentication attempt
+    TEST_FIXTURE(uri_address, failed_authentication_attempt, "Ignore:Linux", "89", "Ignore:Apple", "89")
+    {
+        handle_timeout([] {
+            http_client_config config;
+            web::credentials cred(U("user"), U("schmuser"));
+            config.set_credentials(cred);
+            http_client client(U("https://apis.live.net"), config);
+            http_response response = client.request(methods::GET, U("V5.0/me/skydrive/files")).get();
+            VERIFY_ARE_EQUAL(status_codes::Unauthorized, response.status_code());
+            auto v = response.extract_vector().get();
+            std::string s(v.begin(), v.end());
+            // The resulting data must be non-empty (an error about missing access token)
+            VERIFY_IS_FALSE(s.empty());
+        });
+    }
+
+#if !defined(_WIN32)
+
+    // http_server does not support auth
+    void auth_test_impl(bool fail)
+    {
+        std::string user("user1"), password("user1");
+        auto return_code = status_codes::OK;
+
+        if (fail)
+        {
+            password = "invalid";
+            return_code = status_codes::Unauthorized;
+        }
+
+        http_client_config client_config;
+        web::credentials cred(U(user), U(password));
+        client_config.set_credentials(cred);
+        http_client client(U("http://httpbin.org/basic-auth/user1/user1"), client_config);
+
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(return_code, response.status_code());
+    }
+
+    TEST(auth_no_data) { auth_test_impl(false); }
+
+    TEST(unsuccessful_auth_with_basic_cred) { auth_test_impl(true); }
+
+    TEST_FIXTURE(uri_address, set_user_options_asio_http)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        scoped.server()->next_request().then([](test_request* p_request) { p_request->reply(status_codes::OK); });
+
+        http_client_config config;
+        config.set_nativehandle_options([](native_handle handle) {
+            boost::asio::ip::tcp::socket* socket = static_cast<boost::asio::ip::tcp::socket*>(handle);
+            // Socket shouldn't be open yet since no requests have gone out.
+            VERIFY_ARE_EQUAL(false, socket->is_open());
+        });
+        http_client client(m_uri, config);
+        auto response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(200, response.status_code());
+    }
+
+    TEST_FIXTURE(uri_address, set_user_options_asio_https)
+    {
+        handle_timeout([] {
+            http_client_config config;
+            config.set_nativehandle_options([](native_handle handle) {
+                boost::asio::ssl::stream<boost::asio::ip::tcp::socket&>* streamobj =
+                    static_cast<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&>*>(handle);
+                const auto& tcpLayer = streamobj->lowest_layer();
+                VERIFY_ARE_EQUAL(false, tcpLayer.is_open());
+            });
+
+            http_client client(U("https://apis.live.net"), config);
+            http_response response = client.request(methods::GET, U("V5.0/me/skydrive/files")).get();
+            VERIFY_ARE_EQUAL(status_codes::Unauthorized, response.status_code());
+            auto v = response.extract_vector().get();
+            // The resulting data must be non-empty (an error about missing access token)
+            VERIFY_IS_FALSE(v.empty());
+        });
+    }
+
+#endif
+
+} // SUITE(authentication_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/building_request_tests.cpp b/Release/tests/functional/http/client/building_request_tests.cpp
new file mode 100644 (file)
index 0000000..4fa379c
--- /dev/null
@@ -0,0 +1,334 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * building_request_tests.cpp
+ *
+ * Tests cases manually building up HTTP requests.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#ifdef _WIN32
+#include <WinError.h>
+#endif
+
+#include <locale_guard.h>
+
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(building_request_tests)
+{
+    TEST_FIXTURE(uri_address, simple_values)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        pplx::task<void> t1, t2;
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        // Set a method.
+        const method method = methods::OPTIONS;
+        http_request msg(method);
+        VERIFY_ARE_EQUAL(method, msg.method());
+
+        // Set a path once.
+        const utility::string_t custom_path1 = U("/hey/custom/path");
+        msg.set_request_uri(custom_path1);
+        VERIFY_ARE_EQUAL(custom_path1, msg.relative_uri().to_string());
+        t1 = p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, method, custom_path1);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        // Set the path twice.
+        msg = http_request(method);
+        msg.set_request_uri(custom_path1);
+        VERIFY_ARE_EQUAL(custom_path1, msg.relative_uri().to_string());
+        const utility::string_t custom_path2 = U("/yes/you/there");
+        msg.set_request_uri(custom_path2);
+        VERIFY_ARE_EQUAL(custom_path2, msg.relative_uri().to_string());
+        t2 = p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, method, custom_path2);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+        p_server->close();
+        try
+        {
+            t1.get();
+        }
+        catch (...)
+        {
+            VERIFY_ARE_EQUAL(0, 1, "t1 failed");
+        }
+        try
+        {
+            t2.get();
+        }
+        catch (...)
+        {
+            VERIFY_ARE_EQUAL(0, 2, "t2 failed");
+        }
+    }
+
+    TEST_FIXTURE(uri_address, body_types)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        // Body data types.
+        const method method(U("CUSTOMmethod"));
+        utility::string_t str_body(U("YES_BASIC_STRING BODY"));
+        utility::string_t str_move_body(str_body);
+        std::vector<unsigned char> vector_body;
+        vector_body.resize(str_body.size() * sizeof(utility::char_t));
+        memcpy(&vector_body[0], &str_body[0], str_body.size() * sizeof(utility::char_t));
+        std::vector<unsigned char> vector_move_body(vector_body);
+        utility::string_t custom_content = U("YESNOW!");
+
+        // vector - no content type.
+        http_request msg(method);
+        msg.set_body(std::move(vector_move_body));
+        VERIFY_ARE_EQUAL(U("application/octet-stream"), msg.headers()[U("Content-Type")]);
+        p_server->next_request().then([&](test_request* p_request) {
+            auto received = p_request->m_body;
+            auto sent = vector_body;
+            VERIFY_IS_TRUE(received == sent);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        // vector - with content type.
+        msg = http_request(method);
+        vector_move_body = vector_body;
+        msg.headers().add(U("Content-Type"), custom_content);
+        msg.set_body(std::move(vector_move_body));
+        VERIFY_ARE_EQUAL(custom_content, msg.headers()[U("Content-Type")]);
+        p_server->next_request().then([&](test_request* p_request) {
+            auto received = p_request->m_body;
+            auto sent = vector_body;
+            VERIFY_IS_TRUE(received == sent);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        // string - no content type.
+        msg = http_request(method);
+        msg.set_body(std::move(str_move_body));
+        VERIFY_ARE_EQUAL(U("text/plain; charset=utf-8"), msg.headers()[U("Content-Type")]);
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(
+                p_request, method, U("/"), U("text/plain; charset=utf-8"), str_body);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        // string - with content type.
+        msg = http_request(method);
+        str_move_body = str_body;
+        msg.headers().add(U("Content-Type"), custom_content);
+        msg.set_body(std::move(str_move_body));
+        VERIFY_ARE_EQUAL(custom_content, msg.headers()[U("Content-Type")]);
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, method, U("/"), custom_content, str_body);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+    }
+
+    TEST(set_body_string_with_charset)
+    {
+        http_request request;
+        VERIFY_THROWS(request.set_body(::utility::conversions::to_utf16string("body_data"),
+                                       ::utility::conversions::to_utf16string("text/plain;charset=utf-16")),
+                      std::invalid_argument);
+    }
+
+    TEST_FIXTURE(uri_address, empty_bodies)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        // Body data.
+        std::string empty_str;
+        std::vector<unsigned char> vector_body;
+        utility::string_t str_body;
+        utility::string_t wstr_body;
+
+        // empty vector.
+        const method method(methods::PUT);
+        http_request msg(method);
+        msg.set_body(std::move(vector_body));
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, method, U("/"), U("application/octet-stream"));
+            VERIFY_ARE_EQUAL(0u, p_request->m_body.size());
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        // empty string.
+        msg = http_request(method);
+        msg.set_body(std::move(str_body));
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, method, U("/"), U("text/plain; charset=utf-8"));
+            VERIFY_ARE_EQUAL(0u, p_request->m_body.size());
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        // empty wstring.
+        msg = http_request(method);
+        msg.set_body(std::move(wstr_body));
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, method, U("/"), U("text/plain; charset=utf-8"));
+            VERIFY_ARE_EQUAL(0u, p_request->m_body.size());
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, set_body)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        const method mtd = methods::POST;
+        utility::string_t data(U("YOU KNOW~!!!!!"));
+        utility::string_t content_type = U("text/plain; charset=utf-8");
+
+        // without content type
+        http_request msg(mtd);
+        msg.set_body(data);
+        VERIFY_ARE_EQUAL(content_type, msg.headers()[U("Content-Type")]);
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, mtd, U("/"), content_type, data);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        // with content type
+        content_type = U("YESYES");
+#ifdef _UTF16_STRINGS
+        const utility::string_t expected_content_type = U("YESYES; charset=utf-8");
+#else
+        const utility::string_t expected_content_type = U("YESYES");
+#endif
+        msg = http_request(mtd);
+        msg.set_body(data, content_type);
+        VERIFY_ARE_EQUAL(expected_content_type, msg.headers()[U("Content-Type")]);
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, mtd, U("/"), expected_content_type, data);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, set_body_with_charset)
+    {
+        http_request msg(methods::PUT);
+        msg.set_body("datadatadata", "text/plain;charset=us-ascii");
+        VERIFY_THROWS(msg.set_body(::utility::conversions::to_utf16string("datadatadata"),
+                                   ::utility::conversions::to_utf16string("text/plain;charset=us-ascii")),
+                      std::invalid_argument);
+    }
+
+    TEST_FIXTURE(uri_address, set_content_length_locale, "Ignore:Android", "Locale unsupported on Android")
+    {
+        std::locale changedLocale;
+        try
+        {
+#ifdef _WIN32
+            changedLocale = std::locale("fr-FR");
+#else
+            changedLocale = std::locale("fr_FR.UTF-8");
+#endif
+        }
+        catch (const std::exception&)
+        {
+            // Silently pass if locale isn't installed on the machine.
+            return;
+        }
+
+        tests::common::utilities::locale_guard loc(changedLocale);
+
+        http_request req(methods::PUT);
+        req.headers().set_content_length(1000);
+        VERIFY_ARE_EQUAL(U("1000"), req.headers()[web::http::header_names::content_length]); // fr_RF would have 1 000
+    }
+
+    TEST_FIXTURE(uri_address, set_port_locale, "Ignore:Android", "Locale unsupported on Android")
+    {
+        std::locale changedLocale;
+        try
+        {
+#ifdef _WIN32
+            changedLocale = std::locale("fr-FR");
+#else
+            changedLocale = std::locale("fr_FR.UTF-8");
+#endif
+        }
+        catch (const std::exception&)
+        {
+            // Silently pass if locale isn't installed on machine.
+            return;
+        }
+        tests::common::utilities::locale_guard loc(changedLocale);
+
+        test_http_server::scoped_server scoped(m_uri);
+        pplx::task<void> t;
+        http_client client(m_uri);
+
+        utility::string_t data(U("STRING data 1000"));
+        t = scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(
+                p_request, methods::PUT, U("/"), U("text/plain; charset=utf-8"), data);
+            p_request->reply(200);
+        });
+
+        http_request msg(methods::PUT);
+        msg.set_body(data);
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        scoped.server()->close();
+        t.get();
+    }
+
+    TEST_FIXTURE(uri_address, reuse_request)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        http_request msg(methods::GET);
+        for (int i = 0; i < 3; ++i)
+        {
+            p_server->next_request().then([](test_request* p_request) {
+                http_asserts::assert_test_request_equals(p_request, methods::GET, U("/"));
+                p_request->reply(200);
+            });
+            http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+        }
+    }
+}
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/client_construction.cpp b/Release/tests/functional/http/client/client_construction.cpp
new file mode 100644 (file)
index 0000000..acad00e
--- /dev/null
@@ -0,0 +1,227 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * client_construction.cpp
+ *
+ * Tests cases for covering creating http_clients.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <fstream>
+
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(client_construction)
+{
+    // Tests using different types of strings to construct an http_client.
+    TEST_FIXTURE(uri_address, string_types)
+    {
+        // The goal of this test case is to make sure we can compile,
+        // if the URI class doesn't have the proper constructors it won't.
+        // So we don't need to actually do a request.
+        http_client c1(U("http://localhost:4567/"));
+        http_client c3(utility::string_t(U("http://localhost:4567/")));
+    }
+
+    // Tests different variations on specifying the URI in http_client constructor.
+    TEST_FIXTURE(uri_address, different_uris)
+    {
+        const utility::string_t paths[] = {U(""), U("/"), U("/toplevel/nested"), U("/toplevel/nested/")};
+        const utility::string_t expected_paths[] = {U("/"), U("/"), U("/toplevel/nested"), U("/toplevel/nested/")};
+        const size_t num_paths = sizeof(paths) / sizeof(paths[0]);
+        for (size_t i = 0; i < num_paths; ++i)
+        {
+            uri address(U("http://localhost:55678") + paths[i]);
+            test_http_server::scoped_server scoped(address);
+            http_client client(address);
+            test_connection(scoped.server(), &client, expected_paths[i]);
+        }
+    }
+
+    // Helper function verifies that when constructing an http_client with given
+    // URI std::invalid_argument is thrown.
+    static void verify_client_invalid_argument(const uri& address)
+    {
+        try
+        {
+            http_client client(address);
+            VERIFY_IS_TRUE(false);
+        }
+        catch (std::invalid_argument&)
+        {
+            // expected
+        }
+    }
+
+    TEST_FIXTURE(uri_address, client_construction_error_cases)
+    {
+        uri address(U("nothttp://localhost:34567/"));
+
+        // Invalid scheme.
+        verify_client_invalid_argument(address);
+
+        // empty host.
+        address = uri(U("http://:34567/"));
+        verify_client_invalid_argument(address);
+    }
+
+    TEST_FIXTURE(uri_address, client_construction_no_scheme)
+    {
+        uri address(U("//localhost:34568/p/g"));
+        test_http_server::scoped_server scoped(m_uri);
+
+        http_client client(address);
+        test_connection(scoped.server(), &client, U("/p/g"));
+    }
+
+    TEST_FIXTURE(uri_address, copy_assignment)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+
+        // copy constructor
+        http_client original(m_uri);
+        http_client new_client(original);
+        test_connection(scoped.server(), &new_client, U("/"));
+        test_connection(scoped.server(), &original, U("/"));
+
+        // assignment
+        http_client new_client2(U("http://bad:-1"));
+        new_client2 = original;
+        test_connection(scoped.server(), &new_client2, U("/"));
+        test_connection(scoped.server(), &original, U("/"));
+    }
+
+    TEST_FIXTURE(uri_address, move_not_init)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+
+        // move constructor
+        http_client original(m_uri);
+        http_client new_client = std::move(original);
+        test_connection(scoped.server(), &new_client, U("/"));
+
+        // move assignment
+        original = http_client(m_uri);
+        test_connection(scoped.server(), &original, U("/"));
+    }
+
+    TEST_FIXTURE(uri_address, move_init)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+
+        // move constructor
+        http_client original(m_uri);
+        test_connection(scoped.server(), &original, U("/"));
+        http_client new_client = std::move(original);
+        test_connection(scoped.server(), &new_client, U("/"));
+
+        // move assignment
+        original = http_client(m_uri);
+        test_connection(scoped.server(), &original, U("/"));
+    }
+
+    // Verify that we can read the config from the http_client
+    TEST_FIXTURE(uri_address, get_client_config)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+
+        http_client_config config;
+
+        VERIFY_ARE_EQUAL(config.chunksize(), 64 * 1024);
+        config.set_chunksize(1024);
+        VERIFY_ARE_EQUAL(config.chunksize(), 1024);
+
+        utility::seconds timeout(100);
+        config.set_timeout(timeout);
+        http_client client(m_uri, config);
+
+        const http_client_config& config2 = client.client_config();
+        VERIFY_ARE_EQUAL(config2.timeout().count(), timeout.count());
+        std::chrono::milliseconds milli_timeout = config2.timeout();
+        VERIFY_ARE_EQUAL(milli_timeout.count(), std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count());
+        auto micro_timeout = config.timeout<std::chrono::microseconds>();
+        VERIFY_ARE_EQUAL(micro_timeout.count(), std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
+
+        VERIFY_ARE_EQUAL(config2.chunksize(), 1024);
+    }
+
+    // Verify that we can get the baseuri from http_client constructors
+    TEST_FIXTURE(uri_address, BaseURI_test)
+    {
+        http_client baseclient1(m_uri);
+        VERIFY_ARE_EQUAL(baseclient1.base_uri(), m_uri);
+
+        http_client_config config;
+        http_client baseclient2(m_uri, config);
+        VERIFY_ARE_EQUAL(baseclient2.base_uri(), m_uri);
+    }
+
+#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)
+    {
+        http_client_config config;
+        bool called = false;
+
+        config.set_ssl_context_callback([&called](boost::asio::ssl::context& ctx) { called = true; });
+
+        http_client client(U("https://www.google.com/"), config);
+
+        try
+        {
+            client.request(methods::GET, U("/")).get();
+        }
+        catch (...)
+        {
+        }
+
+        VERIFY_IS_TRUE(called, "The sslcontext options is not called for HTTPS protocol");
+    }
+
+    // Verify that the callback of sslcontext is not called for HTTP
+    TEST_FIXTURE(uri_address, ssl_context_callback_http)
+    {
+        http_client_config config;
+        bool called = false;
+
+        config.set_ssl_context_callback([&called](boost::asio::ssl::context& ctx) { called = true; });
+
+        http_client client(U("http://www.google.com/"), config);
+
+        try
+        {
+            client.request(methods::GET, U("/")).get();
+        }
+        catch (...)
+        {
+        }
+
+        VERIFY_IS_FALSE(called, "The sslcontext options is called for HTTP protocol");
+    }
+
+#endif
+
+} // SUITE(client_construction)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/compression_tests.cpp b/Release/tests/functional/http/client/compression_tests.cpp
new file mode 100644 (file)
index 0000000..46ac3f4
--- /dev/null
@@ -0,0 +1,1356 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * compression_tests.cpp
+ *
+ * Tests cases, including client/server, for the web::http::compression namespace.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/details/http_helpers.h"
+#include "cpprest/version.h"
+#include <fstream>
+
+#ifndef __cplusplus_winrt
+#include "cpprest/http_listener.h"
+#endif
+
+using namespace web;
+using namespace utility;
+using namespace web::http;
+using namespace web::http::client;
+using namespace web::http::compression;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(compression_tests)
+{
+    // A fake "pass-through" compressor/decompressor for testing
+    class fake_provider : public compress_provider, public decompress_provider
+    {
+    public:
+        static const utility::string_t FAKE;
+
+        fake_provider(size_t size = static_cast<size_t>(-1)) : _size(size), _so_far(0), _done(false) {}
+
+        virtual const utility::string_t& algorithm() const { return FAKE; }
+
+        virtual size_t decompress(const uint8_t* input,
+                                  size_t input_size,
+                                  uint8_t* output,
+                                  size_t output_size,
+                                  operation_hint hint,
+                                  size_t& input_bytes_processed,
+                                  bool& done)
+        {
+            size_t bytes;
+
+            if (_done)
+            {
+                input_bytes_processed = 0;
+                done = true;
+                return 0;
+            }
+            if (_size == static_cast<size_t>(-1) || input_size > _size - _so_far)
+            {
+                std::stringstream ss;
+                ss << "Fake decompress - invalid data " << input_size << ", " << output_size << " with " << _so_far
+                   << " / " << _size;
+                throw std::runtime_error(std::move(ss.str()));
+            }
+            bytes = (std::min)(input_size, output_size);
+            if (bytes)
+            {
+                memcpy(output, input, bytes);
+            }
+            _so_far += bytes;
+            _done = (_so_far == _size);
+            done = _done;
+            input_bytes_processed = bytes;
+            return input_bytes_processed;
+        }
+
+        virtual pplx::task<operation_result> decompress(
+            const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint)
+        {
+            operation_result r;
+
+            try
+            {
+                r.output_bytes_produced =
+                    decompress(input, input_size, output, output_size, hint, r.input_bytes_processed, r.done);
+            }
+            catch (...)
+            {
+                pplx::task_completion_event<operation_result> ev;
+                ev.set_exception(std::current_exception());
+                return pplx::create_task(ev);
+            }
+
+            return pplx::task_from_result<operation_result>(r);
+        }
+
+        virtual size_t compress(const uint8_t* input,
+                                size_t input_size,
+                                uint8_t* output,
+                                size_t output_size,
+                                operation_hint hint,
+                                size_t& input_bytes_processed,
+                                bool& done)
+        {
+            size_t bytes;
+
+            if (_done)
+            {
+                input_bytes_processed = 0;
+                done = true;
+                return 0;
+            }
+            if (_size == static_cast<size_t>(-1) || input_size > _size - _so_far)
+            {
+                std::stringstream ss;
+                ss << "Fake compress - invalid data " << input_size << ", " << output_size << " with " << _so_far
+                   << " / " << _size;
+                throw std::runtime_error(std::move(ss.str()));
+            }
+            bytes = (std::min)(input_size, output_size);
+            if (bytes)
+            {
+                memcpy(output, input, bytes);
+            }
+            _so_far += bytes;
+            _done = (hint == operation_hint::is_last && _so_far == _size);
+            done = _done;
+            input_bytes_processed = bytes;
+            return input_bytes_processed;
+        }
+
+        virtual pplx::task<operation_result> compress(
+            const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint)
+        {
+            operation_result r;
+
+            try
+            {
+                r.output_bytes_produced =
+                    compress(input, input_size, output, output_size, hint, r.input_bytes_processed, r.done);
+            }
+            catch (...)
+            {
+                pplx::task_completion_event<operation_result> ev;
+                ev.set_exception(std::current_exception());
+                return pplx::create_task(ev);
+            }
+
+            return pplx::task_from_result<operation_result>(r);
+        }
+
+        virtual void reset()
+        {
+            _done = false;
+            _so_far = 0;
+        }
+
+    private:
+        size_t _size;
+        size_t _so_far;
+        bool _done;
+    };
+
+    const utility::string_t fake_provider::FAKE = _XPLATSTR("fake");
+
+    void compress_and_decompress(std::unique_ptr<compress_provider> compressor,
+                                 std::unique_ptr<decompress_provider> decompressor,
+                                 const size_t buffer_size,
+                                 const size_t chunk_size,
+                                 bool compressible)
+    {
+        std::vector<uint8_t> input_buffer;
+        size_t i;
+
+        VERIFY_ARE_EQUAL(compressor->algorithm(), decompressor->algorithm());
+
+        input_buffer.reserve(buffer_size);
+        for (i = 0; i < buffer_size; ++i)
+        {
+            uint8_t element;
+            if (compressible)
+            {
+                element = static_cast<uint8_t>('a' + i % 26);
+            }
+            else
+            {
+                element = static_cast<uint8_t>(std::rand());
+            }
+
+            input_buffer.push_back(element);
+        }
+
+        // compress in chunks
+        std::vector<size_t> chunk_sizes;
+        std::vector<uint8_t> cmp_buffer(buffer_size);
+        size_t cmpsize = buffer_size;
+        size_t csize = 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)
+        {
+            if (i == buffer_size)
+            {
+                // the entire input buffer has been consumed by the compressor
+                hint = operation_hint::is_last;
+            }
+            if (csize == cmpsize)
+            {
+                // extend the output buffer if there may be more compressed bytes to retrieve
+                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),
+                               cmp_buffer.data() + 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(hint == operation_hint::is_last || !r.done);
+            chunk_sizes.push_back(r.output_bytes_produced);
+            csize += r.output_bytes_produced;
+        }
+        VERIFY_ARE_EQUAL(r.done, true);
+
+        // once more with no input or output, to assure no error and done
+        r = compressor->compress(NULL, 0, NULL, 0, operation_hint::is_last).get();
+        VERIFY_ARE_EQUAL(r.input_bytes_processed, 0);
+        VERIFY_ARE_EQUAL(r.output_bytes_produced, 0);
+        VERIFY_ARE_EQUAL(r.done, true);
+
+        cmp_buffer.resize(csize); // actual
+
+        // decompress in as-compressed chunks
+        std::vector<uint8_t> dcmp_buffer(buffer_size);
+        size_t dsize = 0;
+        size_t nn = 0;
+        for (std::vector<size_t>::iterator it = chunk_sizes.begin(); it != chunk_sizes.end(); ++it)
+        {
+            if (*it)
+            {
+                auto hint = operation_hint::has_more;
+                if (it == chunk_sizes.begin())
+                {
+                    hint = operation_hint::is_last;
+                }
+
+                r = decompressor
+                        ->decompress(cmp_buffer.data() + nn,
+                                     *it,
+                                     dcmp_buffer.data() + dsize,
+                                     (std::min)(chunk_size, buffer_size - dsize),
+                                     hint)
+                        .get();
+                nn += *it;
+                dsize += r.output_bytes_produced;
+            }
+        }
+        VERIFY_ARE_EQUAL(csize, nn);
+        VERIFY_ARE_EQUAL(dsize, buffer_size);
+        VERIFY_ARE_EQUAL(input_buffer, dcmp_buffer);
+        VERIFY_IS_TRUE(r.done);
+
+        // decompress again in fixed-size chunks
+        nn = 0;
+        dsize = 0;
+        decompressor->reset();
+        memset(dcmp_buffer.data(), 0, dcmp_buffer.size());
+        do
+        {
+            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),
+                                     operation_hint::has_more)
+                        .get();
+                dsize += r.output_bytes_produced;
+                nn += r.input_bytes_processed;
+                n -= r.input_bytes_processed;
+            } while (n);
+        } while (nn < csize || !r.done);
+        VERIFY_ARE_EQUAL(csize, nn);
+        VERIFY_ARE_EQUAL(dsize, buffer_size);
+        VERIFY_ARE_EQUAL(input_buffer, dcmp_buffer);
+        VERIFY_IS_TRUE(r.done);
+
+        // once more with no input, to assure no error and done
+        r = decompressor->decompress(NULL, 0, NULL, 0, operation_hint::has_more).get();
+        VERIFY_ARE_EQUAL(r.input_bytes_processed, 0);
+        VERIFY_ARE_EQUAL(r.output_bytes_produced, 0);
+        VERIFY_IS_TRUE(r.done);
+
+        // decompress all at once
+        decompressor->reset();
+        memset(dcmp_buffer.data(), 0, dcmp_buffer.size());
+        r = decompressor
+                ->decompress(cmp_buffer.data(), csize, dcmp_buffer.data(), dcmp_buffer.size(), operation_hint::is_last)
+                .get();
+        VERIFY_ARE_EQUAL(r.output_bytes_produced, buffer_size);
+        VERIFY_ARE_EQUAL(input_buffer, dcmp_buffer);
+
+        if (decompressor->algorithm() != fake_provider::FAKE)
+        {
+            // invalid decompress buffer, first and subsequent tries
+            cmp_buffer[0] = ~cmp_buffer[1];
+            decompressor->reset();
+            for (i = 0; i < 2; i++)
+            {
+                nn = 0;
+                try
+                {
+                    r = decompressor
+                            ->decompress(cmp_buffer.data(),
+                                         csize,
+                                         dcmp_buffer.data(),
+                                         dcmp_buffer.size(),
+                                         operation_hint::is_last)
+                            .get();
+                    VERIFY_IS_FALSE(r.done && r.output_bytes_produced == buffer_size);
+                }
+                catch (std::runtime_error)
+                {
+                }
+            }
+        }
+    }
+
+    void compress_test(std::shared_ptr<compress_factory> cfactory, std::shared_ptr<decompress_factory> dfactory)
+    {
+        size_t tuples[][2] = {{3, 1024},
+                              {7999, 8192},
+                              {8192, 8192},
+                              {16001, 8192},
+                              {16384, 8192},
+                              {140000, 65536},
+                              {256 * 1024, 65536},
+                              {256 * 1024, 256 * 1024},
+                              {263456, 256 * 1024}};
+
+        for (int i = 0; i < sizeof(tuples) / sizeof(tuples[0]); i++)
+        {
+            for (int j = 0; j < 2; j++)
+            {
+                if (!cfactory)
+                {
+                    auto size = tuples[i][0];
+                    compress_and_decompress(utility::details::make_unique<fake_provider>(size),
+                                            utility::details::make_unique<fake_provider>(size),
+                                            size,
+                                            tuples[i][1],
+                                            !!j);
+                }
+                else
+                {
+                    compress_and_decompress(
+                        cfactory->make_compressor(), dfactory->make_decompressor(), tuples[i][0], tuples[i][1], !!j);
+                }
+            }
+        }
+    }
+
+    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),
+                          builtin::get_decompress_factory(builtin::algorithm::BROTLI));
+        }
+    }
+
+    TEST_FIXTURE(uri_address, compress_headers)
+    {
+        const utility::string_t _NONE = _XPLATSTR("none");
+
+        std::unique_ptr<compress_provider> c;
+        std::unique_ptr<decompress_provider> d;
+
+        std::shared_ptr<compress_factory> fcf =
+            make_compress_factory(fake_provider::FAKE, []() -> std::unique_ptr<compress_provider> {
+                return utility::details::make_unique<fake_provider>();
+            });
+        std::vector<std::shared_ptr<compress_factory>> fcv;
+        fcv.push_back(fcf);
+        std::shared_ptr<decompress_factory> fdf =
+            make_decompress_factory(fake_provider::FAKE, 800, []() -> std::unique_ptr<decompress_provider> {
+                return utility::details::make_unique<fake_provider>();
+            });
+        std::vector<std::shared_ptr<decompress_factory>> fdv;
+        fdv.push_back(fdf);
+
+        std::shared_ptr<compress_factory> ncf =
+            make_compress_factory(_NONE, []() -> std::unique_ptr<compress_provider> {
+                return utility::details::make_unique<fake_provider>();
+            });
+        std::vector<std::shared_ptr<compress_factory>> ncv;
+        ncv.push_back(ncf);
+        std::shared_ptr<decompress_factory> ndf =
+            make_decompress_factory(_NONE, 800, []() -> std::unique_ptr<decompress_provider> {
+                return utility::details::make_unique<fake_provider>();
+            });
+        std::vector<std::shared_ptr<decompress_factory>> ndv;
+        ndv.push_back(ndf);
+
+        // Supported algorithms
+        VERIFY_ARE_EQUAL(builtin::supported(), builtin::algorithm::supported(builtin::algorithm::GZIP));
+        VERIFY_ARE_EQUAL(builtin::supported(), builtin::algorithm::supported(builtin::algorithm::DEFLATE));
+        if (builtin::algorithm::supported(builtin::algorithm::BROTLI))
+        {
+            VERIFY_IS_TRUE(builtin::supported());
+        }
+        VERIFY_IS_FALSE(builtin::algorithm::supported(_XPLATSTR("")));
+        VERIFY_IS_FALSE(builtin::algorithm::supported(_XPLATSTR("foo")));
+
+        // Strings that double as both Transfer-Encoding and TE
+        std::vector<utility::string_t> encodings = {_XPLATSTR("gzip"),
+                                                    _XPLATSTR("gZip  "),
+                                                    _XPLATSTR(" GZIP"),
+                                                    _XPLATSTR(" gzip "),
+                                                    _XPLATSTR("  gzip  ,   chunked  "),
+                                                    _XPLATSTR(" gZip , chunked "),
+                                                    _XPLATSTR("GZIP,chunked")};
+
+        // Similar, but geared to match a non-built-in algorithm
+        std::vector<utility::string_t> fake = {_XPLATSTR("fake"),
+                                               _XPLATSTR("faKe  "),
+                                               _XPLATSTR(" FAKE"),
+                                               _XPLATSTR(" fake "),
+                                               _XPLATSTR("  fake  ,   chunked  "),
+                                               _XPLATSTR(" faKe , chunked "),
+                                               _XPLATSTR("FAKE,chunked")};
+
+        std::vector<utility::string_t> invalid = {_XPLATSTR(","),
+                                                  _XPLATSTR(",gzip"),
+                                                  _XPLATSTR("gzip,"),
+                                                  _XPLATSTR(",gzip, chunked"),
+                                                  _XPLATSTR(" ,gzip, chunked"),
+                                                  _XPLATSTR("gzip, chunked,"),
+                                                  _XPLATSTR("gzip, chunked, "),
+                                                  _XPLATSTR("gzip,, chunked"),
+                                                  _XPLATSTR("gzip , , chunked"),
+                                                  _XPLATSTR("foo")};
+
+        std::vector<utility::string_t> invalid_tes = {
+            _XPLATSTR("deflate;q=0.5, gzip;q=2"),
+            _XPLATSTR("deflate;q=1.5, gzip;q=1"),
+        };
+
+        std::vector<utility::string_t> empty = {_XPLATSTR(""), _XPLATSTR(" ")};
+
+        // Repeat for Transfer-Encoding (which also covers part of TE) and Content-Encoding (which also covers all of
+        // Accept-Encoding)
+        for (int transfer = 0; transfer < 2; transfer++)
+        {
+            compression::details::header_types ctype =
+                transfer ? compression::details::header_types::te : compression::details::header_types::accept_encoding;
+            compression::details::header_types dtype = transfer ? compression::details::header_types::transfer_encoding
+                                                                : compression::details::header_types::content_encoding;
+
+            // No compression - Transfer-Encoding
+            d = compression::details::get_decompressor_from_header(
+                _XPLATSTR(" chunked "), compression::details::header_types::transfer_encoding);
+            VERIFY_IS_FALSE((bool)d);
+
+            utility::string_t gzip(builtin::algorithm::GZIP);
+            for (auto encoding = encodings.begin(); encoding != encodings.end(); encoding++)
+            {
+                bool has_comma = false;
+
+                has_comma = encoding->find(_XPLATSTR(",")) != utility::string_t::npos;
+
+                // Built-in only
+                c = compression::details::get_compressor_from_header(*encoding, ctype);
+                VERIFY_ARE_EQUAL((bool)c, builtin::supported());
+                if (c)
+                {
+                    VERIFY_ARE_EQUAL(c->algorithm(), gzip);
+                }
+
+                try
+                {
+                    d = compression::details::get_decompressor_from_header(*encoding, dtype);
+                    VERIFY_ARE_EQUAL((bool)d, builtin::supported());
+                    if (d)
+                    {
+                        VERIFY_ARE_EQUAL(d->algorithm(), gzip);
+                    }
+                }
+                catch (http_exception)
+                {
+                    VERIFY_IS_TRUE(transfer == !has_comma);
+                }
+            }
+
+            for (auto encoding = fake.begin(); encoding != fake.end(); encoding++)
+            {
+                bool has_comma = false;
+
+                has_comma = encoding->find(_XPLATSTR(",")) != utility::string_t::npos;
+
+                // Supplied compressor/decompressor
+                c = compression::details::get_compressor_from_header(*encoding, ctype, fcv);
+                VERIFY_IS_TRUE((bool)c);
+                VERIFY_IS_TRUE(c->algorithm() == fcf->algorithm());
+
+                try
+                {
+                    d = compression::details::get_decompressor_from_header(*encoding, dtype, fdv);
+                    VERIFY_IS_TRUE((bool)d);
+                    VERIFY_IS_TRUE(d->algorithm() == fdf->algorithm());
+                }
+                catch (http_exception)
+                {
+                    VERIFY_IS_TRUE(transfer == !has_comma);
+                }
+
+                // No matching compressor
+                c = compression::details::get_compressor_from_header(*encoding, ctype, ncv);
+                VERIFY_IS_FALSE((bool)c);
+
+                try
+                {
+                    d = compression::details::get_decompressor_from_header(*encoding, dtype, ndv);
+                    VERIFY_IS_FALSE(true);
+                }
+                catch (http_exception)
+                {
+                }
+            }
+
+            // Negative tests - invalid headers, no matching algorithm, etc.
+            for (auto encoding = invalid.begin(); encoding != invalid.end(); encoding++)
+            {
+                try
+                {
+                    c = compression::details::get_compressor_from_header(*encoding, ctype);
+                    VERIFY_IS_TRUE(encoding->find(_XPLATSTR(",")) == utility::string_t::npos);
+                    VERIFY_IS_FALSE((bool)c);
+                }
+                catch (http_exception)
+                {
+                }
+
+                try
+                {
+                    d = compression::details::get_decompressor_from_header(*encoding, dtype);
+                    VERIFY_IS_TRUE(!builtin::supported() && encoding->find(_XPLATSTR(",")) == utility::string_t::npos);
+                    VERIFY_IS_FALSE((bool)d);
+                }
+                catch (http_exception)
+                {
+                }
+            }
+
+            // Negative tests - empty headers
+            for (auto encoding = empty.begin(); encoding != empty.end(); encoding++)
+            {
+                c = compression::details::get_compressor_from_header(*encoding, ctype);
+                VERIFY_IS_FALSE((bool)c);
+
+                try
+                {
+                    d = compression::details::get_decompressor_from_header(*encoding, dtype);
+                    VERIFY_IS_FALSE(true);
+                }
+                catch (http_exception)
+                {
+                }
+            }
+
+            // Negative tests - invalid rankings
+            for (auto te = invalid_tes.begin(); te != invalid_tes.end(); te++)
+            {
+                try
+                {
+                    c = compression::details::get_compressor_from_header(*te, ctype);
+                    VERIFY_IS_FALSE(true);
+                }
+                catch (http_exception)
+                {
+                }
+            }
+
+            utility::string_t builtin;
+            std::vector<std::shared_ptr<decompress_factory>> dv;
+
+            // Builtins
+            builtin = compression::details::build_supported_header(ctype);
+            if (transfer)
+            {
+                VERIFY_ARE_EQUAL(!builtin.empty(), builtin::supported());
+            }
+            else
+            {
+                VERIFY_IS_FALSE(builtin.empty());
+            }
+
+            // Null decompressor - effectively forces no compression algorithms
+            dv.push_back(std::shared_ptr<decompress_factory>());
+            builtin = compression::details::build_supported_header(ctype, dv);
+            VERIFY_ARE_EQUAL(transfer != 0, builtin.empty());
+            dv.pop_back();
+
+            if (builtin::supported())
+            {
+                dv.push_back(builtin::get_decompress_factory(builtin::algorithm::GZIP));
+                builtin = compression::details::build_supported_header(ctype, dv); // --> "gzip;q=1.0"
+                VERIFY_IS_FALSE(builtin.empty());
+            }
+            else
+            {
+                builtin = _XPLATSTR("gzip;q=1.0");
+            }
+
+            // TE- and/or Accept-Encoding-specific test cases, regenerated for each pass
+            std::vector<utility::string_t> tes = {
+                builtin,
+                _XPLATSTR("  deflate;q=0.777  ,foo;q=0,gzip;q=0.9,     bar;q=1.0, xxx;q=1  "),
+                _XPLATSTR("gzip ; q=1, deflate;q=0.5"),
+                _XPLATSTR("gzip;q=1.0, deflate;q=0.5"),
+                _XPLATSTR("deflate;q=0.5, gzip;q=1"),
+                _XPLATSTR("gzip,deflate;q=0.7"),
+                _XPLATSTR("trailers,gzip,deflate;q=0.7")};
+
+            for (int fake = 0; fake < 2; fake++)
+            {
+                if (fake)
+                {
+                    // Switch built-in vs. supplied results the second time around
+                    for (auto& te : tes)
+                    {
+                        te.replace(te.find(builtin::algorithm::GZIP), gzip.size(), fake_provider::FAKE);
+                        if (te.find(builtin::algorithm::DEFLATE) != utility::string_t::npos)
+                        {
+                            te.replace(te.find(builtin::algorithm::DEFLATE),
+                                       utility::string_t(builtin::algorithm::DEFLATE).size(),
+                                       _NONE);
+                        }
+                    }
+                }
+
+                for (auto te = tes.begin(); te != tes.end(); te++)
+                {
+                    // Built-in only
+                    c = compression::details::get_compressor_from_header(*te, ctype);
+                    if (c)
+                    {
+                        VERIFY_IS_TRUE(builtin::supported());
+                        VERIFY_IS_FALSE(fake != 0);
+                        VERIFY_ARE_EQUAL(c->algorithm(), gzip);
+                    }
+                    else
+                    {
+                        VERIFY_IS_TRUE(fake != 0 || !builtin::supported());
+                    }
+
+                    // Supplied compressor - both matching and non-matching
+                    c = compression::details::get_compressor_from_header(*te, ctype, fcv);
+                    VERIFY_ARE_EQUAL(c != 0, fake != 0);
+                    if (c)
+                    {
+                        VERIFY_ARE_EQUAL(c->algorithm(), fake_provider::FAKE);
+                    }
+                }
+            }
+        }
+    }
+
+    template<typename _CharType>
+    class my_rawptr_buffer : public concurrency::streams::rawptr_buffer<_CharType>
+    {
+    public:
+        my_rawptr_buffer(const _CharType* data, size_t size)
+            : concurrency::streams::rawptr_buffer<_CharType>(data, size)
+        {
+        }
+
+        // No acquire(), to force non-acquire compression client codepaths
+        virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
+        {
+            (void)ptr;
+            (void)count;
+            return false;
+        }
+
+        virtual void release(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
+        {
+            (void)ptr;
+            (void)count;
+        }
+
+        static concurrency::streams::basic_istream<_CharType> open_istream(const _CharType* data, size_t size)
+        {
+            return concurrency::streams::basic_istream<_CharType>(
+                concurrency::streams::streambuf<_CharType>(std::make_shared<my_rawptr_buffer<_CharType>>(data, size)));
+        }
+    };
+
+    TEST_FIXTURE(uri_address, compress_client_server)
+    {
+        bool processed;
+        bool skip_transfer_put = false;
+        int transfer;
+
+        size_t buffer_sizes[] = {0, 1, 3, 4, 4096, 65536, 100000, 157890};
+
+        std::vector<std::shared_ptr<decompress_factory>> dfactories;
+        std::vector<std::shared_ptr<compress_factory>> cfactories;
+
+#if defined(_WIN32) && !defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
+        // Run a quick test to see if we're dealing with older/broken winhttp for compressed transfer encoding
+        {
+            test_http_server* p_server = nullptr;
+            std::unique_ptr<test_http_server::scoped_server> scoped =
+                std::move(utility::details::make_unique<test_http_server::scoped_server>(m_uri));
+            scoped->server()->next_request().then([&skip_transfer_put](pplx::task<test_request*> op) {
+                try
+                {
+                    op.get()->reply(static_cast<unsigned short>(status_codes::OK));
+                }
+                catch (std::runtime_error)
+                {
+                    // The test server throws if it's destructed with outstanding tasks,
+                    // which will happen if winhttp responds 501 without informing us
+                    VERIFY_IS_TRUE(skip_transfer_put);
+                }
+            });
+
+            http_client client(m_uri);
+            http_request msg(methods::PUT);
+            msg.set_compressor(utility::details::make_unique<fake_provider>(0));
+            msg.set_body(concurrency::streams::rawptr_stream<uint8_t>::open_istream((const uint8_t*)nullptr, 0));
+            http_response rsp = client.request(msg).get();
+            rsp.content_ready().wait();
+            if (rsp.status_code() == status_codes::NotImplemented)
+            {
+                skip_transfer_put = true;
+            }
+            else
+            {
+                VERIFY_IS_TRUE(rsp.status_code() == status_codes::OK);
+            }
+        }
+#endif // _WIN32
+
+        auto extra_size = [](size_t bufsz) -> size_t { return (std::max)(static_cast<size_t>(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
+        for (int real = 0; real < 2; real++)
+        {
+            web::http::experimental::listener::http_listener listener;
+            std::unique_ptr<test_http_server::scoped_server> scoped;
+            test_http_server* p_server = nullptr;
+            std::vector<uint8_t> v;
+            size_t buffer_size;
+
+            // Start the listener, and configure callbacks if necessary
+            if (real)
+            {
+                listener = std::move(web::http::experimental::listener::http_listener(m_uri));
+                listener.open().wait();
+                listener.support(methods::PUT, [&v, &dfactories, &processed](http_request request) {
+                    utility::string_t encoding;
+                    http_response rsp;
+
+                    if (request.headers().match(web::http::header_names::transfer_encoding, encoding) ||
+                        request.headers().match(web::http::header_names::content_encoding, encoding))
+                    {
+                        if (encoding.find(fake_provider::FAKE) != utility::string_t::npos)
+                        {
+                            // This one won't be found by the server in the default set...
+                            rsp._get_impl()->set_decompress_factories(dfactories);
+                        }
+                    }
+                    processed = true;
+                    rsp.set_status_code(status_codes::OK);
+                    request.reply(rsp);
+                });
+                listener.support(
+                    methods::GET,
+                    [&v, &buffer_size, &cfactories, &processed, &transfer, &extra_size](http_request request) {
+                        utility::string_t encoding;
+                        http_response rsp;
+                        bool done;
+
+                        if (transfer)
+                        {
+#if defined(_WIN32) && !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
+                            // Compression happens in the listener itself
+                            done = request.headers().match(web::http::header_names::te, encoding);
+                            VERIFY_IS_TRUE(done);
+                            if (encoding.find(fake_provider::FAKE) != utility::string_t::npos)
+                            {
+                                // This one won't be found in the server's default set...
+                                rsp._get_impl()->set_compressor(
+                                    utility::details::make_unique<fake_provider>(buffer_size));
+                            }
+#endif // _WIN32
+                            rsp.set_body(
+                                concurrency::streams::rawptr_stream<uint8_t>::open_istream(v.data(), v.size()));
+                        }
+                        else
+                        {
+                            std::unique_ptr<compress_provider> c;
+                            std::vector<uint8_t> pre;
+                            size_t used;
+
+                            done = request.headers().match(web::http::header_names::accept_encoding, encoding);
+                            VERIFY_IS_TRUE(done);
+                            pre.resize(v.size() + extra_size(buffer_size));
+                            c = compression::details::get_compressor_from_header(
+                                encoding, compression::details::header_types::accept_encoding, cfactories);
+                            VERIFY_IS_TRUE((bool)c);
+                            auto got = c->compress(
+                                v.data(), v.size(), pre.data(), pre.size(), operation_hint::is_last, used, done);
+                            VERIFY_IS_TRUE(used == v.size());
+                            VERIFY_IS_TRUE(done);
+
+                            // Add a single pre-compressed stream, since Content-Encoding requires Content-Length
+                            pre.resize(got);
+                            rsp.headers().add(header_names::content_encoding, c->algorithm());
+                            rsp.set_body(
+                                concurrency::streams::container_stream<std::vector<uint8_t>>::open_istream(pre));
+                        }
+                        processed = true;
+                        rsp.set_status_code(status_codes::OK);
+                        request.reply(rsp);
+                    });
+            }
+            else
+            {
+                scoped = std::move(utility::details::make_unique<test_http_server::scoped_server>(m_uri));
+                p_server = scoped->server();
+            }
+
+            // Test various buffer sizes
+            for (int sz = 0; sz < sizeof(buffer_sizes) / sizeof(buffer_sizes[0]); sz++)
+            {
+                std::vector<utility::string_t> algorithms;
+                std::map<utility::string_t, std::shared_ptr<decompress_factory>> dmap;
+                std::map<utility::string_t, std::shared_ptr<compress_factory>> cmap;
+
+                buffer_size = buffer_sizes[sz];
+
+                dfactories.clear();
+                cfactories.clear();
+
+                // Re-build the sets of compress and decompress factories, to account for the buffer size in our "fake"
+                // ones
+                if (builtin::algorithm::supported(builtin::algorithm::GZIP))
+                {
+                    algorithms.push_back(builtin::algorithm::GZIP);
+                    dmap[builtin::algorithm::GZIP] = builtin::get_decompress_factory(builtin::algorithm::GZIP);
+                    cmap[builtin::algorithm::GZIP] = builtin::get_compress_factory(builtin::algorithm::GZIP);
+                    dfactories.push_back(dmap[builtin::algorithm::GZIP]);
+                    cfactories.push_back(cmap[builtin::algorithm::GZIP]);
+                }
+                if (builtin::algorithm::supported(builtin::algorithm::DEFLATE))
+                {
+                    algorithms.push_back(builtin::algorithm::DEFLATE);
+                    dmap[builtin::algorithm::DEFLATE] = builtin::get_decompress_factory(builtin::algorithm::DEFLATE);
+                    cmap[builtin::algorithm::DEFLATE] = builtin::get_compress_factory(builtin::algorithm::DEFLATE);
+                    dfactories.push_back(dmap[builtin::algorithm::DEFLATE]);
+                    cfactories.push_back(cmap[builtin::algorithm::DEFLATE]);
+                }
+                if (builtin::algorithm::supported(builtin::algorithm::BROTLI))
+                {
+                    algorithms.push_back(builtin::algorithm::BROTLI);
+                    dmap[builtin::algorithm::BROTLI] = builtin::get_decompress_factory(builtin::algorithm::BROTLI);
+                    cmap[builtin::algorithm::BROTLI] =
+                        make_compress_factory(builtin::algorithm::BROTLI, []() -> std::unique_ptr<compress_provider> {
+                            // Use a memory-constrained Brotli instance in some cases for code coverage
+                            return builtin::make_brotli_compressor(10, 11, 0, 16, 0, 0);
+                        });
+                    dfactories.push_back(dmap[builtin::algorithm::BROTLI]);
+                    cfactories.push_back(builtin::get_compress_factory(builtin::algorithm::BROTLI));
+                }
+                algorithms.push_back(fake_provider::FAKE);
+                dmap[fake_provider::FAKE] = make_decompress_factory(
+                    fake_provider::FAKE, 1000, [buffer_size]() -> std::unique_ptr<decompress_provider> {
+                        return utility::details::make_unique<fake_provider>(buffer_size);
+                    });
+                cmap[fake_provider::FAKE] =
+                    make_compress_factory(fake_provider::FAKE, [buffer_size]() -> std::unique_ptr<compress_provider> {
+                        return utility::details::make_unique<fake_provider>(buffer_size);
+                    });
+                dfactories.push_back(dmap[fake_provider::FAKE]);
+                cfactories.push_back(cmap[fake_provider::FAKE]);
+
+                v.resize(buffer_size);
+
+                // Test compressible (net shrinking) and non-compressible (net growing) buffers
+                for (int compressible = 0; compressible < 2; compressible++)
+                {
+                    for (size_t x = 0; x < buffer_size; x++)
+                    {
+                        if (compressible)
+                        {
+                            v[x] = static_cast<uint8_t>('a' + x % 26);
+                        }
+                        else
+                        {
+                            v[x] = static_cast<uint8_t>(std::rand());
+                        }
+                    }
+
+                    // Test both Transfer-Encoding and Content-Encoding
+                    for (transfer = 0; transfer < 2; transfer++)
+                    {
+                        web::http::client::http_client_config config;
+                        config.set_request_compressed_response(!transfer);
+                        http_client client(m_uri, config);
+
+                        // Test supported compression algorithms
+                        for (auto& algorithm : algorithms)
+                        {
+                            // Test both GET and PUT
+                            for (int put = 0; put < 2; put++)
+                            {
+                                if (transfer && put && skip_transfer_put)
+                                {
+                                    continue;
+                                }
+
+                                processed = false;
+
+                                if (put)
+                                {
+                                    std::vector<concurrency::streams::istream> streams;
+                                    std::vector<uint8_t> pre;
+
+                                    if (transfer)
+                                    {
+                                        // Add a pair of non-compressed streams for Transfer-Encoding, one with and one
+                                        // without acquire/release support
+                                        streams.emplace_back(concurrency::streams::rawptr_stream<uint8_t>::open_istream(
+                                            (const uint8_t*)v.data(), v.size()));
+                                        streams.emplace_back(
+                                            my_rawptr_buffer<uint8_t>::open_istream(v.data(), v.size()));
+                                    }
+                                    else
+                                    {
+                                        bool done;
+                                        size_t used;
+                                        pre.resize(v.size() + extra_size(buffer_size));
+
+                                        auto c = builtin::make_compressor(algorithm);
+                                        if (algorithm == fake_provider::FAKE)
+                                        {
+                                            VERIFY_IS_FALSE((bool)c);
+                                            c = utility::details::make_unique<fake_provider>(buffer_size);
+                                        }
+                                        VERIFY_IS_TRUE((bool)c);
+                                        auto got = c->compress(v.data(),
+                                                               v.size(),
+                                                               pre.data(),
+                                                               pre.size(),
+                                                               operation_hint::is_last,
+                                                               used,
+                                                               done);
+                                        VERIFY_ARE_EQUAL(used, v.size());
+                                        VERIFY_IS_TRUE(done);
+
+                                        // Add a single pre-compressed stream, since Content-Encoding requires
+                                        // Content-Length
+                                        streams.emplace_back(concurrency::streams::rawptr_stream<uint8_t>::open_istream(
+                                            pre.data(), got));
+                                    }
+
+                                    for (auto& stream : streams)
+                                    {
+                                        http_request msg(methods::PUT);
+
+                                        processed = false;
+
+                                        msg.set_body(stream);
+                                        if (transfer)
+                                        {
+                                            if (real)
+                                            {
+                                                bool boo = msg.set_compressor(algorithm);
+                                                VERIFY_ARE_EQUAL(boo, algorithm != fake_provider::FAKE);
+                                                if (algorithm == fake_provider::FAKE)
+                                                {
+                                                    msg.set_compressor(
+                                                        utility::details::make_unique<fake_provider>(buffer_size));
+                                                }
+                                            }
+                                            else
+                                            {
+                                                msg.set_compressor(cmap[algorithm]->make_compressor());
+                                            }
+                                        }
+                                        else
+                                        {
+                                            msg.headers().add(header_names::content_encoding, algorithm);
+                                        }
+
+                                        if (!real)
+                                        {
+                                            // We implement the decompression path in the server, to prove that valid,
+                                            // compressed data is sent
+                                            p_server->next_request().then([&](test_request* p_request) {
+                                                std::unique_ptr<decompress_provider> d;
+                                                std::vector<uint8_t> vv;
+                                                utility::string_t header;
+                                                size_t used;
+                                                size_t got;
+                                                bool done;
+
+                                                http_asserts::assert_test_request_equals(
+                                                    p_request, methods::PUT, U("/"));
+
+                                                if (transfer)
+                                                {
+                                                    VERIFY_IS_FALSE(p_request->match_header(
+                                                        header_names::content_encoding, header));
+                                                    done = p_request->match_header(header_names::transfer_encoding,
+                                                                                   header);
+                                                    VERIFY_IS_TRUE(done);
+                                                    d = compression::details::get_decompressor_from_header(
+                                                        header,
+                                                        compression::details::header_types::transfer_encoding,
+                                                        dfactories);
+                                                }
+                                                else
+                                                {
+                                                    done = p_request->match_header(header_names::transfer_encoding,
+                                                                                   header);
+                                                    if (done)
+                                                    {
+                                                        VERIFY_IS_TRUE(
+                                                            utility::details::str_iequal(_XPLATSTR("chunked"), header));
+                                                    }
+                                                    done =
+                                                        p_request->match_header(header_names::content_encoding, header);
+                                                    VERIFY_IS_TRUE(done);
+                                                    d = compression::details::get_decompressor_from_header(
+                                                        header,
+                                                        compression::details::header_types::content_encoding,
+                                                        dfactories);
+                                                }
+#if defined(_WIN32) && !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
+                                                VERIFY_IS_TRUE((bool)d);
+#else  // _WIN32
+                                                VERIFY_ARE_NOT_EQUAL((bool)d, !!transfer);
+#endif // _WIN32
+
+                                                vv.resize(buffer_size + extra_size(buffer_size));
+                                                if (d)
+                                                {
+                                                    got = d->decompress(p_request->m_body.data(),
+                                                                        p_request->m_body.size(),
+                                                                        vv.data(),
+                                                                        vv.size(),
+                                                                        operation_hint::is_last,
+                                                                        used,
+                                                                        done);
+                                                    VERIFY_ARE_EQUAL(used, p_request->m_body.size());
+                                                    VERIFY_IS_TRUE(done);
+                                                }
+                                                else
+                                                {
+                                                    std::copy(v.begin(), v.end(), vv.begin());
+                                                    got = v.size();
+                                                }
+                                                VERIFY_ARE_EQUAL(buffer_size, got);
+                                                vv.resize(buffer_size);
+                                                VERIFY_ARE_EQUAL(v, vv);
+                                                processed = true;
+
+                                                p_request->reply(static_cast<unsigned short>(status_codes::OK));
+                                            });
+                                        }
+
+                                        // Send the request
+                                        http_response rsp = client.request(msg).get();
+                                        VERIFY_ARE_EQUAL(rsp.status_code(), status_codes::OK);
+                                        rsp.content_ready().wait();
+                                        stream.close().wait();
+                                        VERIFY_IS_TRUE(processed);
+                                    }
+                                }
+                                else
+                                {
+                                    std::vector<uint8_t> vv;
+                                    concurrency::streams::ostream stream =
+                                        concurrency::streams::rawptr_stream<uint8_t>::open_ostream(vv.data(),
+                                                                                                   buffer_size);
+                                    http_request msg(methods::GET);
+
+                                    std::vector<std::shared_ptr<decompress_factory>> df = {dmap[algorithm]};
+                                    msg.set_decompress_factories(df);
+
+                                    vv.resize(buffer_size + extra_size(buffer_size)); // extra to ensure no overflow
+
+                                    concurrency::streams::rawptr_buffer<uint8_t> buf(
+                                        vv.data(), vv.size(), std::ios::out);
+
+                                    if (!real)
+                                    {
+                                        p_server->next_request().then([&](test_request* p_request) {
+                                            std::map<utility::string_t, utility::string_t> headers;
+                                            std::unique_ptr<compress_provider> c;
+                                            utility::string_t header;
+                                            std::vector<uint8_t> cmp;
+                                            size_t used;
+                                            size_t extra = 0;
+                                            size_t skip = 0;
+                                            size_t got;
+                                            bool done;
+
+                                            std::string ext = ";x=y";
+                                            std::string trailer = "a=b\r\nx=y\r\n";
+
+                                            http_asserts::assert_test_request_equals(p_request, methods::GET, U("/"));
+
+                                            if (transfer)
+                                            {
+                                                // On Windows, someone along the way adds "Accept-Encoding: peerdist",
+                                                // so we can't unconditionally assert that Accept-Encoding is not
+                                                // present
+                                                done = p_request->match_header(header_names::accept_encoding, header);
+                                                VERIFY_IS_TRUE(!done ||
+                                                               header.find(algorithm) == utility::string_t::npos);
+                                                done = p_request->match_header(header_names::te, header);
+                                                if (done)
+                                                {
+                                                    c = compression::details::get_compressor_from_header(
+                                                        header, compression::details::header_types::te, cfactories);
+                                                }
+
+                                                // Account for space for the chunk header and delimiters, plus a chunk
+                                                // extension and a chunked trailer part
+                                                extra = 2 * web::http::details::chunked_encoding::
+                                                                additional_encoding_space +
+                                                        ext.size() + trailer.size();
+                                                skip = web::http::details::chunked_encoding::data_offset + ext.size();
+                                            }
+                                            else
+                                            {
+                                                VERIFY_IS_FALSE(p_request->match_header(header_names::te, header));
+                                                done = p_request->match_header(header_names::accept_encoding, header);
+                                                VERIFY_IS_TRUE(done);
+                                                c = compression::details::get_compressor_from_header(
+                                                    header,
+                                                    compression::details::header_types::accept_encoding,
+                                                    cfactories);
+                                            }
+#if !defined __cplusplus_winrt
+                                            VERIFY_IS_TRUE((bool)c);
+#else  // __cplusplus_winrt
+                                            VERIFY_ARE_NOT_EQUAL((bool)c, !!transfer);
+#endif // __cplusplus_winrt
+                                            cmp.resize(extra + buffer_size + extra_size(buffer_size));
+                                            if (c)
+                                            {
+                                                got = c->compress(v.data(),
+                                                                  v.size(),
+                                                                  cmp.data() + skip,
+                                                                  cmp.size() - extra,
+                                                                  operation_hint::is_last,
+                                                                  used,
+                                                                  done);
+                                                VERIFY_ARE_EQUAL(used, v.size());
+                                                VERIFY_IS_TRUE(done);
+                                            }
+                                            else
+                                            {
+                                                memcpy(cmp.data() + skip, v.data(), v.size());
+                                                got = v.size();
+                                            }
+                                            if (transfer)
+                                            {
+                                                // Add delimiters for the first (and only) data chunk, plus the final
+                                                // 0-length chunk, and hack in a dummy chunk extension and a dummy
+                                                // trailer part.  Note that we put *two* "0\r\n" in here in the 0-length
+                                                // case... and none of the parsers complain.
+                                                size_t total =
+                                                    got +
+                                                    web::http::details::chunked_encoding::additional_encoding_space +
+                                                    ext.size();
+                                                _ASSERTE(ext.size() >= 2);
+                                                if (got > ext.size() - 1)
+                                                {
+                                                    cmp[total - 2] =
+                                                        cmp[got + web::http::details::chunked_encoding::data_offset];
+                                                }
+                                                if (got > ext.size() - 2)
+                                                {
+                                                    cmp[total - 1] =
+                                                        cmp[got + web::http::details::chunked_encoding::data_offset +
+                                                            1];
+                                                }
+                                                size_t offset =
+                                                    web::http::details::chunked_encoding::add_chunked_delimiters(
+                                                        cmp.data(), total, got);
+                                                size_t offset2 =
+                                                    web::http::details::chunked_encoding::add_chunked_delimiters(
+                                                        cmp.data() + total - 7,
+                                                        web::http::details::chunked_encoding::additional_encoding_space,
+                                                        0);
+                                                _ASSERTE(
+                                                    offset2 == 7 &&
+                                                    web::http::details::chunked_encoding::additional_encoding_space -
+                                                            7 ==
+                                                        5);
+                                                memcpy(cmp.data() + web::http::details::chunked_encoding::data_offset -
+                                                           2,
+                                                       ext.data(),
+                                                       ext.size());
+                                                cmp[web::http::details::chunked_encoding::data_offset + ext.size() -
+                                                    2] = '\r';
+                                                cmp[web::http::details::chunked_encoding::data_offset + ext.size() -
+                                                    1] = '\n';
+                                                if (got > ext.size() - 1)
+                                                {
+                                                    cmp[got + web::http::details::chunked_encoding::data_offset] =
+                                                        cmp[total - 2];
+                                                }
+                                                if (got > ext.size() - 2)
+                                                {
+                                                    cmp[got + web::http::details::chunked_encoding::data_offset + 1] =
+                                                        cmp[total - 1];
+                                                }
+                                                cmp[total - 2] = '\r';
+                                                cmp[total - 1] = '\n';
+                                                memcpy(cmp.data() + total + 3, trailer.data(), trailer.size());
+                                                cmp[total + trailer.size() + 3] = '\r';
+                                                cmp[total + trailer.size() + 4] = '\n';
+                                                cmp.erase(cmp.begin(), cmp.begin() + offset);
+                                                cmp.resize(
+                                                    ext.size() + got + trailer.size() +
+                                                    web::http::details::chunked_encoding::additional_encoding_space -
+                                                    offset + 5);
+                                                if (c)
+                                                {
+                                                    headers[header_names::transfer_encoding] =
+                                                        c->algorithm() + _XPLATSTR(", chunked");
+                                                }
+                                                else
+                                                {
+                                                    headers[header_names::transfer_encoding] = _XPLATSTR("chunked");
+                                                }
+                                            }
+                                            else
+                                            {
+                                                cmp.resize(got);
+                                                headers[header_names::content_encoding] = c->algorithm();
+                                            }
+                                            processed = true;
+
+                                            if (cmp.size())
+                                            {
+                                                p_request->reply(static_cast<unsigned short>(status_codes::OK),
+                                                                 utility::string_t(),
+                                                                 headers,
+                                                                 cmp);
+                                            }
+                                            else
+                                            {
+                                                p_request->reply(static_cast<unsigned short>(status_codes::OK),
+                                                                 utility::string_t(),
+                                                                 headers);
+                                            }
+                                        });
+                                    }
+
+                                    // Common send and response processing code
+                                    http_response rsp = client.request(msg).get();
+                                    VERIFY_ARE_EQUAL(rsp.status_code(), status_codes::OK);
+                                    VERIFY_NO_THROWS(rsp.content_ready().wait());
+
+                                    if (transfer)
+                                    {
+                                        VERIFY_IS_TRUE(rsp.headers().has(header_names::transfer_encoding));
+                                        VERIFY_IS_FALSE(rsp.headers().has(header_names::content_encoding));
+                                    }
+                                    else
+                                    {
+                                        utility::string_t header;
+
+                                        VERIFY_IS_TRUE(rsp.headers().has(header_names::content_encoding));
+                                        bool boo = rsp.headers().match(header_names::transfer_encoding, header);
+                                        if (boo)
+                                        {
+                                            VERIFY_IS_TRUE(utility::details::str_iequal(_XPLATSTR("chunked"), header));
+                                        }
+                                    }
+
+                                    size_t offset = 0;
+                                    VERIFY_NO_THROWS(offset = rsp.body().read_to_end(buf).get());
+                                    VERIFY_ARE_EQUAL(offset, buffer_size);
+                                    VERIFY_ARE_EQUAL(offset, static_cast<size_t>(buf.getpos(std::ios::out)));
+                                    vv.resize(buffer_size);
+                                    VERIFY_ARE_EQUAL(v, vv);
+                                    buf.close(std::ios_base::out).wait();
+                                    stream.close().wait();
+                                }
+                                VERIFY_IS_TRUE(processed);
+                            }
+                        }
+                    }
+                }
+            }
+            if (real)
+            {
+                listener.close().wait();
+            }
+        }
+    }
+} // SUITE(request_helper_tests)
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/connection_pool_tests.cpp b/Release/tests/functional/http/client/connection_pool_tests.cpp
new file mode 100644 (file)
index 0000000..10880cb
--- /dev/null
@@ -0,0 +1,50 @@
+#include "stdafx.h"
+
+#include "../../../src/http/common/connection_pool_helpers.h"
+#include <memory>
+
+using namespace web::http::client::details;
+
+SUITE(connection_pooling)
+{
+    TEST(empty_returns_nullptr)
+    {
+        connection_pool_stack<int> connectionStack;
+        VERIFY_ARE_EQUAL(connectionStack.try_acquire(), std::shared_ptr<int> {});
+    }
+
+    static int noisyCount = 0;
+    struct noisy
+    {
+        noisy() = delete;
+        noisy(int) { ++noisyCount; }
+        noisy(const noisy&) = delete;
+        noisy(noisy&&) { ++noisyCount; }
+        noisy& operator=(const noisy&) = delete;
+        noisy& operator=(noisy&&) = delete;
+        ~noisy() { --noisyCount; }
+    };
+
+    TEST(cycled_connections_survive)
+    {
+        connection_pool_stack<noisy> connectionStack;
+        VERIFY_ARE_EQUAL(0, noisyCount);
+        connectionStack.release(std::make_shared<noisy>(42));
+        connectionStack.release(std::make_shared<noisy>(42));
+        connectionStack.release(std::make_shared<noisy>(42));
+        VERIFY_ARE_EQUAL(3, noisyCount);
+        VERIFY_IS_TRUE(connectionStack.free_stale_connections());
+        auto tmp = connectionStack.try_acquire();
+        VERIFY_ARE_NOT_EQUAL(tmp, std::shared_ptr<noisy> {});
+        connectionStack.release(std::move(tmp));
+        VERIFY_ARE_EQUAL(tmp, std::shared_ptr<noisy> {});
+        tmp = connectionStack.try_acquire();
+        VERIFY_ARE_NOT_EQUAL(tmp, std::shared_ptr<noisy> {});
+        connectionStack.release(std::move(tmp));
+        VERIFY_IS_TRUE(connectionStack.free_stale_connections());
+        VERIFY_ARE_EQUAL(1, noisyCount);
+        VERIFY_IS_FALSE(connectionStack.free_stale_connections());
+        VERIFY_ARE_EQUAL(0, noisyCount);
+        VERIFY_IS_FALSE(connectionStack.free_stale_connections());
+    }
+};
diff --git a/Release/tests/functional/http/client/connections_and_errors.cpp b/Release/tests/functional/http/client/connections_and_errors.cpp
new file mode 100644 (file)
index 0000000..847755d
--- /dev/null
@@ -0,0 +1,448 @@
+/***
+ * 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 covering issues dealing with http_client lifetime, underlying TCP connections, and general connection
+ *errors.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#ifndef __cplusplus_winrt
+#include "cpprest/http_listener.h"
+#endif
+
+#include <chrono>
+#include <thread>
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+// Test implementation for pending_requests_after_client.
+static void pending_requests_after_client_impl(const uri& address)
+{
+    std::vector<pplx::task<void>> completed_requests;
+    {
+        test_http_server::scoped_server scoped(address);
+        const method mtd = methods::GET;
+
+        const size_t num_requests = 10;
+
+        std::vector<pplx::task<test_request*>> requests = scoped.server()->next_requests(num_requests);
+        std::vector<pplx::task<http_response>> responses;
+        {
+            http_client client(address);
+
+            // send requests.
+            for (size_t i = 0; i < num_requests; ++i)
+            {
+                responses.push_back(client.request(mtd));
+            }
+        }
+
+        // send responses.
+        for (size_t i = 0; i < num_requests; ++i)
+        {
+            completed_requests.push_back(requests[i].then([&](test_request* request) {
+                http_asserts::assert_test_request_equals(request, mtd, U("/"));
+                VERIFY_ARE_EQUAL(0u, request->reply(status_codes::OK));
+            }));
+        }
+
+        // verify responses.
+        for (size_t i = 0; i < num_requests; ++i)
+        {
+            try
+            {
+                http_asserts::assert_response_equals(responses[i].get(), status_codes::OK);
+            }
+            catch (...)
+            {
+                VERIFY_IS_TRUE(false);
+            }
+        }
+    }
+    for (auto&& req : completed_requests)
+        req.get();
+}
+
+SUITE(connections_and_errors)
+{
+    // Tests requests still outstanding after the http_client has been destroyed.
+    TEST_FIXTURE(uri_address, pending_requests_after_client) { pending_requests_after_client_impl(m_uri); }
+
+    TEST_FIXTURE(uri_address, server_doesnt_exist)
+    {
+        http_client_config config;
+        config.set_timeout(std::chrono::seconds(1));
+        http_client client(m_uri, config);
+        VERIFY_THROWS(client.request(methods::GET).wait(), web::http::http_exception);
+    }
+
+    TEST_FIXTURE(uri_address, open_failure)
+    {
+        http_client client(U("http://localhost323:-1"));
+
+        // This API should not throw. The exception should be surfaced
+        // during task.wait/get
+        auto t = client.request(methods::GET);
+        VERIFY_THROWS(t.wait(), web::http::http_exception);
+    }
+
+    TEST_FIXTURE(uri_address, server_close_without_responding)
+    {
+        http_client_config config;
+        config.set_timeout(utility::seconds(1));
+
+        http_client client(m_uri, config);
+        test_http_server::scoped_server server(m_uri);
+        auto t = server.server()->next_request();
+
+        // Send request.
+        auto response = client.request(methods::PUT);
+
+        // Wait for request
+        VERIFY_NO_THROWS(t.get());
+
+        // Close server connection.
+        server.server()->close();
+
+        VERIFY_THROWS_HTTP_ERROR_CODE(response.wait(), std::errc::connection_aborted);
+
+        // Try sending another request.
+        VERIFY_THROWS(client.request(methods::GET).wait(), web::http::http_exception);
+    }
+
+    TEST_FIXTURE(uri_address, request_timeout)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        auto t = scoped.server()->next_request();
+        http_client_config config;
+        config.set_timeout(utility::seconds(1));
+
+        http_client client(m_uri, config);
+        auto responseTask = client.request(methods::GET);
+
+#ifdef __APPLE__
+        // CodePlex 295
+        VERIFY_THROWS(responseTask.get(), http_exception);
+#else
+        VERIFY_THROWS_HTTP_ERROR_CODE(responseTask.get(), std::errc::timed_out);
+#endif
+        t.get();
+    }
+
+    TEST_FIXTURE(uri_address, request_timeout_microsecond)
+    {
+        pplx::task<test_request*> t;
+        {
+            test_http_server::scoped_server scoped(m_uri);
+            t = scoped.server()->next_request();
+            http_client_config config;
+            config.set_timeout(std::chrono::microseconds(900));
+
+            http_client client(m_uri, config);
+            auto responseTask = client.request(methods::GET);
+#ifdef __APPLE__
+            // CodePlex 295
+            VERIFY_THROWS(responseTask.get(), http_exception);
+#else
+            VERIFY_THROWS_HTTP_ERROR_CODE(responseTask.get(), std::errc::timed_out);
+#endif
+        }
+        try
+        {
+            t.get();
+        }
+        catch (...)
+        {
+        }
+    }
+
+    TEST_FIXTURE(uri_address, invalid_method)
+    {
+        web::http::uri uri(U("http://www.bing.com/"));
+        http_client client(uri);
+        string_t invalid_chars = U("\a\b\f\v\n\r\t\x20\x7f");
+
+        for (auto iter = invalid_chars.begin(); iter < invalid_chars.end(); iter++)
+        {
+            string_t method = U("my method");
+            method[2] = *iter;
+            VERIFY_THROWS(client.request(method).get(), http_exception);
+        }
+    }
+
+    // This test sends an SSL request to a non-SSL server and should fail on handshaking
+    TEST_FIXTURE(uri_address, handshake_fail)
+    {
+        web::http::uri ssl_uri(U("https://localhost:34568/"));
+
+        test_http_server::scoped_server scoped(m_uri);
+
+        http_client client(ssl_uri);
+        auto request = client.request(methods::GET);
+
+        VERIFY_THROWS(request.get(), http_exception);
+    }
+
+#if !defined(__cplusplus_winrt)
+    TEST_FIXTURE(uri_address, content_ready_timeout)
+    {
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+
+        streams::producer_consumer_buffer<uint8_t> buf;
+
+        listener.support([buf](http_request request) {
+            http_response response(200);
+            response.set_body(streams::istream(buf), U("text/plain"));
+            response.headers().add(header_names::connection, U("close"));
+            request.reply(response);
+        });
+
+        {
+            http_client_config config;
+            config.set_timeout(utility::seconds(1));
+            http_client client(m_uri, config);
+            http_request msg(methods::GET);
+            http_response rsp = client.request(msg).get();
+
+            // The response body should timeout and we should receive an exception
+            VERIFY_THROWS_HTTP_ERROR_CODE(rsp.content_ready().wait(), std::errc::timed_out);
+        }
+
+        buf.close(std::ios_base::out).wait();
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, stream_timeout)
+    {
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+
+        streams::producer_consumer_buffer<uint8_t> buf;
+
+        listener.support([buf](http_request request) {
+            http_response response(200);
+            response.set_body(streams::istream(buf), U("text/plain"));
+            response.headers().add(header_names::connection, U("close"));
+            request.reply(response);
+        });
+
+        {
+            http_client_config config;
+            config.set_timeout(utility::seconds(1));
+            http_client client(m_uri, config);
+            http_request msg(methods::GET);
+            http_response rsp = client.request(msg).get();
+
+            // The response body should timeout and we should receive an exception
+            auto readTask = rsp.body().read_to_end(streams::producer_consumer_buffer<uint8_t>());
+            VERIFY_THROWS_HTTP_ERROR_CODE(readTask.wait(), std::errc::timed_out);
+        }
+
+        buf.close(std::ios_base::out).wait();
+        listener.close().wait();
+    }
+#endif
+
+    TEST_FIXTURE(uri_address, cancel_before_request)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client c(m_uri);
+        pplx::cancellation_token_source source;
+        source.cancel();
+
+        auto responseTask = c.request(methods::PUT, U("/"), source.get_token());
+        VERIFY_THROWS_HTTP_ERROR_CODE(responseTask.get(), std::errc::operation_canceled);
+    }
+
+// This test can't be implemented with our test server so isn't available on WinRT.
+#ifndef __cplusplus_winrt
+    TEST_FIXTURE(uri_address, cancel_after_headers)
+    {
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+        http_client c(m_uri);
+        pplx::cancellation_token_source source;
+        pplx::extensibility::event_t ev;
+
+        listener.support([&](http_request request) {
+            streams::producer_consumer_buffer<uint8_t> buf;
+            http_response response(200);
+            response.set_body(streams::istream(buf), U("text/plain"));
+            request.reply(response);
+            ev.wait();
+            buf.putc('a').wait();
+            buf.putc('b').wait();
+            buf.putc('c').wait();
+            buf.putc('d').wait();
+            buf.close(std::ios::out).wait();
+        });
+
+        auto responseTask = c.request(methods::GET, source.get_token());
+        http_response response = responseTask.get();
+        source.cancel();
+        ev.set();
+
+        VERIFY_THROWS_HTTP_ERROR_CODE(response.extract_string().get(), std::errc::operation_canceled);
+
+        // Codeplex 328.
+#if !defined(_WIN32)
+        tests::common::utilities::os_utilities::sleep(1000);
+#endif
+
+        listener.close().wait();
+    }
+#endif
+
+    TEST_FIXTURE(uri_address, cancel_after_body)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client c(m_uri);
+        pplx::cancellation_token_source source;
+        std::map<utility::string_t, utility::string_t> headers;
+        headers[U("Content-Type")] = U("text/plain; charset=utf-8");
+        std::string bodyData("Hello");
+
+        p_server->next_request().then(
+            [&](test_request* r) { VERIFY_ARE_EQUAL(0u, r->reply(status_codes::OK, U("OK"), headers, bodyData)); });
+
+        auto response = c.request(methods::PUT, U("/"), U("data"), source.get_token()).get();
+        VERIFY_ARE_EQUAL(utility::conversions::to_string_t(bodyData), response.extract_string().get());
+        source.cancel();
+        response.content_ready().wait();
+    }
+
+    TEST_FIXTURE(uri_address, cancel_with_error)
+    {
+        http_client c(m_uri);
+        pplx::task<http_response> responseTask;
+        {
+            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();
+        }
+
+        // All errors after cancellation are ignored.
+        VERIFY_THROWS_HTTP_ERROR_CODE(responseTask.get(), std::errc::operation_canceled);
+    }
+
+    TEST_FIXTURE(uri_address, cancel_while_uploading_data)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client c(m_uri);
+        pplx::cancellation_token_source source;
+
+        auto buf = streams::producer_consumer_buffer<uint8_t>();
+        buf.putc('A').wait();
+        auto responseTask = c.request(methods::PUT, U("/"), buf.create_istream(), 2, source.get_token());
+        source.cancel();
+        buf.putc('B').wait();
+        buf.close(std::ios::out).wait();
+        VERIFY_THROWS_HTTP_ERROR_CODE(responseTask.get(), std::errc::operation_canceled);
+    }
+
+// This test can't be implemented with our test server since it doesn't stream data so isn't avaliable on WinRT.
+#ifndef __cplusplus_winrt
+    TEST_FIXTURE(uri_address, cancel_while_downloading_data)
+    {
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+        http_client c(m_uri);
+        pplx::cancellation_token_source source;
+
+        pplx::extensibility::event_t ev;
+        pplx::extensibility::event_t ev2;
+
+        listener.support([&](http_request request) {
+            streams::producer_consumer_buffer<uint8_t> buf;
+            http_response response(200);
+            response.set_body(streams::istream(buf), U("text/plain"));
+            request.reply(response);
+            buf.putc('a').wait();
+            buf.putc('b').wait();
+            ev.set();
+            ev2.wait();
+            buf.putc('c').wait();
+            buf.putc('d').wait();
+            buf.close(std::ios::out).wait();
+        });
+
+        auto response = c.request(methods::GET, source.get_token()).get();
+        ev.wait();
+        source.cancel();
+        ev2.set();
+
+        VERIFY_THROWS_HTTP_ERROR_CODE(response.extract_string().get(), std::errc::operation_canceled);
+
+        // Codeplex 328.
+#if !defined(_WIN32)
+        tests::common::utilities::os_utilities::sleep(1000);
+#endif
+
+        listener.close().wait();
+    }
+#endif
+
+    // Try to connect to a server on a closed port and cancel the operation.
+    TEST_FIXTURE(uri_address, cancel_bad_port)
+    {
+        // http_client_asio had a bug where, when canceled, it would cancel only the
+        // current connection but then go and try the next address from the list of
+        // resolved addresses, i.e., it wouldn't actually cancel as long as there
+        // are more addresses to try. Consequently, it would not report the task as
+        // being canceled. This was easiest to observe when trying to connect to a
+        // server that does not respond on a certain port, otherwise the timing
+        // might be tricky.
+
+        // We need to connect to a URI for which there are multiple addresses
+        // associated (i.e., multiple A records).
+        web::http::uri uri(U("https://microsoft.com:442/"));
+
+        // Send request.
+        http_client_config config;
+        config.set_timeout(std::chrono::milliseconds(1000));
+        http_client c(uri, config);
+        web::http::http_request r;
+        auto cts = pplx::cancellation_token_source();
+        auto ct = cts.get_token();
+        auto t = c.request(r, ct);
+
+        // Make sure that the client already finished resolving before canceling,
+        // otherwise the bug might not be triggered.
+        std::this_thread::sleep_for(std::chrono::milliseconds(400));
+        cts.cancel();
+
+        VERIFY_THROWS_HTTP_ERROR_CODE(t.get(), std::errc::operation_canceled);
+    }
+
+} // SUITE(connections_and_errors)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/header_tests.cpp b/Release/tests/functional/http/client/header_tests.cpp
new file mode 100644 (file)
index 0000000..4936aab
--- /dev/null
@@ -0,0 +1,405 @@
+/***
+ * 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 http_headers.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "cpprest/details/http_helpers.h"
+
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(outside_tests)
+{
+    TEST_FIXTURE(uri_address, request_headers)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        http_request msg(methods::POST);
+
+#ifndef __cplusplus_winrt
+        // The WinRT-based HTTP stack does not support headers that have no
+        // value, which means that there is no point in making this particular
+        // header test, it is an unsupported feature on WinRT.
+        msg.headers().add(U("HEHE"), U(""));
+#endif
+
+        msg.headers().add(U("MyHeader"), U("hehe;blach"));
+        msg.headers().add(U("Yo1"), U("You, Too"));
+        msg.headers().add(U("Yo2"), U("You2"));
+        msg.headers().add(U("Yo3"), U("You3"));
+        msg.headers().add(U("Yo4"), U("You4"));
+        msg.headers().add(U("Yo5"), U("You5"));
+        msg.headers().add(U("Yo6"), U("You6"));
+        msg.headers().add(U("Yo7"), U("You7"));
+        msg.headers().add(U("Yo8"), U("You8"));
+        msg.headers().add(U("Yo9"), U("You9"));
+        msg.headers().add(U("Yo10"), U("You10"));
+        msg.headers().add(U("Yo11"), U("You11"));
+        msg.headers().add(U("Accept"), U("text/plain"));
+        VERIFY_ARE_EQUAL(U("You5"), msg.headers()[U("Yo5")]);
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::POST, U("/"));
+            http_asserts::assert_test_request_contains_headers(p_request, msg.headers());
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, field_name_casing)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        const method mtd = methods::GET;
+        const utility::string_t field_name1 = U("CustomHeader");
+        const utility::string_t field_name2 = U("CUSTOMHEADER");
+        const utility::string_t field_name3 = U("CuSTomHEAdeR");
+        const utility::string_t value1 = U("value1");
+        const utility::string_t value2 = U("value2");
+        const utility::string_t value3 = U("value3");
+
+        http_request msg(mtd);
+        msg.headers()[field_name1] = value1;
+        msg.headers()[field_name2].append(U(", ") + value2);
+        msg.headers()[field_name3].append(U(", ") + value3);
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, mtd, U("/"));
+            std::map<utility::string_t, utility::string_t> expected_headers;
+            expected_headers[field_name1] = value1 + U(", ") + value2 + U(", ") + value3;
+            http_asserts::assert_test_request_contains_headers(p_request, expected_headers);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, field_name_duplicate)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        const method mtd = methods::GET;
+        const utility::string_t field_name1 = U("CUSTOMHEADER");
+        const utility::string_t value1 = U("value1");
+        const utility::string_t value2 = U("value2");
+
+        http_request msg(mtd);
+        msg.headers().add(field_name1, value1);
+        msg.headers().add(field_name1, value2);
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, mtd, U("/"));
+            std::map<utility::string_t, utility::string_t> expected_headers;
+            expected_headers[field_name1] = value1 + U(", ") + value2;
+            http_asserts::assert_test_request_contains_headers(p_request, expected_headers);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, field_name_no_multivalue_allowed)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        const method mtd = methods::GET;
+
+        http_request msg(mtd);
+
+        msg.headers().set_content_type(web::http::details::mime_types::text_plain);
+        msg.headers().set_content_type(web::http::details::mime_types::application_json);
+
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, mtd, U("/"));
+            std::map<utility::string_t, utility::string_t> expected_headers;
+            expected_headers[U("Content-Type")] = web::http::details::mime_types::application_json;
+            http_asserts::assert_test_request_contains_headers(p_request, expected_headers);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+    }
+    TEST_FIXTURE(uri_address, copy_move)
+    {
+        // copy constructor
+        http_headers h1;
+        h1.add(U("key1"), U("key2"));
+        http_headers h2(h1);
+        http_asserts::assert_http_headers_equals(h1, h2);
+
+        // move constructor
+        http_headers h3(std::move(h1));
+        VERIFY_ARE_EQUAL(1u, h3.size());
+        VERIFY_ARE_EQUAL(U("key2"), h3[U("key1")]);
+
+        // assignment operator
+        h1 = h3;
+        VERIFY_ARE_EQUAL(1u, h1.size());
+        VERIFY_ARE_EQUAL(U("key2"), h1[U("key1")]);
+        http_asserts::assert_http_headers_equals(h1, h3);
+
+        // move assignment operator
+        h1 = http_headers();
+        h1 = std::move(h2);
+        VERIFY_ARE_EQUAL(1u, h1.size());
+        VERIFY_ARE_EQUAL(U("key2"), h1[U("key1")]);
+    }
+
+    TEST_FIXTURE(uri_address, match_types)
+    {
+        // wchar
+        http_headers h1;
+        h1[U("key1")] = U("string");
+        utility::char_t buf[12];
+        VERIFY_IS_TRUE(h1.match(U("key1"), buf));
+        VERIFY_ARE_EQUAL(U("string"), utility::string_t(buf));
+
+        // utility::string_t
+        utility::string_t wstr;
+        VERIFY_IS_TRUE(h1.match(U("key1"), wstr));
+        VERIFY_ARE_EQUAL(U("string"), wstr);
+
+        // int
+        h1[U("key2")] = U("22");
+        int i;
+        VERIFY_IS_TRUE(h1.match(U("key2"), i));
+        VERIFY_ARE_EQUAL(22, i);
+
+        // unsigned long
+        unsigned long l;
+        VERIFY_IS_TRUE(h1.match(U("key2"), l));
+        VERIFY_ARE_EQUAL(22ul, l);
+    }
+
+    TEST_FIXTURE(uri_address, match_edge_cases)
+    {
+        // match with empty string
+        http_headers h;
+        h[U("here")] = U("");
+        utility::string_t value(U("k"));
+        VERIFY_IS_TRUE(h.match(U("HeRE"), value));
+        VERIFY_ARE_EQUAL(U(""), value);
+
+        // match with string containing spaces
+        h.add(U("blah"), U("spaces ss"));
+        VERIFY_IS_TRUE(h.match(U("blah"), value));
+        VERIFY_ARE_EQUAL(U("spaces ss"), value);
+
+        // match failing
+        value = utility::string_t();
+        VERIFY_IS_FALSE(h.match(U("hahah"), value));
+        VERIFY_ARE_EQUAL(U(""), value);
+    }
+
+    TEST_FIXTURE(uri_address, headers_find)
+    {
+        // Find when empty.
+        http_headers h;
+        VERIFY_ARE_EQUAL(h.end(), h.find(U("key1")));
+
+        // Find that exists.
+        h[U("key1")] = U("yes");
+        VERIFY_ARE_EQUAL(U("yes"), h.find(U("key1"))->second);
+
+        // Find that doesn't exist.
+        VERIFY_ARE_EQUAL(h.end(), h.find(U("key2")));
+    }
+
+    TEST_FIXTURE(uri_address, headers_add)
+    {
+        // Add multiple
+        http_headers h;
+        h.add(U("key1"), 22);
+        h.add(U("key2"), U("str2"));
+        VERIFY_ARE_EQUAL(U("22"), h[U("key1")]);
+        VERIFY_ARE_EQUAL(U("str2"), h[U("key2")]);
+
+        // Add one that already exists
+        h.add(U("key2"), U("str3"));
+        VERIFY_ARE_EQUAL(U("str2, str3"), h[U("key2")]);
+
+        // Add with different case
+        h.add(U("KEY2"), U("str4"));
+        VERIFY_ARE_EQUAL(U("str2, str3, str4"), h[U("keY2")]);
+
+        // Add with spaces in string
+        h.add(U("key3"), U("value with spaces"));
+        VERIFY_ARE_EQUAL(U("value with spaces"), h[U("key3")]);
+    }
+
+    TEST_FIXTURE(uri_address, headers_iterators)
+    {
+        // begin when empty
+        http_headers h;
+        VERIFY_ARE_EQUAL(h.begin(), h.end());
+
+        // with some values.
+        h.add(U("key1"), U("value1"));
+        h.add(U("key2"), U("value2"));
+        h.add(U("key3"), U("value3"));
+        http_headers::const_iterator iter = h.begin();
+        VERIFY_ARE_EQUAL(U("value1"), iter->second);
+        ++iter;
+        VERIFY_ARE_EQUAL(U("value2"), iter->second);
+        ++iter;
+        VERIFY_ARE_EQUAL(U("value3"), iter->second);
+        ++iter;
+        VERIFY_ARE_EQUAL(h.end(), iter);
+    }
+
+    TEST_FIXTURE(uri_address, headers_foreach)
+    {
+        // begin when empty
+        http_headers h;
+        VERIFY_ARE_EQUAL(h.begin(), h.end());
+
+        // with some values.
+        h.add(U("key1"), U("value"));
+        h.add(U("key2"), U("value"));
+        h.add(U("key3"), U("value"));
+
+        std::for_each(std::begin(h), std::end(h), [=](http_headers::const_reference kv) {
+            VERIFY_ARE_EQUAL(U("value"), kv.second);
+        });
+
+        std::for_each(
+            std::begin(h), std::end(h), [=](http_headers::reference kv) { VERIFY_ARE_EQUAL(U("value"), kv.second); });
+    }
+
+    TEST_FIXTURE(uri_address, response_headers)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        std::map<utility::string_t, utility::string_t> headers;
+        headers[U("H1")] = U("");
+        headers[U("H2")] = U("hah");
+        headers[U("H3")] = U("es");
+        headers[U("H4")] = U("es;kjr");
+        headers[U("H5")] = U("asb");
+        headers[U("H6")] = U("abc");
+        headers[U("H7")] = U("eds");
+        headers[U("H8")] = U("blue");
+        headers[U("H9")] = U("sd");
+        headers[U("H10")] = U("res");
+        test_server_utilities::verify_request(
+            &client, methods::GET, U("/"), scoped.server(), status_codes::OK, headers);
+    }
+
+    TEST_FIXTURE(uri_address, cache_control_header)
+    {
+        http_headers headers;
+        VERIFY_ARE_EQUAL(headers.cache_control(), U(""));
+        const utility::string_t value(U("custom value"));
+        headers.set_cache_control(value);
+        VERIFY_ARE_EQUAL(headers.cache_control(), value);
+        utility::string_t foundValue;
+        VERIFY_IS_TRUE(headers.match(header_names::cache_control, foundValue));
+        VERIFY_ARE_EQUAL(value, foundValue);
+    }
+
+    TEST_FIXTURE(uri_address, content_length_header)
+    {
+        http_headers headers;
+        VERIFY_ARE_EQUAL(headers.content_length(), 0);
+        const size_t value = 44;
+        headers.set_content_length(value);
+        VERIFY_ARE_EQUAL(headers.content_length(), value);
+        size_t foundValue;
+        VERIFY_IS_TRUE(headers.match(header_names::content_length, foundValue));
+        VERIFY_ARE_EQUAL(value, foundValue);
+    }
+
+    TEST_FIXTURE(uri_address, date_header)
+    {
+        http_headers headers;
+        VERIFY_ARE_EQUAL(headers.date(), U(""));
+        const utility::datetime value(utility::datetime::utc_now());
+        headers.set_date(value);
+        VERIFY_ARE_EQUAL(headers.date(), value.to_string());
+        utility::string_t foundValue;
+        VERIFY_IS_TRUE(headers.match(header_names::date, foundValue));
+        VERIFY_ARE_EQUAL(value.to_string(), foundValue);
+    }
+
+    TEST_FIXTURE(uri_address, parsing_content_type_redundantsemicolon_json)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        web::json::value body = web::json::value::string(U("Json body"));
+
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[header_names::content_type] = U("application/json; charset=utf-8;;;;");
+            p_request->reply(200, U("OK"), headers, utility::conversions::to_utf8string(body.serialize()));
+        });
+
+        http_client client(m_uri);
+        auto resp = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(resp.extract_json().get().serialize(), body.serialize());
+    }
+
+    TEST_FIXTURE(uri_address, parsing_content_type_redundantsemicolon_string)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        std::string body("Body");
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[header_names::content_type] = U("text/plain; charset  =  UTF-8;;;; ");
+            p_request->reply(200, U("OK"), headers, body);
+        });
+
+        http_client client(m_uri);
+        auto resp = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(resp.extract_string().get(), utility::conversions::to_string_t(body));
+    }
+
+    TEST_FIXTURE(uri_address, overwrite_http_header)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        // Test default case of cpprestsdk setting host header as host:port
+        auto& host = m_uri.host();
+        int port = m_uri.port();
+        utility::string_t expected_default_header = host + U(":") + utility::conversions::details::to_string_t(port);
+        http_request default_host_headers_request(methods::GET);
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            auto headers = p_request->m_headers;
+            VERIFY_ARE_EQUAL(expected_default_header, headers[header_names::host]);
+            p_request->reply(200);
+        });
+
+        client.request(default_host_headers_request).get();
+
+#ifndef __cplusplus_winrt
+        // Test case where we overwrite the host header
+        http_request overwritten_host_headers_request(methods::GET);
+        overwritten_host_headers_request.headers().add(U("Host"), host);
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            auto headers = p_request->m_headers;
+            VERIFY_ARE_EQUAL(host, headers[header_names::host]);
+            p_request->reply(200);
+        });
+        client.request(overwritten_host_headers_request).get();
+#endif
+    }
+} // SUITE(header_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/http_client_fuzz_tests.cpp b/Release/tests/functional/http/client/http_client_fuzz_tests.cpp
new file mode 100644 (file)
index 0000000..85cb9de
--- /dev/null
@@ -0,0 +1,102 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * http_client_fuzz_tests.cpp
+ *
+ * Tests cases for fuzzing http_client (headers).
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::client;
+using namespace concurrency;
+using namespace concurrency::streams;
+using namespace utility;
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(http_client_fuzz_tests)
+{
+    class fuzz_uri_address
+    {
+    public:
+        // Ensure that your traffic goes to port 8877 on the machine where NetFuzz is running
+        // Netfuzz sets an HTTP proxy at that location which your client must talk to
+        fuzz_uri_address() : m_uri(U("http://localhost:8877/")) {}
+        web::http::uri m_uri;
+    };
+
+    TEST_FIXTURE(fuzz_uri_address, fuzz_header_basic, "Ignore", "Manual")
+    {
+        http_client client(m_uri);
+        method requestMethod = methods::GET;
+        http_request msg(requestMethod);
+
+        try
+        {
+            auto response = client.request(msg).get();
+            printf("Response code:%d\n", response.status_code());
+            auto response2 = response.content_ready().get();
+            printf("Response2 code:%d\n", response2.status_code());
+        }
+        catch (http_exception& e)
+        {
+            printf("Exception:%s\n", e.what());
+        }
+    }
+
+    TEST_FIXTURE(fuzz_uri_address, fuzz_request_headers, "Ignore", "Manual")
+    {
+        http_client client(m_uri);
+        http_request msg(methods::POST);
+
+#ifndef __cplusplus_winrt
+        // The WinRT-based HTTP stack does not support headers that have no
+        // value, which means that there is no point in making this particular
+        // header test, it is an unsupported feature on WinRT.
+        msg.headers().add(U("HEHE"), U(""));
+#endif
+
+        msg.headers().add(U("MyHeader"), U("hehe;blach"));
+        msg.headers().add(U("Yo1"), U("You, Too"));
+        msg.headers().add(U("Yo2"), U("You2"));
+        msg.headers().add(U("Yo3"), U("You3"));
+        msg.headers().add(U("Yo4"), U("You4"));
+        msg.headers().add(U("Yo5"), U("You5"));
+        msg.headers().add(U("Yo6"), U("You6"));
+        msg.headers().add(U("Yo7"), U("You7"));
+        msg.headers().add(U("Yo8"), U("You8"));
+        msg.headers().add(U("Yo9"), U("You9"));
+        msg.headers().add(U("Yo10"), U("You10"));
+        msg.headers().add(U("Yo11"), U("You11"));
+        msg.headers().add(U("Accept"), U("text/plain"));
+        VERIFY_ARE_EQUAL(U("You5"), msg.headers()[U("Yo5")]);
+        try
+        {
+            auto response = client.request(msg).get();
+            printf("Response code:%d\n", response.status_code());
+        }
+        catch (http_exception& e)
+        {
+            printf("Exception:%s\n", e.what());
+        }
+    }
+} // SUITE(http_client_fuzz_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/http_client_tests.cpp b/Release/tests/functional/http/client/http_client_tests.cpp
new file mode 100644 (file)
index 0000000..ecbec98
--- /dev/null
@@ -0,0 +1,55 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * http_client_tests.cpp
+ *
+ * Common definitions and helper functions for http_client test cases.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+void test_connection(test_http_server* p_server, http_client* p_client, const utility::string_t& path)
+{
+    p_server->next_request().then([path](test_request* p_request) {
+        http_asserts::assert_test_request_equals(p_request, methods::GET, path);
+        VERIFY_ARE_EQUAL(0u, p_request->reply(200));
+    });
+    http_asserts::assert_response_equals(p_client->request(methods::GET).get(), status_codes::OK);
+}
+
+// Helper function send a simple request to test the connection.
+// Take in the path to request and what path should be received in the server.
+void test_connection(test_http_server* p_server,
+                     http_client* p_client,
+                     const utility::string_t& request_path,
+                     const utility::string_t& expected_path)
+{
+    p_server->next_request().then([expected_path](test_request* p_request) {
+        http_asserts::assert_test_request_equals(p_request, methods::GET, expected_path);
+        VERIFY_ARE_EQUAL(0u, p_request->reply(200));
+    });
+    http_asserts::assert_response_equals(p_client->request(methods::GET, request_path).get(), status_codes::OK);
+}
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/http_client_tests.h b/Release/tests/functional/http/client/http_client_tests.h
new file mode 100644 (file)
index 0000000..7224cb1
--- /dev/null
@@ -0,0 +1,51 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * http_client_tests.h
+ *
+ * Common declarations and helper functions for http_client test cases.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/http_client.h"
+#include "http_test_utilities.h"
+#include "unittestpp.h"
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+class uri_address
+{
+public:
+    uri_address() : m_uri(U("http://localhost:34568/")) {}
+    web::http::uri m_uri;
+};
+
+// Helper function to send a simple request to a server to test
+// the connection.
+void test_connection(tests::functional::http::utilities::test_http_server* p_server,
+                     web::http::client::http_client* p_client,
+                     const utility::string_t& path);
+
+// Helper function send a simple request to test the connection.
+// Take in the path to request and what path should be received in the server.
+void test_connection(tests::functional::http::utilities::test_http_server* p_server,
+                     web::http::client::http_client* p_client,
+                     const utility::string_t& request_path,
+                     const utility::string_t& expected_path);
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/http_methods_tests.cpp b/Release/tests/functional/http/client/http_methods_tests.cpp
new file mode 100644 (file)
index 0000000..b8f60c9
--- /dev/null
@@ -0,0 +1,104 @@
+/***
+ * 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 HTTP methods.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(http_methods_tests)
+{
+    // Tests the defined methods and custom methods.
+    TEST_FIXTURE(uri_address, http_methods)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        // Don't include 'CONNECT' it has a special meaning.
+        utility::string_t send_methods[] = {methods::GET,
+                                            U("GET"),
+                                            methods::DEL,
+                                            methods::HEAD,
+#ifdef _WIN32 // -  this is never passed to the listener with http_listener
+                                            methods::OPTIONS,
+#endif
+                                            methods::POST,
+                                            methods::PUT,
+                                            methods::PATCH,
+#ifndef __cplusplus_winrt
+#ifdef _WIN32 // - ditto
+                                            methods::TRCE,
+#endif
+#endif
+
+                                            U("CUstomMETHOD")};
+        utility::string_t recv_methods[] = {U("GET"),
+                                            U("GET"),
+                                            U("DELETE"),
+                                            U("HEAD"),
+#ifdef _WIN32
+                                            U("OPTIONS"),
+#endif
+                                            U("POST"),
+                                            U("PUT"),
+                                            U("PATCH"),
+#ifndef __cplusplus_winrt
+#ifdef _WIN32
+                                            U("TRACE"),
+#endif
+#endif
+
+                                            U("CUstomMETHOD")};
+        const size_t num_methods = sizeof(send_methods) / sizeof(send_methods[0]);
+
+        for (int i = 0; i < num_methods; ++i)
+        {
+            p_server->next_request().then([i, &recv_methods](test_request* p_request) {
+                http_asserts::assert_test_request_equals(p_request, recv_methods[i], U("/"));
+                VERIFY_ARE_EQUAL(0u, p_request->reply(200));
+            });
+            http_asserts::assert_response_equals(client.request(send_methods[i]).get(), status_codes::OK);
+        }
+    }
+
+#ifdef __cplusplus_winrt
+    TEST_FIXTURE(uri_address, http_trace_fails_on_winrt)
+    {
+        http_client client(m_uri);
+        VERIFY_THROWS(client.request(methods::TRCE).get(), http_exception);
+    }
+#endif
+
+    TEST(http_request_empty_method) { VERIFY_THROWS(http_request(U("")), std::invalid_argument); }
+
+    TEST_FIXTURE(uri_address, empty_method)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        VERIFY_THROWS(client.request(U("")), std::invalid_argument);
+    }
+}
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/multiple_requests.cpp b/Release/tests/functional/http/client/multiple_requests.cpp
new file mode 100644 (file)
index 0000000..2f7d856
--- /dev/null
@@ -0,0 +1,153 @@
+/***
+ * 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"
+
+using namespace web;
+using namespace utility;
+using namespace utility::conversions;
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::common::utilities;
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+// Helper function to initialize an array of strings to contain 1 MB data.
+static void initialize_data(std::string* data_arrays, const size_t count)
+{
+    // 10k
+    std::string data;
+    for (int j = 0; j < 1024 * 10; ++j)
+    {
+        data.push_back('A' + (j % 26));
+    }
+
+    for (size_t i = 0; i < count; ++i)
+    {
+        data_arrays[i] = data;
+        data_arrays[i].push_back('a' + (char)i);
+    }
+}
+
+SUITE(multiple_requests)
+{
+    TEST_FIXTURE(uri_address, requests_with_data)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client_config config;
+        http_client client(m_uri, config);
+
+        const size_t num_requests = 20;
+        std::string request_body;
+        initialize_data(&request_body, 1);
+        const method method = methods::PUT;
+        const web::http::status_code code = status_codes::OK;
+
+        std::vector<pplx::task<test_request*>> reqs;
+        // response to requests
+        for (size_t i = 0; i < num_requests; ++i)
+        {
+            reqs.push_back(scoped.server()->next_request());
+        }
+
+        // send requests
+        std::vector<pplx::task<http_response>> responses;
+        for (size_t i = 0; i < num_requests; ++i)
+        {
+            http_request msg(method);
+            msg.set_body(request_body);
+            responses.push_back(client.request(msg));
+        }
+
+        for (auto&& requestTask : reqs)
+        {
+            auto request = requestTask.get();
+            http_asserts::assert_test_request_equals(
+                request, method, U("/"), U("text/plain"), to_string_t(request_body));
+            VERIFY_ARE_EQUAL(0u, request->reply(code));
+        }
+
+        // wait for requests.
+        for (size_t i = 0; i < num_requests; ++i)
+        {
+            try
+            {
+                http_asserts::assert_response_equals(responses[i].get(), code);
+            }
+            catch (...)
+            {
+                VERIFY_ARE_EQUAL(1, 0);
+            }
+        }
+    }
+
+    // Tests multiple requests with responses containing data.
+    TEST_FIXTURE(uri_address, responses_with_data)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        const size_t num_requests = 20;
+        std::string request_body;
+        initialize_data(&request_body, 1);
+        const method method = methods::PUT;
+        const web::http::status_code code = status_codes::OK;
+        std::map<utility::string_t, utility::string_t> headers;
+        headers[U("Content-Type")] = U("text/plain");
+
+        // response to requests
+        auto requestTasks = scoped.server()->next_requests(num_requests);
+
+        // send requests
+        std::vector<pplx::task<http_response>> responses;
+        for (size_t i = 0; i < num_requests; ++i)
+        {
+            responses.push_back(client.request(method));
+        }
+
+        // response to requests
+        for (size_t i = 0; i < num_requests; ++i)
+        {
+            test_request* request = requestTasks[i].get();
+            http_asserts::assert_test_request_equals(request, method, U("/"));
+            VERIFY_ARE_EQUAL(0u, request->reply(code, U(""), headers, request_body));
+        }
+
+        // wait for requests.
+        for (size_t i = 0; i < num_requests; ++i)
+        {
+            try
+            {
+                http_response rsp = responses[i].get();
+                http_asserts::assert_response_equals(rsp, code, headers);
+                VERIFY_ARE_EQUAL(to_string_t(request_body), rsp.extract_string().get());
+            }
+            catch (...)
+            {
+                VERIFY_ARE_EQUAL(1, 0);
+            }
+        }
+    }
+
+} // SUITE(multiple_requests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/oauth1_tests.cpp b/Release/tests/functional/http/client/oauth1_tests.cpp
new file mode 100644 (file)
index 0000000..97a1bce
--- /dev/null
@@ -0,0 +1,324 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Test cases for oauth1.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "cpprest/details/http_helpers.h"
+
+using namespace web;
+using namespace web::http;
+using namespace web::http::client;
+using namespace web::http::details;
+using namespace web::http::oauth1::experimental;
+using namespace web::http::oauth1::details;
+using namespace utility;
+using namespace concurrency;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(oauth1_tests)
+{
+    struct oauth1_test_config
+    {
+        oauth1_test_config()
+            : m_server_uri(U("http://localhost:17778/"))
+            , m_test_token(U("test_token"), U("test_token_secret"))
+            , m_oauth1_config(U("test_key"),
+                              U("test_secret"),
+                              m_server_uri,
+                              m_server_uri,
+                              m_server_uri,
+                              m_server_uri,
+                              oauth1_methods::hmac_sha1)
+            , m_oauth1_handler(std::shared_ptr<oauth1_config>(new oauth1_config(m_oauth1_config)))
+        {
+        }
+
+        const utility::string_t m_server_uri;
+        const oauth1_token m_test_token;
+
+        oauth1_config m_oauth1_config;
+        oauth1_handler m_oauth1_handler;
+    };
+
+    struct oauth1_token_setup : public oauth1_test_config
+    {
+        oauth1_token_setup() { m_oauth1_config.set_token(m_test_token); }
+    };
+
+    struct oauth1_server_setup : public oauth1_test_config
+    {
+        oauth1_server_setup() : m_server(m_server_uri) {}
+
+        test_http_server::scoped_server m_server;
+    };
+
+#define TEST_ACCESSOR(value_, name_)                                                                                   \
+    t.set_##name_(value_);                                                                                             \
+    VERIFY_ARE_EQUAL(value_, t.name_());
+
+    TEST(oauth1_token_accessors)
+    {
+        oauth1_token t(U(""), U(""));
+        TEST_ACCESSOR(U("a%123"), access_token)
+        TEST_ACCESSOR(U("b%20456"), secret)
+
+        const auto key1 = U("abc");
+        const auto value1 = U("123");
+        const auto key2 = U("xyz");
+        const auto value2 = U("456");
+        t.set_additional_parameter(key1, value1);
+        t.set_additional_parameter(U("xyz"), U("456"));
+        const auto& parameters = t.additional_parameters();
+        VERIFY_ARE_EQUAL(parameters.at(key1), value1);
+        VERIFY_ARE_EQUAL(parameters.at(key2), value2);
+        t.clear_additional_parameters();
+        VERIFY_ARE_EQUAL(0, t.additional_parameters().size());
+    }
+
+    TEST(oauth1_config_accessors)
+    {
+        oauth1_config t(U(""), U(""), U(""), U(""), U(""), U(""), oauth1_methods::hmac_sha1);
+        TEST_ACCESSOR(U("Test123"), consumer_key)
+        TEST_ACCESSOR(U("bar456"), consumer_secret)
+        TEST_ACCESSOR(U("file:///123?123=a&1="), temp_endpoint)
+        TEST_ACCESSOR(U("x:yxw#0"), auth_endpoint)
+        TEST_ACCESSOR(U("baz:"), token_endpoint)
+        TEST_ACCESSOR(U("/xyzzy=2"), callback_uri)
+        TEST_ACCESSOR(oauth1_methods::plaintext, method)
+        TEST_ACCESSOR(U("wally.world x"), realm)
+
+        const auto key1 = U("abc");
+        const auto value1 = U("123");
+        const auto key2 = U("xyz");
+        const auto value2 = U("456");
+        t.add_parameter(key1, value1);
+        t.add_parameter(U("xyz"), U("456"));
+        const auto parameters = t.parameters();
+        VERIFY_ARE_EQUAL(parameters.at(key1), value1);
+        VERIFY_ARE_EQUAL(parameters.at(key2), value2);
+        t.clear_parameters();
+        VERIFY_ARE_EQUAL(0, t.parameters().size());
+        t.set_parameters(parameters);
+        const auto parameters2 = t.parameters();
+        VERIFY_ARE_EQUAL(parameters2.at(key1), value1);
+        VERIFY_ARE_EQUAL(parameters2.at(key2), value2);
+    }
+
+#undef TEST_ACCESSOR
+
+    // clang-format off
+    TEST_FIXTURE(oauth1_token_setup, oauth1_signature_base_string)
+    {
+        // Basic base string generation.
+        {
+            http_request r;
+            r.set_method(methods::POST);
+            r.set_request_uri(U("http://example.com:80/request?a=b&c=d")); // Port set to avoid default.
+
+            auto state = m_oauth1_config._generate_auth_state();
+            state.set_timestamp(U("12345678"));
+            state.set_nonce(U("ABCDEFGH"));
+
+            utility::string_t base_string = m_oauth1_config._build_signature_base_string(r, state);
+            utility::string_t correct_base_string(
+                U("POST&http%3A%2F%2Fexample.com%2Frequest&a%3Db%26c%3Dd%26oauth_consumer_key%3Dtest_key%26oauth_nonce%")
+                U("3DABCDEFGH%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D12345678%26oauth_token%3Dtest_")
+                U("token%26oauth_version%3D1.0"));
+            VERIFY_ARE_EQUAL(correct_base_string, base_string);
+        }
+
+        // Added "extra_param" and proper parameter normalization.
+        {
+            http_request r;
+            r.set_method(methods::POST);
+            r.set_request_uri(U("http://example.com:80/request?a=b&c=d"));
+
+            auto state = m_oauth1_config._generate_auth_state(U("oauth_test"), U("xyzzy"));
+            state.set_timestamp(U("12345678"));
+            state.set_nonce(U("ABCDEFGH"));
+
+            utility::string_t base_string = m_oauth1_config._build_signature_base_string(r, state);
+            utility::string_t correct_base_string(
+                U("POST&http%3A%2F%2Fexample.com%2Frequest&a%3Db%26c%3Dd%26oauth_consumer_key%3Dtest_key%26oauth_nonce%")
+                U("3DABCDEFGH%26oauth_signature_method%3DHMAC-SHA1%26oauth_test%3Dxyzzy%26oauth_timestamp%3D12345678%")
+                U("26oauth_token%3Dtest_token%26oauth_version%3D1.0"));
+            VERIFY_ARE_EQUAL(correct_base_string, base_string);
+        }
+
+        // Use application/x-www-form-urlencoded with parameters in body
+        {
+            http_request r(methods::POST);
+            r.set_request_uri(U("http://example.com:80/request?a=b&c=d")); // Port set to avoid default.
+            r.set_body("MyVariableOne=ValueOne&MyVariableTwo=ValueTwo", "application/x-www-form-urlencoded");
+
+            auto state = m_oauth1_config._generate_auth_state();
+            state.set_timestamp(U("12345678"));
+            state.set_nonce(U("ABCDEFGH"));
+
+            utility::string_t base_string = m_oauth1_config._build_signature_base_string(r, state);
+            utility::string_t correct_base_string(
+                U("POST&http%3A%2F%2Fexample.com%2Frequest&a%3Db%26c%3Dd%26MyVariableOne%3DValueOne%26%26MyVariableTwo%")
+                U("3DValueTwo%26oauth_consumer_key%3Dtest_key%26oauth_nonce%3DABCDEFGH%26oauth_signature_method%3DHMAC-")
+                U("SHA1%26oauth_timestamp%3D12345678%26oauth_token%3Dtest_token%26oauth_version%3D1.0"));
+        }
+    }
+
+    TEST_FIXTURE(oauth1_token_setup, oauth1_hmac_sha1_method)
+    {
+        http_request r;
+        r.set_method(methods::POST);
+        r.set_request_uri(U("http://example.com:80/request?a=b&c=d")); // Port set to avoid default.
+
+        auto state = m_oauth1_config._generate_auth_state();
+        state.set_timestamp(U("12345678"));
+        state.set_nonce(U("ABCDEFGH"));
+
+        utility::string_t signature = m_oauth1_config._build_hmac_sha1_signature(r, state);
+
+        utility::string_t correct_signature(U("iUq3VlP39UNXoJHXlKjgSTmjEs8="));
+        VERIFY_ARE_EQUAL(correct_signature, signature);
+    }
+
+    TEST_FIXTURE(oauth1_token_setup, oauth1_plaintext_method)
+    {
+        utility::string_t signature(m_oauth1_config._build_plaintext_signature());
+        utility::string_t correct_signature(U("test_secret&test_token_secret"));
+        VERIFY_ARE_EQUAL(correct_signature, signature);
+    }
+
+    TEST_FIXTURE(oauth1_server_setup, oauth1_hmac_sha1_request)
+    {
+        m_oauth1_config.set_token(m_test_token);
+        m_oauth1_config.set_method(oauth1_methods::hmac_sha1);
+
+        http_client_config client_config;
+        client_config.set_oauth1(m_oauth1_config);
+        http_client client(m_server_uri, client_config);
+
+        m_server.server()->next_request().then([](test_request* request) {
+            const utility::string_t header_authorization(request->m_headers[header_names::authorization]);
+            const utility::string_t prefix(
+                U("OAuth oauth_version=\"1.0\", oauth_consumer_key=\"test_key\", oauth_token=\"test_token\", ")
+                U("oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\""));
+            VERIFY_ARE_EQUAL(0, header_authorization.find(prefix));
+            request->reply(status_codes::OK);
+        });
+
+        VERIFY_IS_TRUE(m_oauth1_config.token().is_valid_access_token());
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+    }
+
+    TEST_FIXTURE(oauth1_server_setup, oauth1_plaintext_request)
+    {
+        m_oauth1_config.set_token(m_test_token);
+        m_oauth1_config.set_method(oauth1_methods::plaintext);
+
+        http_client_config client_config;
+        client_config.set_oauth1(m_oauth1_config);
+        http_client client(m_server_uri, client_config);
+
+        m_server.server()->next_request().then([](test_request* request) {
+            const utility::string_t header_authorization(request->m_headers[header_names::authorization]);
+            const utility::string_t prefix(
+                U("OAuth oauth_version=\"1.0\", oauth_consumer_key=\"test_key\", oauth_token=\"test_token\", ")
+                U("oauth_signature_method=\"PLAINTEXT\", oauth_timestamp=\""));
+            VERIFY_ARE_EQUAL(0, header_authorization.find(prefix));
+            request->reply(status_codes::OK);
+        });
+
+        VERIFY_IS_TRUE(m_oauth1_config.token().is_valid_access_token());
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+    }
+
+    TEST_FIXTURE(oauth1_server_setup, oauth1_build_authorization_uri)
+    {
+        m_server.server()->next_request().then([](test_request* request) {
+            const utility::string_t header_authorization(request->m_headers[header_names::authorization]);
+
+            // Verify prefix, and without 'oauth_token'.
+            const utility::string_t prefix(U("OAuth oauth_version=\"1.0\", oauth_consumer_key=\"test_key\", ")
+                                           U("oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\""));
+            VERIFY_ARE_EQUAL(0, header_authorization.find(prefix));
+
+            // Verify suffix with proper 'oauth_callback'.
+            const utility::string_t suffix(U(", oauth_callback=\"http%3A%2F%2Flocalhost%3A17778%2F\""));
+            VERIFY_IS_TRUE(std::equal(suffix.rbegin(), suffix.rend(), header_authorization.rbegin()));
+
+            // Reply with temporary token and secret.
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[header_names::content_type] = mime_types::application_x_www_form_urlencoded;
+            request->reply(status_codes::OK,
+                           U(""),
+                           headers,
+                           "oauth_token=testbar&oauth_token_secret=xyzzy&oauth_callback_confirmed=true");
+        });
+
+        VERIFY_IS_FALSE(m_oauth1_config.token().is_valid_access_token());
+        utility::string_t auth_uri = m_oauth1_config.build_authorization_uri().get();
+        VERIFY_ARE_EQUAL(auth_uri, U("http://localhost:17778/?oauth_token=testbar"));
+        VERIFY_IS_FALSE(m_oauth1_config.token().is_valid_access_token());
+    }
+
+    // NOTE: This test also covers token_from_verifier().
+    TEST_FIXTURE(oauth1_server_setup, oauth1_token_from_redirected_uri)
+    {
+        m_server.server()->next_request().then([](test_request* request) {
+            const utility::string_t header_authorization(request->m_headers[header_names::authorization]);
+
+            // Verify temporary token prefix.
+            const utility::string_t prefix(
+                U("OAuth oauth_version=\"1.0\", oauth_consumer_key=\"test_key\", oauth_token=\"xyzzy\", ")
+                U("oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\""));
+            VERIFY_ARE_EQUAL(0, header_authorization.find(prefix));
+
+            // Verify suffix with 'oauth_verifier'.
+            const utility::string_t suffix(U(", oauth_verifier=\"simsalabim\""));
+            VERIFY_IS_TRUE(std::equal(suffix.rbegin(), suffix.rend(), header_authorization.rbegin()));
+
+            // Verify we have 'oauth_nonce' and 'oauth_signature'.
+            VERIFY_ARE_NOT_EQUAL(utility::string_t::npos, header_authorization.find(U("oauth_nonce")));
+            VERIFY_ARE_NOT_EQUAL(utility::string_t::npos, header_authorization.find(U("oauth_signature")));
+
+            // Reply with access token and secret.
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[header_names::content_type] = mime_types::application_x_www_form_urlencoded;
+            request->reply(status_codes::OK, U(""), headers, "oauth_token=test&oauth_token_secret=bar");
+        });
+
+        m_oauth1_config.set_token(oauth1_token(U("xyzzy"), U(""))); // Simulate temporary token.
+
+        const web::http::uri redirected_uri(U("http://localhost:17778/?oauth_token=xyzzy&oauth_verifier=simsalabim"));
+        m_oauth1_config.token_from_redirected_uri(redirected_uri).wait();
+
+        VERIFY_IS_TRUE(m_oauth1_config.token().is_valid_access_token());
+        VERIFY_ARE_EQUAL(m_oauth1_config.token().access_token(), U("test"));
+        VERIFY_ARE_EQUAL(m_oauth1_config.token().secret(), U("bar"));
+    }
+
+    // clang-format on
+
+} // SUITE(oauth1_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/oauth2_tests.cpp b/Release/tests/functional/http/client/oauth2_tests.cpp
new file mode 100644 (file)
index 0000000..e1f5408
--- /dev/null
@@ -0,0 +1,396 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Test cases for oauth2.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "cpprest/details/http_helpers.h"
+
+using namespace web;
+using namespace web::http;
+using namespace web::http::client;
+using namespace web::http::details;
+using namespace web::http::oauth2::experimental;
+using namespace utility;
+using namespace concurrency;
+
+using namespace tests::functional::http::utilities;
+extern utility::string_t _to_base64(const unsigned char* ptr, size_t size);
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+static std::vector<unsigned char> to_body_data(utility::string_t str)
+{
+    const std::string utf8(conversions::to_utf8string(std::move(str)));
+    return std::vector<unsigned char>(utf8.data(), utf8.data() + utf8.size());
+}
+
+static bool is_application_x_www_form_urlencoded(test_request* request)
+{
+    const auto content_type(request->m_headers[header_names::content_type]);
+    return (0 == content_type.find(mime_types::application_x_www_form_urlencoded));
+}
+
+static utility::string_t get_request_user_agent(test_request* request)
+{
+    if (request->m_headers.find(header_names::user_agent) != request->m_headers.end())
+    {
+        return request->m_headers[header_names::user_agent];
+    }
+
+    return utility::string_t();
+}
+
+SUITE(oauth2_tests)
+{
+    struct oauth2_test_setup
+    {
+        oauth2_test_setup()
+            : m_uri(U("http://localhost:16743/"))
+            , m_oauth2_config(U("123ABC"), U("456DEF"), U("https://test1"), m_uri.to_string(), U("https://bar"))
+            , m_scoped(m_uri)
+        {
+        }
+
+        web::http::uri m_uri;
+        oauth2_config m_oauth2_config;
+        test_http_server::scoped_server m_scoped;
+    };
+
+#define TEST_ACCESSOR(value_, name_)                                                                                   \
+    t.set_##name_(value_);                                                                                             \
+    VERIFY_ARE_EQUAL(value_, t.name_());
+
+    TEST(oauth2_token_accessors)
+    {
+        oauth2_token t;
+        TEST_ACCESSOR(U("b%20456"), access_token)
+        TEST_ACCESSOR(U("a%123"), refresh_token)
+        TEST_ACCESSOR(U("b%20456"), token_type)
+        TEST_ACCESSOR(U("ad.ww xyz"), scope)
+        TEST_ACCESSOR(0, expires_in)
+        TEST_ACCESSOR(123, expires_in)
+    }
+
+    TEST(oauth2_config_accessors)
+    {
+        oauth2_config t(U(""), U(""), U(""), U(""), U(""));
+        TEST_ACCESSOR(U("ABC123abc"), client_key)
+        TEST_ACCESSOR(U("123abcABC"), client_secret)
+        TEST_ACCESSOR(U("x:/t/a?q=c&a#3"), auth_endpoint)
+        TEST_ACCESSOR(U("y:///?a=21#1=2"), token_endpoint)
+        TEST_ACCESSOR(U("z://?=#"), redirect_uri)
+        TEST_ACCESSOR(U("xyzw=stuv"), scope)
+        TEST_ACCESSOR(U("1234567890"), state)
+        TEST_ACCESSOR(U("keyx"), access_token_key)
+        TEST_ACCESSOR(true, implicit_grant)
+        TEST_ACCESSOR(false, implicit_grant)
+        TEST_ACCESSOR(true, bearer_auth)
+        TEST_ACCESSOR(false, bearer_auth)
+        TEST_ACCESSOR(true, http_basic_auth)
+        TEST_ACCESSOR(false, http_basic_auth)
+    }
+
+#undef TEST_ACCESSOR
+
+    TEST(oauth2_build_authorization_uri)
+    {
+        oauth2_config config(U(""), U(""), U(""), U(""), U(""));
+        config.set_state(U("xyzzy"));
+        config.set_implicit_grant(false);
+
+        // Empty authorization URI.
+        {
+            VERIFY_ARE_EQUAL(U("/?response_type=code&client_id=&redirect_uri=&state=xyzzy"),
+                             config.build_authorization_uri(false));
+        }
+
+        // Authorization URI with scope parameter.
+        {
+            config.set_scope(U("testing_123"));
+            VERIFY_ARE_EQUAL(U("/?response_type=code&client_id=&redirect_uri=&state=xyzzy&scope=testing_123"),
+                             config.build_authorization_uri(false));
+        }
+
+        // Full authorization URI with scope.
+        {
+            config.set_client_key(U("4567abcd"));
+            config.set_auth_endpoint(U("https://test1"));
+            config.set_redirect_uri(U("http://localhost:8080"));
+            VERIFY_ARE_EQUAL(U("https://test1/?response_type=code&client_id=4567abcd&redirect_uri=http://")
+                                 U("localhost:8080&state=xyzzy&scope=testing_123"),
+                             config.build_authorization_uri(false));
+        }
+
+        // Verify again with implicit grant.
+        {
+            config.set_implicit_grant(true);
+            VERIFY_ARE_EQUAL(U("https://test1/?response_type=token&client_id=4567abcd&redirect_uri=http://")
+                                 U("localhost:8080&state=xyzzy&scope=testing_123"),
+                             config.build_authorization_uri(false));
+        }
+
+        // Verify that a new state() will be generated.
+        {
+            const uri auth_uri(config.build_authorization_uri(true));
+            auto params = uri::split_query(auth_uri.query());
+            VERIFY_ARE_NOT_EQUAL(params[U("state")], U("xyzzy"));
+        }
+    }
+
+    TEST_FIXTURE(oauth2_test_setup, oauth2_token_from_code)
+    {
+        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=authorization_code&code=789GHI&redirect_uri=https%3A%2F%2Fbar")),
+                    request->m_body);
+
+                VERIFY_ARE_EQUAL(U("test_user_agent"), get_request_user_agent(request));
+
+                std::map<utility::string_t, utility::string_t> 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_code(U("789GHI")).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=authorization_code&code=789GHI&redirect_uri=https%3A%2F%")
+                                                  U("2Fbar&client_id=123ABC&client_secret=456DEF")),
+                                 request->m_body);
+
+                VERIFY_ARE_EQUAL(U("test_user_agent"), get_request_user_agent(request));
+
+                std::map<utility::string_t, utility::string_t> 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_code(U("789GHI")).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_token_from_redirected_uri)
+    {
+        // Authorization code grant.
+        {
+            m_scoped.server()->next_request().then([](test_request* request) {
+                std::map<utility::string_t, utility::string_t> headers;
+                headers[header_names::content_type] = mime_types::application_json;
+                request->reply(
+                    status_codes::OK, U(""), headers, "{\"access_token\":\"test1\",\"token_type\":\"bearer\"}");
+            });
+
+            m_oauth2_config.set_implicit_grant(false);
+            m_oauth2_config.set_state(U("xyzzy"));
+
+            const web::http::uri redirected_uri(m_uri.to_string() + U("?code=sesame&state=xyzzy"));
+            m_oauth2_config.token_from_redirected_uri(redirected_uri).wait();
+
+            VERIFY_IS_TRUE(m_oauth2_config.token().is_valid_access_token());
+            VERIFY_ARE_EQUAL(m_oauth2_config.token().access_token(), U("test1"));
+        }
+
+        // Implicit grant.
+        {
+            m_oauth2_config.set_implicit_grant(true);
+            const web::http::uri redirected_uri(m_uri.to_string() + U("#access_token=abcd1234&state=xyzzy"));
+            m_oauth2_config.token_from_redirected_uri(redirected_uri).wait();
+
+            VERIFY_IS_TRUE(m_oauth2_config.token().is_valid_access_token());
+            VERIFY_ARE_EQUAL(m_oauth2_config.token().access_token(), U("abcd1234"));
+        }
+    }
+
+    TEST_FIXTURE(oauth2_test_setup, oauth2_token_from_refresh)
+    {
+        oauth2_token token(U("accessing"));
+        token.set_refresh_token(U("refreshing"));
+        m_oauth2_config.set_token(token);
+        VERIFY_IS_TRUE(m_oauth2_config.is_enabled());
+
+        // Verify token refresh without scope.
+        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=refresh_token&refresh_token=refreshing")), request->m_body);
+
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[header_names::content_type] = mime_types::application_json;
+            request->reply(status_codes::OK,
+                           U(""),
+                           headers,
+                           "{\"access_token\":\"ABBA\",\"refresh_token\":\"BAZ\",\"token_type\":\"bearer\"}");
+        });
+
+        m_oauth2_config.token_from_refresh().wait();
+        VERIFY_ARE_EQUAL(U("ABBA"), m_oauth2_config.token().access_token());
+        VERIFY_ARE_EQUAL(U("BAZ"), m_oauth2_config.token().refresh_token());
+
+        // Verify chaining refresh tokens and refresh with scope.
+        m_scoped.server()->next_request().then([](test_request* request) {
+            VERIFY_IS_TRUE(is_application_x_www_form_urlencoded(request));
+
+            VERIFY_ARE_EQUAL(to_body_data(U("grant_type=refresh_token&refresh_token=BAZ&scope=xyzzy")),
+                             request->m_body);
+
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[header_names::content_type] = mime_types::application_json;
+            request->reply(status_codes::OK, U(""), headers, "{\"access_token\":\"done\",\"token_type\":\"bearer\"}");
+        });
+
+        m_oauth2_config.set_scope(U("xyzzy"));
+        m_oauth2_config.token_from_refresh().wait();
+        VERIFY_ARE_EQUAL(U("done"), m_oauth2_config.token().access_token());
+    }
+
+    TEST_FIXTURE(oauth2_test_setup, oauth2_bearer_token)
+    {
+        m_oauth2_config.set_token(oauth2_token(U("12345678")));
+        http_client_config config;
+
+        // Default, bearer token in "Authorization" header (bearer_auth() == true)
+        {
+            config.set_oauth2(m_oauth2_config);
+
+            http_client client(m_uri, config);
+            m_scoped.server()->next_request().then([](test_request* request) {
+                VERIFY_ARE_EQUAL(U("Bearer 12345678"), request->m_headers[header_names::authorization]);
+                VERIFY_ARE_EQUAL(U("/"), request->m_path);
+                request->reply(status_codes::OK);
+            });
+
+            http_response response = client.request(methods::GET).get();
+            VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+        }
+
+        // Bearer token in query, default access token key (bearer_auth() == false)
+        {
+            m_oauth2_config.set_bearer_auth(false);
+            config.set_oauth2(m_oauth2_config);
+
+            http_client client(m_uri, config);
+            m_scoped.server()->next_request().then([](test_request* request) {
+                VERIFY_ARE_EQUAL(U(""), request->m_headers[header_names::authorization]);
+                VERIFY_ARE_EQUAL(U("/?access_token=12345678"), request->m_path);
+                request->reply(status_codes::OK);
+            });
+
+            http_response response = client.request(methods::GET).get();
+            VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+        }
+
+        // Bearer token in query, updated token, custom access token key (bearer_auth() == false)
+        {
+            m_oauth2_config.set_bearer_auth(false);
+            m_oauth2_config.set_access_token_key(U("open"));
+            m_oauth2_config.set_token(oauth2_token(U("Sesame")));
+            config.set_oauth2(m_oauth2_config);
+
+            http_client client(m_uri, config);
+            m_scoped.server()->next_request().then([](test_request* request) {
+                VERIFY_ARE_EQUAL(U(""), request->m_headers[header_names::authorization]);
+                VERIFY_ARE_EQUAL(U("/?open=Sesame"), request->m_path);
+                request->reply(status_codes::OK);
+            });
+
+            http_response response = client.request(methods::GET).get();
+            VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+        }
+    }
+
+    TEST_FIXTURE(oauth2_test_setup, oauth2_token_parsing)
+    {
+        VERIFY_IS_FALSE(m_oauth2_config.is_enabled());
+
+        // Verify reply JSON 'access_token', 'refresh_token', 'expires_in' and 'scope'.
+        {
+            m_scoped.server()->next_request().then([](test_request* request) {
+                std::map<utility::string_t, utility::string_t> headers;
+                headers[header_names::content_type] = mime_types::application_json;
+                request->reply(status_codes::OK,
+                               U(""),
+                               headers,
+                               "{\"access_token\":\"123\",\"refresh_token\":\"ABC\",\"token_type\":\"bearer\","
+                               "\"expires_in\":12345678,\"scope\":\"baz\"}");
+            });
+
+            m_oauth2_config.token_from_code(U("")).wait();
+            VERIFY_ARE_EQUAL(U("123"), m_oauth2_config.token().access_token());
+            VERIFY_ARE_EQUAL(U("ABC"), m_oauth2_config.token().refresh_token());
+            VERIFY_ARE_EQUAL(12345678, m_oauth2_config.token().expires_in());
+            VERIFY_ARE_EQUAL(U("baz"), m_oauth2_config.token().scope());
+            VERIFY_IS_TRUE(m_oauth2_config.is_enabled());
+        }
+
+        // Verify undefined 'expires_in' and 'scope'.
+        {
+            m_scoped.server()->next_request().then([](test_request* request) {
+                std::map<utility::string_t, utility::string_t> headers;
+                headers[header_names::content_type] = mime_types::application_json;
+                request->reply(
+                    status_codes::OK, U(""), headers, "{\"access_token\":\"123\",\"token_type\":\"bearer\"}");
+            });
+
+            const utility::string_t test_scope(U("wally world"));
+            m_oauth2_config.set_scope(test_scope);
+
+            m_oauth2_config.token_from_code(U("")).wait();
+            VERIFY_ARE_EQUAL(oauth2_token::undefined_expiration, m_oauth2_config.token().expires_in());
+            VERIFY_ARE_EQUAL(test_scope, m_oauth2_config.token().scope());
+        }
+    }
+
+} // SUITE(oauth2_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/outside_tests.cpp b/Release/tests/functional/http/client/outside_tests.cpp
new file mode 100644 (file)
index 0000000..439772c
--- /dev/null
@@ -0,0 +1,293 @@
+/***
+ * 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 using http_clients to outside websites.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+#if defined(_MSC_VER) && !defined(__cplusplus_winrt)
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#include <winhttp.h>
+#pragma comment(lib, "winhttp")
+#endif
+#include "cpprest/details/http_helpers.h"
+#include "cpprest/rawptrstream.h"
+#include "os_utilities.h"
+#include <stdexcept>
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::common::utilities;
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(outside_tests)
+{
+    TEST_FIXTURE(uri_address, outside_cnn_dot_com)
+    {
+        handle_timeout([] {
+            // http://www.cnn.com redirects users from countries outside of the US to the "http://edition.cnn.com/" drop
+            // location
+            http_client client(U("http://edition.cnn.com"));
+
+            // CNN's main page doesn't use chunked transfer encoding.
+            http_response response = client.request(methods::GET).get();
+            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();
+            VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+            response.content_ready().wait();
+        });
+    }
+
+    TEST_FIXTURE(uri_address, outside_wikipedia_compressed_http_response)
+    {
+        if (web::http::compression::builtin::supported() == false)
+        {
+            // On platforms which do not support compressed http, nothing to check.
+            return;
+        }
+        http_client_config config;
+        config.set_request_compressed_response(true);
+
+        http_client client(U("https://en.wikipedia.org/wiki/HTTP_compression"), config);
+        http_request httpRequest(methods::GET);
+
+        http_response response = client.request(httpRequest).get();
+        VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+        response.content_ready().wait();
+
+        auto s = response.extract_utf8string().get();
+        VERIFY_IS_FALSE(s.empty());
+
+        utility::string_t encoding;
+        VERIFY_IS_TRUE(response.headers().match(web::http::header_names::content_encoding, encoding));
+
+        VERIFY_ARE_EQUAL(encoding, U("gzip"));
+    }
+
+    TEST_FIXTURE(uri_address, outside_google_dot_com)
+    {
+        // Use code.google.com instead of www.google.com, which redirects
+        http_client client(U("http://code.google.com"));
+        http_request request(methods::GET);
+        for (int i = 0; i < 2; ++i)
+        {
+            http_response response = client.request(request).get();
+            VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+        }
+    }
+
+    TEST_FIXTURE(uri_address, multiple_https_requests)
+    {
+        handle_timeout([&] {
+            // Use code.google.com instead of www.google.com, which redirects
+            http_client client(U("https://code.google.com"));
+
+            http_response response;
+            for (int i = 0; i < 5; ++i)
+            {
+                response = client.request(methods::GET).get();
+                VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+                response.content_ready().wait();
+            }
+        });
+    }
+
+#if (defined(_MSC_VER) && (_MSC_VER >= 1900)) && !CPPREST_FORCE_PPLX
+    TEST_FIXTURE(uri_address, multiple_https_requests_sync_scheduler)
+    {
+        struct sync_scheduler : public scheduler_interface
+        {
+        public:
+            virtual void schedule(TaskProc_t function, PVOID context) override { function(context); }
+        };
+
+        // Save the current ambient scheduler
+        const auto scheduler = get_cpprestsdk_ambient_scheduler();
+
+        // Change the ambient scheduler to one that schedules synchronously
+        static std::shared_ptr<scheduler_interface> syncScheduler = std::make_shared<sync_scheduler>();
+        set_cpprestsdk_ambient_scheduler(syncScheduler);
+
+        handle_timeout([&] {
+            // Use code.google.com instead of www.google.com, which redirects
+            http_client client(U("https://code.google.com"));
+
+            http_response response;
+            for (int i = 0; i < 5; ++i)
+            {
+                response = client.request(methods::GET).get();
+                VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+                response.content_ready().wait();
+            }
+        });
+
+        // Revert to the original scheduler
+        set_cpprestsdk_ambient_scheduler(scheduler);
+    }
+#endif
+
+    TEST_FIXTURE(uri_address, reading_google_stream)
+    {
+        handle_timeout([&] {
+            // Use code.google.com instead of www.google.com, which redirects
+            http_client simpleclient(U("http://code.google.com"));
+            utility::string_t path = m_uri.query();
+            http_response response = simpleclient.request(::http::methods::GET).get();
+
+            uint8_t chars[71];
+            memset(chars, 0, sizeof(chars));
+
+            streams::rawptr_buffer<uint8_t> temp(chars, sizeof(chars));
+
+            VERIFY_ARE_EQUAL(response.body().read(temp, 70).get(), 70);
+            // Uncomment the following line to output the chars.
+            // std::cout << chars << '\n';
+            VERIFY_ARE_EQUAL(strcmp((const char*)chars,
+                                    "<html>\n  <head>\n    <meta name=\"google-site-verification\" content=\"4zc"),
+                             0);
+        });
+    }
+
+    TEST_FIXTURE(uri_address, no_transfer_encoding_content_length)
+    {
+        handle_timeout([] {
+            http_client client(U("http://ws.audioscrobbler.com/2.0/") U(
+                "?method=artist.gettoptracks&artist=cher&api_key=6fcd59047568e89b1615975081258990&format=json"));
+
+            client.request(methods::GET)
+                .then([](http_response response) {
+                    VERIFY_ARE_EQUAL(response.status_code(), status_codes::OK);
+                    VERIFY_IS_FALSE(response.headers().has(header_names::content_length) &&
+                                    response.headers().has(header_names::transfer_encoding));
+                    return response.extract_string();
+                })
+                .then([](string_t result) {
+                    // Verify that the body size isn't empty.
+                    VERIFY_IS_TRUE(result.size() > 0);
+                })
+                .wait();
+        });
+    }
+
+    // Note additional sites for testing can be found at:
+    // https://badssl.com/
+    // https://www.ssllabs.com/ssltest/
+    // http://www.internetsociety.org/deploy360/resources/dane-test-sites/
+    // https://onlinessl.netlock.hu/#
+    static void test_failed_ssl_cert(const uri& base_uri)
+    {
+        handle_timeout([&base_uri] {
+            http_client client(base_uri);
+            auto requestTask = client.request(methods::GET);
+            VERIFY_THROWS(requestTask.get(), http_exception);
+        });
+    }
+
+#if !defined(__cplusplus_winrt)
+    static void test_ignored_ssl_cert(const uri& base_uri)
+    {
+        handle_timeout([&base_uri] {
+            http_client_config config;
+            config.set_validate_certificates(false);
+            http_client client(base_uri, config);
+            auto response = client.request(methods::GET).get();
+            VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+        });
+    }
+#endif // !defined(__cplusplus_winrt)
+
+    TEST(server_selfsigned_cert) { test_failed_ssl_cert(U("https://self-signed.badssl.com/")); }
+
+#if !defined(__cplusplus_winrt)
+    TEST(server_selfsigned_cert_ignored) { test_ignored_ssl_cert(U("https://self-signed.badssl.com/")); }
+#endif // !defined(__cplusplus_winrt)
+
+    TEST(server_hostname_mismatch) { test_failed_ssl_cert(U("https://wrong.host.badssl.com/")); }
+
+#if !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
+    TEST(server_hostname_host_override)
+    {
+        handle_timeout([] {
+            http_client client(U("https://wrong.host.badssl.com/"));
+            http_request req(methods::GET);
+            req.headers().add(U("Host"), U("badssl.com"));
+            auto response = client.request(req).get();
+            VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+        });
+    }
+
+    TEST(server_hostname_mismatch_ignored) { test_ignored_ssl_cert(U("https://wrong.host.badssl.com/")); }
+
+    TEST(server_hostname_host_override_after_upgrade)
+    {
+        http_client client(U("http://198.35.26.96/"));
+        http_request req(methods::GET);
+        req.headers().add(U("Host"), U("en.wikipedia.org"));
+        auto response = client.request(req).get();
+        VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+    }
+#endif // !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
+
+    TEST(server_cert_expired) { test_failed_ssl_cert(U("https://expired.badssl.com/")); }
+
+#if !defined(__cplusplus_winrt)
+    TEST(server_cert_expired_ignored) { test_ignored_ssl_cert(U("https://expired.badssl.com/")); }
+#endif // !defined(__cplusplus_winrt)
+
+    TEST(server_cert_revoked, "Ignore:Android", "229", "Ignore:Apple", "229", "Ignore:Linux", "229")
+    {
+        test_failed_ssl_cert(U("https://revoked.badssl.com/"));
+    }
+
+#if !defined(__cplusplus_winrt)
+    TEST(server_cert_revoked_ignored) { test_ignored_ssl_cert(U("https://revoked.badssl.com/")); }
+#endif // !defined(__cplusplus_winrt)
+
+    TEST(server_cert_untrusted) { test_failed_ssl_cert(U("https://untrusted-root.badssl.com/")); }
+
+#if !defined(__cplusplus_winrt)
+    TEST(server_cert_untrusted_ignored) { test_ignored_ssl_cert(U("https://untrusted-root.badssl.com/")); }
+#endif // !defined(__cplusplus_winrt)
+
+#if !defined(__cplusplus_winrt)
+    TEST(ignore_server_cert_invalid, "Ignore:Android", "229", "Ignore:Apple", "229", "Ignore:Linux", "229")
+    {
+        handle_timeout([] {
+            http_client_config config;
+            config.set_validate_certificates(false);
+            config.set_timeout(std::chrono::seconds(1));
+            http_client client(U("https://expired.badssl.com/"), config);
+
+            auto request = client.request(methods::GET).get();
+            VERIFY_ARE_EQUAL(status_codes::OK, request.status_code());
+        });
+    }
+#endif // !defined(__cplusplus_winrt)
+} // SUITE(outside_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/pipeline_stage_tests.cpp b/Release/tests/functional/http/client/pipeline_stage_tests.cpp
new file mode 100644 (file)
index 0000000..a13fa9a
--- /dev/null
@@ -0,0 +1,260 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * pipeline_stage_tests.cpp
+ *
+ * Tests cases using pipeline stages on an http_client.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(pipeline_stage_tests)
+{
+    TEST_FIXTURE(uri_address, http_counting_methods)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+
+        size_t count = 0;
+
+        auto response_counter = [&count](pplx::task<http_response> r_task) -> pplx::task<http_response> {
+            ++count;
+            return r_task;
+        };
+        auto request_counter =
+            [&count, response_counter](http_request request,
+                                       std::shared_ptr<http_pipeline_stage> next_stage) -> pplx::task<http_response> {
+            ++count;
+            return next_stage->propagate(request).then(response_counter);
+        };
+
+        http_client client(m_uri);
+        client.add_handler(request_counter);
+
+        // Don't include 'CONNECT' it has a special meaning.
+        utility::string_t send_methods[] = {methods::GET,
+                                            U("GET"),
+                                            methods::DEL,
+                                            methods::HEAD,
+#ifdef _WIN32 // this is never passed to the listener
+                                            methods::OPTIONS,
+#endif
+                                            methods::POST,
+                                            methods::PUT,
+                                            methods::PATCH,
+                                            U("CUstomMETHOD")};
+        utility::string_t recv_methods[] = {U("GET"),
+                                            U("GET"),
+                                            U("DELETE"),
+                                            U("HEAD"),
+#ifdef _WIN32
+                                            U("OPTIONS"),
+#endif
+                                            U("POST"),
+                                            U("PUT"),
+                                            U("PATCH"),
+                                            U("CUstomMETHOD")};
+        const size_t num_methods = sizeof(send_methods) / sizeof(send_methods[0]);
+
+        for (int i = 0; i < num_methods; ++i)
+        {
+            p_server->next_request().then([i, &recv_methods](test_request* p_request) {
+                http_asserts::assert_test_request_equals(p_request, recv_methods[i], U("/"));
+                VERIFY_ARE_EQUAL(0u, p_request->reply(200));
+            });
+            http_asserts::assert_response_equals(client.request(send_methods[i]).get(), status_codes::OK);
+        }
+
+        VERIFY_ARE_EQUAL(num_methods * 2, count);
+    }
+
+    TEST_FIXTURE(uri_address, http_short_circuit)
+    {
+        size_t count = 0;
+
+        auto request_counter = [&count](http_request request,
+                                        std::shared_ptr<http_pipeline_stage> next_stage) -> pplx::task<http_response> {
+            ++count;
+            request.reply(status_codes::Forbidden);
+            return request.get_response();
+        };
+
+        http_client client(m_uri);
+        client.add_handler(request_counter);
+
+        // Don't include 'CONNECT' it has a special meaning.
+        utility::string_t send_methods[] = {methods::GET,
+                                            U("GET"),
+                                            methods::DEL,
+                                            methods::HEAD,
+                                            methods::OPTIONS,
+                                            methods::POST,
+                                            methods::PUT,
+                                            methods::PATCH,
+                                            U("CUstomMETHOD")};
+        const size_t num_methods = sizeof(send_methods) / sizeof(send_methods[0]);
+
+        for (int i = 0; i < num_methods; ++i)
+        {
+            http_asserts::assert_response_equals(client.request(send_methods[i]).get(), status_codes::Forbidden);
+        }
+
+        VERIFY_ARE_EQUAL(num_methods, count);
+    }
+
+    TEST_FIXTURE(uri_address, http_short_circuit_multiple)
+    {
+        size_t count = 0;
+
+        auto reply_stage = [](http_request request,
+                              std::shared_ptr<http_pipeline_stage> next_stage) -> pplx::task<http_response> {
+            request.reply(status_codes::Forbidden);
+            return request.get_response();
+        };
+
+        auto count_stage = [&count](http_request request,
+                                    std::shared_ptr<http_pipeline_stage> next_stage) -> pplx::task<http_response> {
+            count++;
+            return next_stage->propagate(request);
+        };
+
+        http_client client(m_uri);
+        client.add_handler(count_stage);
+        client.add_handler(count_stage);
+        client.add_handler(reply_stage);
+
+        // Don't include 'CONNECT' it has a special meaning.
+        utility::string_t send_methods[] = {methods::GET,
+                                            U("GET"),
+                                            methods::DEL,
+                                            methods::HEAD,
+                                            methods::OPTIONS,
+                                            methods::POST,
+                                            methods::PUT,
+                                            methods::PATCH,
+                                            U("CUstomMETHOD")};
+        const size_t num_methods = sizeof(send_methods) / sizeof(send_methods[0]);
+
+        for (int i = 0; i < num_methods; ++i)
+        {
+            http_asserts::assert_response_equals(client.request(send_methods[i]).get(), status_codes::Forbidden);
+        }
+
+        VERIFY_ARE_EQUAL(num_methods * 2, count);
+    }
+
+    TEST_FIXTURE(uri_address, http_short_circuit_no_count)
+    {
+        size_t count = 0;
+
+        auto reply_stage = [](http_request request,
+                              std::shared_ptr<http_pipeline_stage> next_stage) -> pplx::task<http_response> {
+            request.reply(status_codes::Forbidden);
+            return request.get_response();
+        };
+
+        auto count_stage = [&count](http_request request,
+                                    std::shared_ptr<http_pipeline_stage> next_stage) -> pplx::task<http_response> {
+            count++;
+            return next_stage->propagate(request);
+        };
+
+        // The counting is prevented from happening, because the short-circuit come before the count.
+        http_client client(m_uri);
+        client.add_handler(reply_stage);
+        client.add_handler(count_stage);
+
+        // Don't include 'CONNECT' it has a special meaning.
+        utility::string_t send_methods[] = {methods::GET,
+                                            U("GET"),
+                                            methods::DEL,
+                                            methods::HEAD,
+                                            methods::OPTIONS,
+                                            methods::POST,
+                                            methods::PUT,
+                                            methods::PATCH,
+                                            U("CUstomMETHOD")};
+        const size_t num_methods = sizeof(send_methods) / sizeof(send_methods[0]);
+
+        for (int i = 0; i < num_methods; ++i)
+        {
+            http_asserts::assert_response_equals(client.request(send_methods[i]).get(), status_codes::Forbidden);
+        }
+
+        VERIFY_ARE_EQUAL(0u, count);
+    }
+
+    /// <summary>
+    /// Pipeline stage used for pipeline_stage_inspect_response.
+    /// </summary>
+    class modify_count_responses_stage : public http_pipeline_stage
+    {
+    public:
+        modify_count_responses_stage() : m_Count(0) {}
+
+        virtual pplx::task<http_response> propagate(http_request request)
+        {
+            request.headers().set_content_type(U("modified content type"));
+
+            auto currentStage = this->shared_from_this();
+            return next_stage()->propagate(request).then([currentStage](http_response response) -> http_response {
+                int prevCount = 0;
+                response.headers().match(U("My Header"), prevCount);
+                utility::stringstream_t data;
+                data << prevCount + ++std::dynamic_pointer_cast<modify_count_responses_stage>(currentStage)->m_Count;
+                response.headers().add(U("My Header"), data.str());
+                return response;
+            });
+        }
+
+    private:
+        int m_Count;
+    };
+
+    TEST_FIXTURE(uri_address, pipeline_stage_inspect_response)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        scoped.server()->next_request().then([](test_request* request) {
+            http_asserts::assert_test_request_equals(request, methods::GET, U("/"), U("modified content type"));
+            request->reply(status_codes::OK);
+        });
+
+        // Put in nested scope so we lose the reference on the shared pointer.
+        {
+            std::shared_ptr<http_pipeline_stage> countStage = std::make_shared<modify_count_responses_stage>();
+            client.add_handler(countStage);
+            std::shared_ptr<http_pipeline_stage> countStage2 = std::make_shared<modify_count_responses_stage>();
+            client.add_handler(countStage2);
+        }
+
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+        VERIFY_ARE_EQUAL(U("1, 2"), response.headers()[U("My Header")]);
+    }
+
+} // SUITE(pipeline_stage_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/progress_handler_tests.cpp b/Release/tests/functional/http/client/progress_handler_tests.cpp
new file mode 100644 (file)
index 0000000..320bcc5
--- /dev/null
@@ -0,0 +1,402 @@
+/***
+ * 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 manually building up HTTP requests with progress handlers.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#ifdef _WIN32
+#include <WinError.h>
+#endif
+
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(progress_handler_tests)
+{
+    TEST_FIXTURE(uri_address, set_progress_handler_no_bodies)
+    {
+        http_client_config config;
+        config.set_chunksize(512);
+
+        http_client client(m_uri, config);
+        const method mtd = methods::GET;
+        utility::size64_t upsize = 4711u, downsize = 4711u;
+        int calls = 0;
+
+        http_request msg(mtd);
+        msg.set_progress_handler([&](message_direction::direction direction, utility::size64_t so_far) {
+            calls += 1;
+            if (direction == message_direction::upload)
+                upsize = so_far;
+            else
+                downsize = so_far;
+        });
+
+        test_http_server::scoped_server scoped(m_uri);
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, mtd, U("/"));
+            std::map<utility::string_t, utility::string_t> headers;
+            p_request->reply(200, utility::string_t(U("OK")), headers);
+        });
+
+        auto response = client.request(msg).get();
+        http_asserts::assert_response_equals(response, status_codes::OK);
+
+        VERIFY_ARE_EQUAL(0, upsize);
+
+        response.content_ready().wait();
+
+        VERIFY_ARE_EQUAL(0, downsize);
+        VERIFY_ARE_EQUAL(2, calls);
+    }
+
+    TEST_FIXTURE(uri_address, set_progress_handler_upload)
+    {
+        http_client_config config;
+        config.set_chunksize(512);
+
+        http_client client(m_uri, config);
+        const method mtd = methods::POST;
+        utility::string_t data;
+        utility::string_t content_type = U("text/plain; charset=utf-8");
+
+        const size_t repeats = 5500;
+        for (size_t i = 0; i < repeats; ++i)
+            data.append(U("abcdefghihklmnopqrstuvwxyz"));
+
+        utility::size64_t upsize = 4711u, downsize = 4711u;
+        int calls = 0;
+
+        http_request msg(mtd);
+        msg.set_progress_handler([&](message_direction::direction direction, utility::size64_t so_far) {
+            calls += 1;
+            if (direction == message_direction::upload)
+                upsize = so_far;
+            else
+                downsize = so_far;
+        });
+
+        msg.set_body(data);
+
+        test_http_server::scoped_server scoped(m_uri);
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, mtd, U("/"), content_type, data);
+            std::map<utility::string_t, utility::string_t> headers;
+            p_request->reply(200, utility::string_t(U("OK")), headers);
+        });
+
+        auto response = client.request(msg).get();
+        http_asserts::assert_response_equals(response, status_codes::OK);
+
+        VERIFY_ARE_EQUAL(26u * repeats, upsize);
+
+        response.content_ready().wait();
+
+        VERIFY_ARE_EQUAL(0, downsize);
+        // We don't have very precise control over how much of a message is transferred
+        // in each chunk being sent or received, so we can't make an exact comparison here.
+        VERIFY_IS_TRUE(calls >= 3);
+    }
+
+    TEST_FIXTURE(uri_address, set_progress_handler_download)
+    {
+        http_client_config config;
+        config.set_chunksize(512);
+
+        http_client client(m_uri, config);
+        const method mtd = methods::GET;
+
+        utility::size64_t upsize = 4711u, downsize = 4711u;
+        int calls = 0;
+
+        http_request msg(mtd);
+        msg.set_progress_handler([&](message_direction::direction direction, utility::size64_t so_far) {
+            calls += 1;
+            if (direction == message_direction::upload)
+                upsize = so_far;
+            else
+                downsize = so_far;
+        });
+
+        const size_t repeats = 6000;
+
+        test_http_server::scoped_server scoped(m_uri);
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, mtd, U("/"));
+            std::string resp_data;
+            for (size_t i = 0; i < repeats; ++i)
+                resp_data.append("abcdefghihklmnopqrstuvwxyz");
+
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("Content-Type")] = U("text/plain");
+            p_request->reply(200, utility::string_t(U("OK")), headers, resp_data);
+        });
+
+        auto response = client.request(msg).get();
+        http_asserts::assert_response_equals(response, status_codes::OK);
+
+        VERIFY_ARE_EQUAL(0, upsize);
+
+        response.content_ready().wait();
+
+        VERIFY_ARE_EQUAL(26u * repeats, downsize);
+        // We don't have very precise control over how much of a message is transferred
+        // in each chunk being sent or received, so we can't make an exact comparison here.
+        VERIFY_IS_TRUE(calls > 4);
+    }
+
+    TEST_FIXTURE(uri_address, set_progress_handler_upload_and_download)
+    {
+        http_client_config config;
+        config.set_chunksize(512);
+
+        http_client client(m_uri, config);
+        const method mtd = methods::POST;
+        utility::string_t data;
+        utility::string_t content_type = U("text/plain; charset=utf-8");
+
+        const size_t repeats = 5500;
+        for (size_t i = 0; i < repeats; ++i)
+            data.append(U("abcdefghihklmnopqrstuvwxyz"));
+
+        utility::size64_t upsize = 4711u, downsize = 4711u;
+        int calls = 0;
+
+        http_request msg(mtd);
+        msg.set_progress_handler([&](message_direction::direction direction, utility::size64_t so_far) {
+            calls += 1;
+            if (direction == message_direction::upload)
+                upsize = so_far;
+            else
+                downsize = so_far;
+        });
+
+        msg.set_body(data);
+
+        test_http_server::scoped_server scoped(m_uri);
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, mtd, U("/"), content_type, data);
+            std::string resp_data;
+            for (size_t i = 0; i < repeats * 2; ++i)
+                resp_data.append("abcdefghihklmnopqrstuvwxyz");
+
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("Content-Type")] = U("text/plain");
+            p_request->reply(200, utility::string_t(U("OK")), headers, resp_data);
+        });
+
+        auto response = client.request(msg).get();
+        http_asserts::assert_response_equals(response, status_codes::OK);
+
+        VERIFY_ARE_EQUAL(26u * repeats, upsize);
+
+        response.content_ready().wait();
+
+        VERIFY_ARE_EQUAL(26u * repeats * 2, downsize);
+        // We don't have very precise control over how much of a message is transferred
+        // in each chunk being sent or received, so we can't make an exact comparison here.
+        VERIFY_IS_TRUE(calls > 4);
+    }
+
+    TEST_FIXTURE(uri_address, set_progress_handler_open_failure)
+    {
+        http_client client(U("http://localhost323:-1"));
+
+        const method mtd = methods::POST;
+        utility::string_t data;
+        utility::string_t content_type = U("text/plain; charset=utf-8");
+
+        const size_t repeats = 5500;
+        for (size_t i = 0; i < repeats; ++i)
+            data.append(U("abcdefghihklmnopqrstuvwxyz"));
+
+        utility::size64_t upsize = 4711u, downsize = 4711u;
+        int calls = 0;
+
+        http_request msg(mtd);
+        // We should never see this handler called.
+        msg.set_progress_handler([&](message_direction::direction direction, utility::size64_t so_far) {
+            calls += 1;
+            if (direction == message_direction::upload)
+                upsize = so_far;
+            else
+                downsize = so_far;
+        });
+
+        msg.set_body(data);
+
+        auto response = client.request(msg);
+        VERIFY_THROWS(response.get(), web::http::http_exception);
+        VERIFY_ARE_EQUAL(4711u, upsize);
+        VERIFY_ARE_EQUAL(4711u, downsize);
+        VERIFY_ARE_EQUAL(0, calls);
+    }
+
+    TEST_FIXTURE(uri_address, set_progress_handler_request_timeout)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client_config config;
+        config.set_chunksize(512);
+        config.set_timeout(utility::seconds(1));
+
+        http_client client(m_uri, config);
+
+        const method mtd = methods::POST;
+        utility::string_t data;
+        utility::string_t content_type = U("text/plain; charset=utf-8");
+
+        const size_t repeats = 5500;
+        for (size_t i = 0; i < repeats; ++i)
+            data.append(U("abcdefghihklmnopqrstuvwxyz"));
+
+        utility::size64_t upsize = 4711u, downsize = 4711u;
+        int calls = 0;
+
+        http_request msg(mtd);
+        // We should never see this handler called for download, but for upload should still happen, since
+        // there's a server (just not a very responsive one) and we're sending data to it.
+        msg.set_progress_handler([&](message_direction::direction direction, utility::size64_t so_far) {
+            calls += 1;
+            if (direction == message_direction::upload)
+                upsize = so_far;
+            else
+                downsize = so_far;
+        });
+
+        msg.set_body(data);
+        auto t = scoped.server()->next_request();
+        auto response = client.request(msg);
+
+#ifdef __APPLE__
+        // CodePlex 295
+        VERIFY_THROWS(response.get(), http_exception);
+#else
+        VERIFY_THROWS_HTTP_ERROR_CODE(response.get(), std::errc::timed_out);
+#endif
+        VERIFY_ARE_EQUAL(26u * repeats, upsize);
+        VERIFY_ARE_EQUAL(4711u, downsize);
+        // We don't have very precise control over how much of the message is transferred
+        // before the exception occurs, so we can't make an exact comparison here.
+        VERIFY_IS_TRUE(calls >= 2);
+        t.get();
+    }
+
+    TEST_FIXTURE(uri_address, upload_nobody_exception)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        http_request msg(methods::GET);
+
+        auto t = scoped.server()->next_request().then(
+            [&](test_request* p_request) { p_request->reply(200, utility::string_t(U("OK"))); });
+
+        msg.set_progress_handler([](message_direction::direction, utility::size64_t) {
+            // First all is for data upload completion
+            throw std::invalid_argument("fake error");
+        });
+
+        VERIFY_THROWS(client.request(msg).get(), std::invalid_argument);
+
+        t.get();
+    }
+
+    TEST_FIXTURE(uri_address, download_nobody_exception)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        http_request msg(methods::GET);
+
+        scoped.server()->next_request().then(
+            [&](test_request* p_request) { p_request->reply(200, utility::string_t(U("OK"))); });
+
+        int numCalls = 0;
+        msg.set_progress_handler([&](message_direction::direction, utility::size64_t) {
+            if (++numCalls == 2)
+            {
+                // second is for data download
+                throw std::invalid_argument("fake error");
+            }
+        });
+
+        VERIFY_THROWS(client.request(msg).get().content_ready().get(), std::invalid_argument);
+    }
+
+    TEST_FIXTURE(uri_address, data_upload_exception)
+    {
+        http_client client(m_uri);
+        http_request msg(methods::PUT);
+        msg.set_body(U("A"));
+
+        msg.set_progress_handler(
+            [&](message_direction::direction, utility::size64_t) { throw std::invalid_argument("fake error"); });
+
+        pplx::task<test_request*> t;
+        {
+            test_http_server::scoped_server scoped(m_uri);
+            t = scoped.server()->next_request();
+            VERIFY_THROWS(client.request(msg).get(), std::invalid_argument);
+        }
+        try
+        {
+            t.get();
+        }
+        catch (const std::runtime_error&)
+        { /* It is ok if the request does not complete before the server is shutdown */
+        }
+    }
+
+    TEST_FIXTURE(uri_address, data_download_exception, "Ignore:Windows", "395")
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        http_request msg(methods::GET);
+
+        auto t = scoped.server()->next_request().then([&](test_request* p_request) {
+            std::string resp_data("abc");
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("Content-Type")] = U("text/plain");
+            p_request->reply(200, utility::string_t(U("OK")), headers, resp_data);
+        });
+
+        int numCalls = 0;
+        msg.set_progress_handler([&](message_direction::direction, utility::size64_t) {
+            if (++numCalls == 2)
+            {
+                // 2rd is for data download
+                throw std::invalid_argument("fake error");
+            }
+        });
+
+        try
+        {
+            handle_timeout([&] { client.request(msg).get().content_ready().get(); });
+        }
+        catch (std::invalid_argument const&)
+        {
+            // Expected.
+        }
+        t.get();
+    }
+}
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/proxy_tests.cpp b/Release/tests/functional/http/client/proxy_tests.cpp
new file mode 100644 (file)
index 0000000..9a6c520
--- /dev/null
@@ -0,0 +1,222 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * proxy_tests.cpp
+ *
+ * Tests cases for using proxies with http_clients.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+// In order to run this test, replace this proxy uri with one that you have access to.
+static const auto proxy_uri = U("http://netproxy.redmond.corp.microsoft.com");
+
+SUITE(proxy_tests)
+{
+    TEST_FIXTURE(uri_address, web_proxy_uri)
+    {
+        uri u(proxy_uri);
+
+        web_proxy uri_proxy(u);
+        VERIFY_IS_TRUE(uri_proxy.is_specified());
+        VERIFY_IS_FALSE(uri_proxy.is_disabled());
+        VERIFY_IS_FALSE(uri_proxy.is_auto_discovery());
+        VERIFY_IS_FALSE(uri_proxy.is_default());
+        VERIFY_ARE_EQUAL(u, uri_proxy.address());
+    }
+
+    TEST_FIXTURE(uri_address, web_proxy_disabled)
+    {
+        web_proxy disabled_proxy(web_proxy::disabled);
+        VERIFY_IS_FALSE(disabled_proxy.is_specified());
+        VERIFY_IS_TRUE(disabled_proxy.is_disabled());
+        VERIFY_IS_FALSE(disabled_proxy.is_auto_discovery());
+        VERIFY_IS_FALSE(disabled_proxy.is_default());
+    }
+
+    TEST_FIXTURE(uri_address, web_proxy_discover)
+    {
+        web_proxy discover_proxy(web_proxy::use_auto_discovery);
+        VERIFY_IS_FALSE(discover_proxy.is_specified());
+        VERIFY_IS_FALSE(discover_proxy.is_disabled());
+        VERIFY_IS_TRUE(discover_proxy.is_auto_discovery());
+        VERIFY_IS_FALSE(discover_proxy.is_default());
+    }
+
+    TEST_FIXTURE(uri_address, web_proxy_default)
+    {
+        web_proxy default_proxy(web_proxy::use_default);
+        VERIFY_IS_FALSE(default_proxy.is_specified());
+        VERIFY_IS_FALSE(default_proxy.is_disabled());
+        VERIFY_IS_FALSE(default_proxy.is_auto_discovery());
+        VERIFY_IS_TRUE(default_proxy.is_default());
+    }
+
+    TEST_FIXTURE(uri_address, web_proxy_default_construct)
+    {
+        web_proxy default_proxy_2;
+        VERIFY_IS_FALSE(default_proxy_2.is_specified());
+        VERIFY_IS_FALSE(default_proxy_2.is_disabled());
+        VERIFY_IS_FALSE(default_proxy_2.is_auto_discovery());
+        VERIFY_IS_TRUE(default_proxy_2.is_default());
+    }
+
+    TEST_FIXTURE(uri_address, http_client_config_set_proxy)
+    {
+        http_client_config hconfig;
+        VERIFY_IS_TRUE(hconfig.proxy().is_default());
+
+        uri u = U("http://x");
+
+        hconfig.set_proxy(web_proxy(u));
+        VERIFY_ARE_EQUAL(u, hconfig.proxy().address());
+    }
+
+#ifndef __cplusplus_winrt
+    // IXHR2 does not allow the proxy settings to be changed
+    TEST_FIXTURE(uri_address, auto_discovery_proxy)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        auto t = scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(
+                p_request, methods::PUT, U("/"), U("text/plain"), U("this is a test"));
+            p_request->reply(status_codes::OK);
+        });
+        http_client_config config;
+        config.set_proxy(web_proxy::use_auto_discovery);
+
+        http_client client(m_uri, config);
+        http_asserts::assert_response_equals(client.request(methods::PUT, U("/"), U("this is a test")).get(),
+                                             status_codes::OK);
+
+        t.get();
+    }
+
+    TEST_FIXTURE(uri_address, disabled_proxy)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        auto t = scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(
+                p_request, methods::PUT, U("/"), U("text/plain"), U("sample data"));
+            p_request->reply(status_codes::OK);
+        });
+
+        http_client_config config;
+        config.set_proxy(web_proxy::disabled);
+
+        http_client client(m_uri, config);
+        http_asserts::assert_response_equals(client.request(methods::PUT, U("/"), U("sample data")).get(),
+                                             status_codes::OK);
+
+        t.get();
+    }
+
+#endif // __cplusplus_winrt
+
+#ifdef __cplusplus_winrt
+    TEST_FIXTURE(uri_address, no_proxy_options_on_winrt)
+    {
+        http_client_config config;
+        config.set_proxy(web_proxy::use_auto_discovery);
+        http_client client(m_uri, config);
+
+        VERIFY_THROWS(client.request(methods::GET, U("/")).get(), http_exception);
+    }
+#endif
+
+#ifndef __cplusplus_winrt
+    // Can't specify a proxy with WinRT implementation.
+    TEST_FIXTURE(uri_address,
+                 http_proxy_with_credentials,
+                 "Ignore:Linux",
+                 "Github 53",
+                 "Ignore:Apple",
+                 "Github 53",
+                 "Ignore:Android",
+                 "Github 53",
+                 "Ignore:IOS",
+                 "Github 53",
+                 "Ignore",
+                 "Manual")
+    {
+        web_proxy proxy(proxy_uri);
+        web::credentials cred(U("artur"), U("fred")); // relax, this is not my real password
+        proxy.set_credentials(cred);
+
+        http_client_config config;
+        config.set_proxy(proxy);
+
+        // Access to this server will succeed because the first request will not be challenged and hence
+        // my bogus credentials will not be supplied.
+        http_client client(U("http://www.microsoft.com"), config);
+
+        try
+        {
+            http_response response = client.request(methods::GET).get();
+            VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+            response.content_ready().wait();
+        }
+        catch (web::http::http_exception const& e)
+        {
+            if (e.error_code().value() == 12007)
+            {
+                // The above "netproxy.redmond.corp.microsoft.com" is an internal site not generally accessible.
+                // This will cause a failure to resolve the URL.
+                // This is ok.
+                return;
+            }
+            throw;
+        }
+    }
+
+    TEST_FIXTURE(uri_address, http_proxy, "Ignore", "Manual")
+    {
+        http_client_config config;
+        config.set_proxy(web_proxy(proxy_uri));
+
+        http_client client(U("http://httpbin.org"), config);
+
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+        response.content_ready().wait();
+    }
+
+    TEST_FIXTURE(uri_address, https_proxy, "Ignore", "Manual")
+    {
+        http_client_config config;
+        config.set_proxy(web_proxy(proxy_uri));
+
+        http_client client(U("https://httpbin.org"), config);
+
+        http_response response = client.request(methods::GET).get();
+        VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+        response.content_ready().wait();
+    }
+
+#endif
+
+} // SUITE(proxy_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/redirect_tests.cpp b/Release/tests/functional/http/client/redirect_tests.cpp
new file mode 100644 (file)
index 0000000..a9d4179
--- /dev/null
@@ -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 <Windows.h>
+#include <VersionHelpers.h>
+#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<void> 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<utility::string_t, utility::string_t> 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<void> 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<pplx::task<void>> 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<pplx::task<void>> 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<pplx::task<void>> 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<pplx::task<void>> 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<pplx::task<void>> 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<pplx::task<void>> 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<pplx::task<void>> 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<pplx::task<void>> 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<pplx::task<void>> 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_helper_tests.cpp b/Release/tests/functional/http/client/request_helper_tests.cpp
new file mode 100644 (file)
index 0000000..6c8ef5e
--- /dev/null
@@ -0,0 +1,277 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * request_helper_tests.cpp
+ *
+ * Tests cases for the convenience helper functions for making requests on http_client.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "cpprest/details/http_helpers.h"
+#include "cpprest/version.h"
+#include <fstream>
+
+using namespace web;
+using namespace utility;
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(request_helper_tests)
+{
+    TEST_FIXTURE(uri_address, do_not_fail_on_content_encoding_when_not_requested)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        auto& server = *scoped.server();
+        http_client client(m_uri);
+
+        server.next_request().then([](test_request* p_request) {
+            p_request->reply(200, U("OK"), {{header_names::content_encoding, U("chunked")}});
+        });
+
+        http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, fail_on_content_encoding_if_unsupported)
+    {
+        if (web::http::compression::builtin::supported())
+        {
+            test_http_server::scoped_server scoped(m_uri);
+            auto& server = *scoped.server();
+            http_client_config config;
+            config.set_request_compressed_response(true);
+            http_client client(m_uri, config);
+
+            server.next_request().then([](test_request* p_request) {
+                p_request->reply(200, U("OK"), {{header_names::content_encoding, U("unsupported-algorithm")}});
+            });
+
+            VERIFY_THROWS(client.request(methods::GET).get(), web::http::http_exception);
+        }
+    }
+
+    TEST_FIXTURE(uri_address, send_accept_encoding)
+    {
+        if (web::http::compression::builtin::supported())
+        {
+            test_http_server::scoped_server scoped(m_uri);
+            auto& server = *scoped.server();
+            http_client_config config;
+            config.set_request_compressed_response(true);
+            http_client client(m_uri, config);
+
+            std::atomic<bool> found_accept_encoding(false);
+
+            server.next_request().then([&found_accept_encoding](test_request* p_request) {
+                found_accept_encoding =
+                    p_request->m_headers.find(header_names::accept_encoding) != p_request->m_headers.end();
+                p_request->reply(200, U("OK"));
+            });
+
+            client.request(methods::GET).get();
+
+            VERIFY_IS_TRUE(found_accept_encoding);
+        }
+    }
+
+    TEST_FIXTURE(uri_address, do_not_send_accept_encoding)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        auto& server = *scoped.server();
+        http_client client(m_uri);
+
+        std::atomic<bool> found_accept_encoding(true);
+
+        server.next_request().then([&found_accept_encoding](test_request* p_request) {
+            utility::string_t header;
+
+            // On Windows, someone along the way (not us!) adds "Accept-Encoding: peerdist"
+            found_accept_encoding =
+                p_request->match_header(header_names::accept_encoding, header) && header != _XPLATSTR("peerdist");
+            p_request->reply(200, U("OK"));
+        });
+
+        client.request(methods::GET).get();
+
+        VERIFY_IS_FALSE(found_accept_encoding);
+    }
+
+    TEST_FIXTURE(uri_address, non_rvalue_bodies)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        // Without content type.
+        utility::string_t send_body = U("YES NOW SEND THE TROOPS!");
+        p_server->next_request().then([&send_body](test_request* p_request) {
+            http_asserts::assert_test_request_equals(
+                p_request, methods::PUT, U("/"), U("text/plain; charset=utf-8"), send_body);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(methods::PUT, U(""), send_body).get(), status_codes::OK);
+
+        // With content type.
+        utility::string_t content_type = U("custom_content");
+        test_server_utilities::verify_request(
+            &client, methods::PUT, U("/"), content_type, send_body, p_server, status_codes::OK, U("OK"));
+
+        // Empty body type
+        send_body.clear();
+        content_type = U("haha_type");
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::PUT, U("/"), content_type);
+            VERIFY_ARE_EQUAL(0u, p_request->m_body.size());
+            VERIFY_ARE_EQUAL(0u, p_request->reply(status_codes::OK, U("OK")));
+        });
+        http_asserts::assert_response_equals(
+            client.request(methods::PUT, U("/"), send_body, content_type).get(), status_codes::OK, U("OK"));
+    }
+
+    TEST_FIXTURE(uri_address, rvalue_bodies)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        // Without content type.
+        utility::string_t send_body = U("YES NOW SEND THE TROOPS!");
+        utility::string_t move_body = send_body;
+        p_server->next_request().then([&send_body](test_request* p_request) {
+            http_asserts::assert_test_request_equals(
+                p_request, methods::PUT, U("/"), U("text/plain; charset=utf-8"), send_body);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(methods::PUT, U(""), std::move(move_body)).get(),
+                                             status_codes::OK);
+
+        // With content type.
+        utility::string_t content_type = U("custom_content");
+        move_body = send_body;
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::PUT, U("/"), content_type, send_body);
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(
+            client.request(methods::PUT, U(""), std::move(move_body), content_type).get(), status_codes::OK);
+
+        // Empty body.
+        content_type = U("haha_type");
+        send_body.clear();
+        move_body = send_body;
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::PUT, U("/"), content_type);
+            VERIFY_ARE_EQUAL(0u, p_request->m_body.size());
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(
+            client.request(methods::PUT, U(""), std::move(move_body), content_type).get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, json_bodies)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        // JSON bool value.
+        json::value bool_value = json::value::boolean(true);
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(
+                p_request, methods::PUT, U("/"), U("application/json"), bool_value.serialize());
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(methods::PUT, U("/"), bool_value).get(), status_codes::OK);
+
+        // JSON null value.
+        json::value null_value = json::value::null();
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(
+                p_request, methods::PUT, U("/"), U("application/json"), null_value.serialize());
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(methods::PUT, U(""), null_value).get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, non_rvalue_2k_body)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        std::string body;
+        for (int i = 0; i < 2048; ++i)
+        {
+            body.append(1, (char)('A' + (i % 26)));
+        }
+        test_server_utilities::verify_request(&client,
+                                              methods::PUT,
+                                              U("/"),
+                                              U("text/plain"),
+                                              ::utility::conversions::to_string_t(body),
+                                              p_server,
+                                              status_codes::OK,
+                                              U("OK"));
+    }
+
+    TEST_FIXTURE(uri_address, default_user_agent)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        p_server->next_request().then([&](test_request* p_request) {
+            utility::stringstream_t stream;
+            stream << _XPLATSTR("cpprestsdk/") << CPPREST_VERSION_MAJOR << _XPLATSTR(".") << CPPREST_VERSION_MINOR
+                   << _XPLATSTR(".") << CPPREST_VERSION_REVISION;
+            utility::string_t foundHeader;
+            p_request->match_header(U("User-Agent"), foundHeader);
+            VERIFY_ARE_EQUAL(stream.str(), foundHeader);
+
+            p_request->reply(200);
+        });
+
+        http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, overwrite_user_agent)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        utility::string_t customUserAgent(U("MyAgent"));
+        p_server->next_request().then([&](test_request* p_request) {
+            utility::string_t foundHeader;
+            p_request->match_header(U("User-Agent"), foundHeader);
+            VERIFY_ARE_EQUAL(customUserAgent, foundHeader);
+
+            p_request->reply(200);
+        });
+
+        http_request request(methods::GET);
+        request.headers()[U("User-Agent")] = customUserAgent;
+        http_asserts::assert_response_equals(client.request(request).get(), status_codes::OK);
+    }
+
+} // SUITE(request_helper_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/request_stream_tests.cpp b/Release/tests/functional/http/client/request_stream_tests.cpp
new file mode 100644 (file)
index 0000000..6d38072
--- /dev/null
@@ -0,0 +1,455 @@
+/***
+ * 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 covering using streams with HTTP request with http_client.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if defined(__cplusplus_winrt)
+using namespace Windows::Storage;
+#endif
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+utility::string_t get_full_name(const utility::string_t& name)
+{
+#if defined(__cplusplus_winrt)
+    // On WinRT, we must compensate for the fact that we will be accessing files in the
+    // Documents folder
+    auto file = pplx::create_task(KnownFolders::DocumentsLibrary->CreateFileAsync(
+                                      ref new Platform::String(name.c_str()), CreationCollisionOption::ReplaceExisting))
+                    .get();
+    return file->Path->Data();
+#else
+    return name;
+#endif
+}
+
+template<typename _CharType>
+pplx::task<streams::streambuf<_CharType>> OPEN_R(const utility::string_t& name)
+{
+#if !defined(__cplusplus_winrt)
+    return streams::file_buffer<_CharType>::open(name, std::ios_base::in);
+#else
+    auto file =
+        pplx::create_task(KnownFolders::DocumentsLibrary->GetFileAsync(ref new Platform::String(name.c_str()))).get();
+
+    return streams::file_buffer<_CharType>::open(file, std::ios_base::in);
+#endif
+}
+
+SUITE(request_stream_tests)
+{
+    // Used to prepare data for stream tests
+    void fill_file(const utility::string_t& name, size_t repetitions = 1)
+    {
+        std::fstream stream(get_full_name(name), std::ios_base::out | std::ios_base::trunc);
+
+        for (size_t i = 0; i < repetitions; i++)
+            stream << "abcdefghijklmnopqrstuvwxyz";
+    }
+
+    void fill_buffer(streams::streambuf<uint8_t> rbuf, size_t repetitions = 1)
+    {
+        const char* text = "abcdefghijklmnopqrstuvwxyz";
+        size_t len = strlen(text);
+        for (size_t i = 0; i < repetitions; i++)
+            rbuf.putn_nocopy((const uint8_t*)text, len);
+    }
+
+#if defined(__cplusplus_winrt)
+    TEST_FIXTURE(uri_address, ixhr2_transfer_encoding)
+    {
+        // Transfer encoding chunked is not supported. Not specifying the
+        // content length should cause an exception from the task. Verify
+        // that there is no unobserved exception
+
+        http_client client(m_uri);
+
+        auto buf = streams::producer_consumer_buffer<uint8_t>();
+        buf.putc(22).wait();
+        buf.close(std::ios_base::out).wait();
+
+        http_request reqG(methods::PUT);
+        reqG.set_body(buf.create_istream());
+        VERIFY_THROWS(client.request(reqG).get(), http_exception);
+
+        VERIFY_THROWS(client.request(methods::POST, U(""), buf.create_istream(), 1).get(), http_exception);
+    }
+#endif
+
+    TEST_FIXTURE(uri_address, set_body_stream_1)
+    {
+        utility::string_t fname = U("set_body_stream_1.txt");
+        fill_file(fname);
+
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        auto stream = OPEN_R<uint8_t>(fname).get();
+        http_request msg(methods::POST);
+        msg.set_body(stream);
+#if defined(__cplusplus_winrt)
+        msg.headers().set_content_length(26);
+#endif
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::POST, U("/"));
+            VERIFY_ARE_EQUAL(26u, p_request->m_body.size());
+            std::string str_body(std::begin(p_request->m_body), std::end(p_request->m_body));
+            VERIFY_ARE_EQUAL(U("abcdefghijklmnopqrstuvwxyz"), ::utility::conversions::to_string_t(str_body));
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+        stream.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, set_body_stream_2)
+    {
+        utility::string_t fname = U("set_body_stream_2.txt");
+        fill_file(fname);
+
+        http_client_config config;
+        config.set_chunksize(16 * 1024);
+
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri, config);
+
+        auto stream = OPEN_R<uint8_t>(fname).get();
+        http_request msg(methods::POST);
+        msg.set_body(stream);
+#if defined(__cplusplus_winrt)
+        msg.headers().set_content_length(26);
+#endif
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::POST, U("/"));
+            VERIFY_ARE_EQUAL(26u, p_request->m_body.size());
+            std::string str_body(std::begin(p_request->m_body), std::end(p_request->m_body));
+            VERIFY_ARE_EQUAL(U("abcdefghijklmnopqrstuvwxyz"), ::utility::conversions::to_string_t(str_body));
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+        stream.close().wait();
+    }
+
+    // Implementation for request with stream test case.
+    static void stream_request_impl(
+        const uri& address, bool withContentLength, size_t chunksize, utility::string_t fname)
+    {
+        fill_file(fname);
+        //(withContentLength);
+        http_client_config config;
+        config.set_chunksize(chunksize);
+
+        test_http_server::scoped_server scoped(address);
+        test_http_server* p_server = scoped.server();
+        http_client client(address, config);
+
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::POST, U("/"));
+            VERIFY_ARE_EQUAL(26u, p_request->m_body.size());
+            std::string str_body(std::begin(p_request->m_body), std::end(p_request->m_body));
+            VERIFY_ARE_EQUAL(U("abcdefghijklmnopqrstuvwxyz"), ::utility::conversions::to_string_t(str_body));
+            p_request->reply(200);
+        });
+
+        auto stream = OPEN_R<uint8_t>(fname).get();
+
+        if (withContentLength)
+        {
+            http_asserts::assert_response_equals(
+                client.request(methods::POST, U(""), stream, 26, U("text/plain")).get(), status_codes::OK);
+        }
+        else
+        {
+#if defined __cplusplus_winrt
+            http_asserts::assert_response_equals(
+                client.request(methods::POST, U(""), stream, 26, U("text/plain")).get(), status_codes::OK);
+#else
+            http_asserts::assert_response_equals(client.request(methods::POST, U(""), stream, U("text/plain")).get(),
+                                                 status_codes::OK);
+#endif
+        }
+
+        stream.close().wait();
+    }
+
+#if !defined(__cplusplus_winrt)
+    TEST_FIXTURE(uri_address, without_content_length_1)
+    {
+        stream_request_impl(m_uri, false, 64 * 1024, U("without_content_length_1.txt"));
+    }
+
+    TEST_FIXTURE(uri_address, without_content_length_2)
+    {
+        stream_request_impl(m_uri, false, 1024, U("without_content_length_2.txt"));
+    }
+#endif
+
+    TEST_FIXTURE(uri_address, with_content_length_1)
+    {
+        stream_request_impl(m_uri, true, 64 * 1024, U("with_content_length_1.txt"));
+    }
+
+    TEST_FIXTURE(uri_address, producer_consumer_buffer_with_content_length)
+    {
+        streams::producer_consumer_buffer<uint8_t> rbuf;
+        fill_buffer(rbuf);
+        rbuf.close(std::ios_base::out);
+
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        http_request msg(methods::POST);
+        msg.set_body(streams::istream(rbuf));
+        msg.headers().set_content_length(26);
+
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::POST, U("/"));
+            VERIFY_ARE_EQUAL(26u, p_request->m_body.size());
+            std::string str_body(std::begin(p_request->m_body), std::end(p_request->m_body));
+            VERIFY_ARE_EQUAL(U("abcdefghijklmnopqrstuvwxyz"), ::utility::conversions::to_string_t(str_body));
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, stream_partial_from_start)
+    {
+        utility::string_t fname = U("stream_partial_from_start.txt");
+        fill_file(fname, 200);
+
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        http_request msg(methods::POST);
+        auto stream = OPEN_R<uint8_t>(fname).get().create_istream();
+        msg.set_body(stream);
+        msg.headers().set_content_length(4500);
+
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::POST, U("/"));
+            VERIFY_ARE_EQUAL(4500u, p_request->m_body.size());
+            std::string str_body(std::begin(p_request->m_body), std::end(p_request->m_body));
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        // We should only have read the first 4500 bytes.
+        auto length = stream.seek(0, std::ios_base::cur);
+        VERIFY_ARE_EQUAL((size_t)length, (size_t)4500);
+
+        stream.close().get();
+    }
+
+    TEST_FIXTURE(uri_address, stream_partial_from_middle)
+    {
+        utility::string_t fname = U("stream_partial_from_middle.txt");
+        fill_file(fname, 100);
+
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        http_request msg(methods::POST);
+        auto stream = OPEN_R<uint8_t>(fname).get().create_istream();
+        msg.set_body(stream);
+        msg.headers().set_content_length(13);
+
+        stream.seek(13, std::ios_base::cur);
+
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::POST, U("/"));
+            VERIFY_ARE_EQUAL(13u, p_request->m_body.size());
+            std::string str_body(std::begin(p_request->m_body), std::end(p_request->m_body));
+            VERIFY_ARE_EQUAL(str_body, "nopqrstuvwxyz");
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        // We should only have read the first 26 bytes.
+        auto length = stream.seek(0, std::ios_base::cur);
+        VERIFY_ARE_EQUAL((int)length, 26);
+
+        stream.close().get();
+    }
+
+    class test_exception : public std::exception
+    {
+    public:
+        test_exception() {}
+    };
+
+// Ignore on WinRT CodePlex 144
+#if !defined(__cplusplus_winrt)
+    TEST_FIXTURE(uri_address, set_body_stream_exception)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        scoped.server();
+        http_client client(m_uri);
+
+        streams::producer_consumer_buffer<uint8_t> buf;
+        const char* data = "abcdefghijklmnopqrstuvwxyz";
+        buf.putn_nocopy(reinterpret_cast<const uint8_t*>(data), 26).wait();
+
+        http_request msg(methods::POST);
+        msg.set_body(buf.create_istream());
+        msg.headers().set_content_length(26);
+
+        buf.close(std::ios::in, std::make_exception_ptr(test_exception())).wait();
+
+        VERIFY_THROWS(client.request(msg).get(), test_exception);
+
+        // Codeplex 328.
+#if !defined(_WIN32)
+        tests::common::utilities::os_utilities::sleep(1000);
+#endif
+    }
+#endif
+
+// These tests aren't possible on WinRT because they don't
+// specify a Content-Length.
+#if !defined(__cplusplus_winrt)
+    TEST_FIXTURE(uri_address, stream_close_early)
+    {
+        http_client client(m_uri);
+        test_http_server::scoped_server scoped(m_uri);
+        scoped.server()->next_request().then([](test_request* request) { request->reply(status_codes::OK); });
+
+        // Make request.
+        streams::producer_consumer_buffer<uint8_t> buf;
+        auto responseTask = client.request(methods::PUT, U(""), buf.create_istream());
+
+        // Write a bit of data then close the stream early.
+        unsigned char data[5] = {'1', '2', '3', '4', '5'};
+        buf.putn_nocopy(&data[0], 5).wait();
+
+        buf.close(std::ios::out).wait();
+
+        // Verify that the task completes successfully
+        http_asserts::assert_response_equals(responseTask.get(), status_codes::OK);
+    }
+
+    TEST_FIXTURE(uri_address, stream_close_early_with_exception)
+    {
+        http_client client(m_uri);
+        test_http_server::scoped_server scoped(m_uri);
+
+        // Make request.
+        streams::producer_consumer_buffer<uint8_t> buf;
+        auto responseTask = client.request(methods::PUT, U(""), buf.create_istream());
+
+        // Write a bit of data then close the stream early.
+        unsigned char data[5] = {'1', '2', '3', '4', '5'};
+        buf.putn_nocopy(&data[0], 5).wait();
+
+        buf.close(std::ios::out, std::make_exception_ptr(test_exception())).wait();
+
+        // Verify that the responseTask throws the exception set when closing the stream
+        VERIFY_THROWS(responseTask.get(), test_exception);
+
+        // Codeplex 328.
+#if !defined(_WIN32)
+        tests::common::utilities::os_utilities::sleep(1000);
+#endif
+    }
+#endif
+
+    // Ignore on WinRT only CodePlex 144
+#if !defined(__cplusplus_winrt)
+    TEST_FIXTURE(uri_address, stream_close_early_with_exception_and_contentlength)
+    {
+        http_client client(m_uri);
+        test_http_server::scoped_server scoped(m_uri);
+
+        // Make request.
+        streams::producer_consumer_buffer<uint8_t> buf;
+        auto responseTask = client.request(methods::PUT, U(""), buf.create_istream(), 10);
+
+        // Write a bit of data then close the stream early.
+        unsigned char data[5] = {'1', '2', '3', '4', '5'};
+        buf.putn_nocopy(&data[0], 5).wait();
+
+        buf.close(std::ios::out, std::make_exception_ptr(test_exception())).wait();
+
+        // Verify that the responseTask throws the exception set when closing the stream
+        VERIFY_THROWS(responseTask.get(), test_exception);
+
+        // Codeplex 328.
+#if !defined(_WIN32)
+        tests::common::utilities::os_utilities::sleep(1000);
+#endif
+    }
+#endif
+
+// Ignore on WinRT only CodePlex 144
+#if !defined(__cplusplus_winrt)
+    TEST_FIXTURE(uri_address, stream_close_early_with_contentlength, "Ignore:Apple", "328")
+    {
+        http_client client(m_uri);
+        test_http_server::scoped_server scoped(m_uri);
+
+        // Make request.
+        streams::producer_consumer_buffer<uint8_t> buf;
+        auto responseTask = client.request(methods::PUT, U(""), buf.create_istream(), 10);
+
+        // Write a bit of data then close the stream early.
+        unsigned char data[5] = {'1', '2', '3', '4', '5'};
+        buf.putn_nocopy(&data[0], 5).wait();
+
+        buf.close(std::ios::out).wait();
+
+        // Verify that the responseTask throws the exception set when closing the stream
+        VERIFY_THROWS(responseTask.get(), http_exception);
+
+        // Codeplex 328.
+#if !defined(_WIN32)
+        tests::common::utilities::os_utilities::sleep(1000);
+#endif
+    }
+#endif
+
+    TEST_FIXTURE(uri_address, get_with_body_nono)
+    {
+        http_client client(m_uri);
+
+        streams::producer_consumer_buffer<uint8_t> buf;
+
+        http_request reqG(methods::GET);
+        reqG.set_body(buf.create_istream());
+        VERIFY_THROWS(client.request(reqG).get(), http_exception);
+
+        http_request reqH(methods::HEAD);
+        reqH.set_body(buf.create_istream());
+        VERIFY_THROWS(client.request(reqH).get(), http_exception);
+    }
+
+} // SUITE(request_stream_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
new file mode 100644 (file)
index 0000000..5d61556
--- /dev/null
@@ -0,0 +1,176 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * request_uri_tests.cpp
+ *
+ * Tests cases covering various kinds of request URIs with http_client.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(request_uri_tests)
+{
+    // Tests path specified in requests with non-empty base path in client constructor.
+    TEST_FIXTURE(uri_address, path_non_empty_ctor)
+    {
+        uri address(U("http://localhost:45678/base_path/"));
+
+        // Path not starting with '/'.
+        {
+            test_http_server::scoped_server scoped(address);
+            http_client client(address);
+            test_connection(scoped.server(), &client, U("next_level"), U("/base_path/next_level"));
+        }
+
+        // Path starting with '/'.
+        {
+            test_http_server::scoped_server scoped(address);
+            http_client client(address);
+            test_connection(scoped.server(), &client, U("/next_level"), U("/base_path/next_level"));
+        }
+    }
+
+    // Tests path specified in requests with empty base path in client constructor.
+    TEST_FIXTURE(uri_address, path_empty_ctor)
+    {
+        uri address(U("http://localhost:45678"));
+
+        // NON empty path.
+        {
+            test_http_server::scoped_server scoped(address);
+            http_client client(address);
+            test_connection(scoped.server(), &client, U("next_level"), U("/next_level"));
+        }
+
+        // Request path of '*'
+        {
+            test_http_server::scoped_server scoped(address);
+            http_client client(address);
+            test_connection(scoped.server(), &client, U("*"), U("/*"));
+        }
+
+        // Empty base of '/' with request path starting with '/'.
+        address = uri(U("http://localhost:45678/"));
+        {
+            test_http_server::scoped_server scoped(address);
+            http_client client(address);
+            test_connection(scoped.server(), &client, U("/hehehe"), U("/hehehe"));
+        }
+    }
+
+    TEST_FIXTURE(uri_address, with_query_fragment)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        // query
+        test_connection(scoped.server(), &client, U("/hehehe?key1=value2&"), U("/hehehe?key1=value2&"));
+
+        // fragment
+
+        // WinRT implementation percent encodes the '#'.
+        utility::string_t expected_value = U("/heheh?key1=value2#fragment");
+#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
+        expected_value = percent_encode_pound(expected_value);
+#endif
+
+        test_connection(scoped.server(), &client, U("/heheh?key1=value2#fragment"), expected_value);
+    }
+
+    TEST_FIXTURE(uri_address, uri_encoding)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        // try with encoding string.
+        http_request msg(methods::GET);
+        msg.set_request_uri(U("/path1!!alreadyencoded"));
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::GET, U("/path1!!alreadyencoded"));
+            http_asserts::assert_test_request_contains_headers(p_request, msg.headers());
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        // verify encoding actual happens with plain.
+        msg = http_request(methods::GET);
+        msg.set_request_uri(web::http::uri::encode_uri(U("/path1 /encode")));
+        VERIFY_ARE_EQUAL(U("/path1%20/encode"), msg.relative_uri().to_string());
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, methods::GET, U("/path1%20/encode"));
+            http_asserts::assert_test_request_contains_headers(p_request, msg.headers());
+            p_request->reply(200);
+        });
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+    }
+
+    // Tests combining case URI query/fragments with relative URI query/fragments.
+    TEST_FIXTURE(uri_address, append_query_fragment)
+    {
+        // Try with query.
+        const utility::string_t base_uri_with_query =
+            web::http::uri_builder(m_uri).append(U("/path1?key1=value1")).to_string();
+        {
+            test_http_server::scoped_server scoped(m_uri);
+            test_http_server* p_server = scoped.server();
+            http_client client(base_uri_with_query);
+
+            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");
+#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);
+                p_request->reply(200);
+            });
+            http_asserts::assert_response_equals(client.request(methods::GET, U("?key2=value2#frag")).get(),
+                                                 status_codes::OK);
+        }
+
+        // Try with fragment.
+        const utility::string_t base_uri_with_frag(m_uri.to_string() + U("path1#fragment"));
+        {
+            test_http_server::scoped_server scoped(m_uri);
+            test_http_server* p_server = scoped.server();
+            http_client client(base_uri_with_frag);
+
+            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");
+#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);
+                p_request->reply(200);
+            });
+            http_asserts::assert_response_equals(client.request(methods::GET, U("path2?key2=value2#fg2")).get(),
+                                                 status_codes::OK);
+        }
+    }
+
+} // SUITE(request_uri_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/response_extract_tests.cpp b/Release/tests/functional/http/client/response_extract_tests.cpp
new file mode 100644 (file)
index 0000000..f269f62
--- /dev/null
@@ -0,0 +1,546 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * response_extract_tests.cpp
+ *
+ * Tests cases covering extract functions on HTTP response.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#ifndef __cplusplus_winrt
+#include "cpprest/http_listener.h"
+#endif
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace utility::conversions;
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(response_extract_tests)
+{
+    // Helper function to send a request and response with given values.
+    template<typename CharType>
+    static http_response send_request_response(test_http_server * p_server,
+                                               http_client * p_client,
+                                               const utility::string_t& content_type,
+                                               const std::basic_string<CharType>& data)
+    {
+        const method method = methods::GET;
+        const ::http::status_code code = status_codes::OK;
+        std::map<utility::string_t, utility::string_t> headers;
+        if (!content_type.empty())
+        {
+            headers[U("Content-Type")] = content_type;
+        }
+        p_server->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, method, U("/"));
+            VERIFY_ARE_EQUAL(0u, p_request->reply(code, U(""), headers, data));
+        });
+        http_response rsp = p_client->request(method).get();
+        http_asserts::assert_response_equals(rsp, code, headers);
+        return rsp;
+    }
+
+    utf16string switch_endian_ness(const utf16string& src_str)
+    {
+        utf16string dest_str;
+        dest_str.resize(src_str.size());
+        unsigned char* src = (unsigned char*)&src_str[0];
+        unsigned char* dest = (unsigned char*)&dest_str[0];
+        for (size_t i = 0; i < dest_str.size() * 2; i += 2)
+        {
+            dest[i] = src[i + 1];
+            dest[i + 1] = src[i];
+        }
+        return dest_str;
+    }
+
+    TEST_FIXTURE(uri_address, extract_string)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        // default encoding (Latin1)
+        std::string data("YOU KNOW ITITITITI");
+        http_response rsp = send_request_response(scoped.server(), &client, U("text/plain"), data);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string().get());
+
+        // us-ascii
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;  charset=  us-AscIi"), data);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string().get());
+
+        // Latin1
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;charset=iso-8859-1"), data);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string().get());
+
+        // utf-8
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset  =  UTF-8"), data);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string().get());
+
+        // "utf-8" - quoted charset
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;charset=\"utf-8\""), data);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string().get());
+
+        // no content length
+        rsp = send_request_response(scoped.server(), &client, U(""), utility::string_t());
+        auto str = rsp.to_string();
+        // If there is no Content-Type in the response, make sure it won't throw when we ask for string
+        if (str.find(U("Content-Type")) == std::string::npos)
+        {
+            VERIFY_ARE_EQUAL(utility::string_t(U("")), rsp.extract_string().get());
+        }
+
+        // utf-16le
+        data = "YES NOW, HERHEHE****";
+        utf16string wdata(utf8_to_utf16(data));
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16le"), wdata);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string().get());
+
+        // utf-16be
+        wdata = switch_endian_ness(wdata);
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16be"), wdata);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string().get());
+
+        // utf-16 no BOM (utf-16be)
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16"), wdata);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string().get());
+
+        // utf-16 big endian BOM.
+        wdata.insert(wdata.begin(), ('\0'));
+        unsigned char* start = (unsigned char*)&wdata[0];
+        start[0] = 0xFE;
+        start[1] = 0xFF;
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16"), wdata);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string().get());
+
+        // utf-16 little endian BOM.
+        wdata = utf8_to_utf16("YOU KNOW THIS **********KICKS");
+        data = utf16_to_utf8(wdata);
+        wdata.insert(wdata.begin(), '\0');
+        start = (unsigned char*)&wdata[0];
+        start[0] = 0xFF;
+        start[1] = 0xFE;
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16"), wdata);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string().get());
+    }
+
+    TEST_FIXTURE(uri_address, extract_utf8string)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        // default encoding (Latin1)
+        std::string data("YOU KNOW ITITITITI");
+        http_response rsp = send_request_response(scoped.server(), &client, U("text/plain"), data);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string().get());
+
+        // us-ascii
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;  charset=  us-AscIi"), data);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string().get());
+
+        // Latin1
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;charset=iso-8859-1"), data);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string().get());
+
+        // utf-8
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset  =  UTF-8"), data);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string().get());
+
+        // "utf-8" - quoted charset
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;charset=\"utf-8\""), data);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string().get());
+
+        // no content length
+        rsp = send_request_response(scoped.server(), &client, U(""), utility::string_t());
+        auto str = rsp.to_string();
+        // If there is no Content-Type in the response, make sure it won't throw when we ask for string
+        if (str.find(U("Content-Type")) == std::string::npos)
+        {
+            VERIFY_ARE_EQUAL("", rsp.extract_utf8string().get());
+        }
+
+        // utf-16le
+        data = "YES NOW, HERHEHE****";
+        utf16string wdata(utf8_to_utf16(data));
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16le"), wdata);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string().get());
+
+        // utf-16be
+        wdata = switch_endian_ness(wdata);
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16be"), wdata);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string().get());
+
+        // utf-16 no BOM (utf-16be)
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16"), wdata);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string().get());
+
+        // utf-16 big endian BOM.
+        wdata.insert(wdata.begin(), ('\0'));
+        unsigned char* start = (unsigned char*)&wdata[0];
+        start[0] = 0xFE;
+        start[1] = 0xFF;
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16"), wdata);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string().get());
+
+        // utf-16 little endian BOM.
+        wdata = utf8_to_utf16("YOU KNOW THIS **********KICKS");
+        data = utf16_to_utf8(wdata);
+        wdata.insert(wdata.begin(), '\0');
+        start = (unsigned char*)&wdata[0];
+        start[0] = 0xFF;
+        start[1] = 0xFE;
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16"), wdata);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string().get());
+    }
+
+    TEST_FIXTURE(uri_address, extract_utf16string)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        // default encoding (Latin1)
+        std::string data("YOU KNOW ITITITITI");
+        utf16string wdata(utf8_to_utf16(data));
+        http_response rsp = send_request_response(scoped.server(), &client, U("text/plain"), data);
+        VERIFY_ARE_EQUAL(wdata, rsp.extract_utf16string().get());
+
+        // us-ascii
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;  charset=  us-AscIi"), data);
+        VERIFY_ARE_EQUAL(wdata, rsp.extract_utf16string().get());
+
+        // Latin1
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;charset=iso-8859-1"), data);
+        VERIFY_ARE_EQUAL(wdata, rsp.extract_utf16string().get());
+
+        // utf-8
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset  =  UTF-8"), data);
+        VERIFY_ARE_EQUAL(wdata, rsp.extract_utf16string().get());
+
+        // "utf-8" - quoted charset
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;charset=\"utf-8\""), data);
+        VERIFY_ARE_EQUAL(wdata, rsp.extract_utf16string().get());
+
+        // no content length
+        rsp = send_request_response(scoped.server(), &client, U(""), utility::string_t());
+        auto str = rsp.to_string();
+        // If there is no Content-Type in the response, make sure it won't throw when we ask for string
+        if (str.find(U("Content-Type")) == std::string::npos)
+        {
+            VERIFY_ARE_EQUAL(utf16string(), rsp.extract_utf16string().get());
+        }
+
+        // utf-16le
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16le"), wdata);
+        VERIFY_ARE_EQUAL(wdata, rsp.extract_utf16string().get());
+
+        // utf-16be
+        auto wdatabe = switch_endian_ness(wdata);
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16be"), wdatabe);
+        VERIFY_ARE_EQUAL(wdata, rsp.extract_utf16string().get());
+
+        // utf-16 no BOM (utf-16be)
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16"), wdatabe);
+        VERIFY_ARE_EQUAL(wdata, rsp.extract_utf16string().get());
+
+        // utf-16 big endian BOM.
+        wdatabe.insert(wdatabe.begin(), ('\0'));
+        unsigned char* start = (unsigned char*)&wdatabe[0];
+        start[0] = 0xFE;
+        start[1] = 0xFF;
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16"), wdatabe);
+        VERIFY_ARE_EQUAL(wdata, rsp.extract_utf16string().get());
+
+        // utf-16 little endian BOM.
+        auto wdatale = wdata;
+        wdatale.insert(wdatale.begin(), '\0');
+        start = (unsigned char*)&wdatale[0];
+        start[0] = 0xFF;
+        start[1] = 0xFE;
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=utf-16"), wdatale);
+        VERIFY_ARE_EQUAL(wdata, rsp.extract_utf16string().get());
+    }
+
+    TEST_FIXTURE(uri_address, extract_string_force)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        std::string data("YOU KNOW ITITITITI");
+        http_response rsp = send_request_response(scoped.server(), &client, U("bad unknown charset"), data);
+        VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string(true).get());
+        rsp = send_request_response(scoped.server(), &client, U("bad unknown charset"), data);
+        VERIFY_ARE_EQUAL(data, rsp.extract_utf8string(true).get());
+        rsp = send_request_response(scoped.server(), &client, U("bad unknown charset"), data);
+        VERIFY_ARE_EQUAL(to_utf16string(data), rsp.extract_utf16string(true).get());
+    }
+
+    TEST_FIXTURE(uri_address, extract_string_incorrect)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        // with non matching content type.
+        const std::string data("YOU KNOW ITITITITI");
+        http_response rsp = send_request_response(scoped.server(), &client, U("non_text"), data);
+        VERIFY_THROWS(rsp.extract_string().get(), http_exception);
+
+        // with unknown charset
+        rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=uis-ascii"), data);
+        VERIFY_THROWS(rsp.extract_string().get(), http_exception);
+    }
+
+#ifndef __cplusplus_winrt
+    TEST_FIXTURE(uri_address, extract_empty_string)
+    {
+        web::http::experimental::listener::http_listener listener(m_uri);
+        http_client client(m_uri);
+        listener.support([](http_request msg) {
+            auto ResponseStreamBuf = streams::producer_consumer_buffer<uint8_t>();
+            ResponseStreamBuf.close(std::ios_base::out).wait();
+            http_response response(status_codes::OK);
+            response.set_body(ResponseStreamBuf.create_istream(), U("text/plain"));
+            response.headers().add(header_names::connection, U("close"));
+            msg.reply(response).wait();
+        });
+
+        listener.open().wait();
+
+        auto response = client.request(methods::GET).get();
+        auto data = response.extract_string().get();
+
+        VERIFY_ARE_EQUAL(0, data.size());
+        listener.close().wait();
+    }
+#endif
+
+    TEST_FIXTURE(uri_address, extract_json)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        // default encoding (Latin1)
+        json::value data = json::value::string(U("JSON string object"));
+        http_response rsp =
+            send_request_response(scoped.server(), &client, U("application/json"), to_utf8string(data.serialize()));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+
+        // us-ascii
+        rsp = send_request_response(
+            scoped.server(), &client, U("application/json;  charset=  us-AscIi"), to_utf8string(data.serialize()));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+
+        // Latin1
+        rsp = send_request_response(
+            scoped.server(), &client, U("application/json;charset=iso-8859-1"), to_utf8string(data.serialize()));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+
+        // utf-8
+        rsp = send_request_response(
+            scoped.server(), &client, U("application/json; charset  =  UTF-8"), to_utf8string((data.serialize())));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+
+        rsp = send_request_response(scoped.server(), &client, U(""), utility::string_t());
+        auto str = rsp.to_string();
+        // If there is no Content-Type in the response, make sure it won't throw when we ask for json
+        if (str.find(U("Content-Type")) == std::string::npos)
+        {
+            VERIFY_ARE_EQUAL(utility::string_t(U("null")), rsp.extract_json().get().serialize());
+        }
+
+#ifdef _WIN32
+        // utf-16le
+        auto utf16str = data.serialize();
+        rsp = send_request_response(scoped.server(), &client, U("application/json; charset=utf-16le"), utf16str);
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+
+        // utf-16be
+        utf16string modified_data = data.serialize();
+        modified_data = switch_endian_ness(modified_data);
+        rsp = send_request_response(scoped.server(), &client, U("application/json; charset=utf-16be"), modified_data);
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+
+        // utf-16 no BOM (utf-16be)
+        rsp = send_request_response(scoped.server(), &client, U("application/json; charset=utf-16"), modified_data);
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+
+        // utf-16 big endian BOM.
+        modified_data.insert(modified_data.begin(), U('\0'));
+        unsigned char* start = (unsigned char*)&modified_data[0];
+        start[0] = 0xFE;
+        start[1] = 0xFF;
+        rsp = send_request_response(scoped.server(), &client, U("application/json; charset=utf-16"), modified_data);
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+
+        // utf-16 little endian BOM.
+        modified_data = data.serialize();
+        modified_data.insert(modified_data.begin(), U('\0'));
+        start = (unsigned char*)&modified_data[0];
+        start[0] = 0xFF;
+        start[1] = 0xFE;
+        rsp = send_request_response(scoped.server(), &client, U("application/json; charset=utf-16"), modified_data);
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+#endif
+
+        // unofficial JSON MIME types
+        rsp = send_request_response(scoped.server(), &client, U("text/json"), to_utf8string(data.serialize()));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+        rsp = send_request_response(scoped.server(), &client, U("text/x-json"), to_utf8string(data.serialize()));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+        rsp = send_request_response(scoped.server(), &client, U("text/javascript"), to_utf8string(data.serialize()));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+        rsp = send_request_response(scoped.server(), &client, U("text/x-javascript"), to_utf8string(data.serialize()));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+        rsp = send_request_response(
+            scoped.server(), &client, U("application/javascript"), to_utf8string(data.serialize()));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+        rsp = send_request_response(
+            scoped.server(), &client, U("application/x-javascript"), to_utf8string(data.serialize()));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
+    }
+
+    TEST_FIXTURE(uri_address, extract_json_force)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        json::value data = json::value::string(U("JSON string object"));
+        http_response rsp =
+            send_request_response(scoped.server(), &client, U("bad charset"), to_utf8string(data.serialize()));
+        VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json(true).get().serialize());
+    }
+
+    TEST_FIXTURE(uri_address, extract_json_incorrect)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        // with non matching content type.
+        json::value json_data = json::value::string(U("JSON string object"));
+        http_response rsp = send_request_response(scoped.server(), &client, U("bad guy"), json_data.serialize());
+        VERIFY_THROWS(rsp.extract_json().get(), http_exception);
+
+        // with unknown charset.
+        rsp = send_request_response(
+            scoped.server(), &client, U("application/json; charset=us-askjhcii"), json_data.serialize());
+        VERIFY_THROWS(rsp.extract_json().get(), http_exception);
+    }
+
+    TEST_FIXTURE(uri_address, set_stream_try_extract_json)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        http_request request(methods::GET);
+        streams::ostream responseStream = streams::bytestream::open_ostream<std::vector<uint8_t>>();
+        request.set_response_stream(responseStream);
+        scoped.server()->next_request().then([](test_request* req) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[header_names::content_type] = U("application/json");
+            req->reply(status_codes::OK, U("OK"), headers, U("{true}"));
+        });
+
+        http_response response = client.request(request).get();
+        VERIFY_THROWS(response.extract_json().get(), http_exception);
+    }
+
+    TEST_FIXTURE(uri_address, extract_vector)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        // textual content type - with unknown charset
+        std::string data("YOU KNOW ITITITITI");
+        std::vector<unsigned char> vector_data;
+        std::for_each(data.begin(), data.end(), [&](char ch) { vector_data.push_back((unsigned char)ch); });
+        http_response rsp = send_request_response(scoped.server(), &client, U("text/plain; charset=unknown"), data);
+        VERIFY_ARE_EQUAL(vector_data, rsp.extract_vector().get());
+
+        // textual type with us-ascii
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;  charset=  us-AscIi"), data);
+        VERIFY_ARE_EQUAL(vector_data, rsp.extract_vector().get());
+
+        // textual type with Latin1
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;  charset=iso-8859-1"), data);
+        VERIFY_ARE_EQUAL(vector_data, rsp.extract_vector().get());
+
+        // textual type with utf-8
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;  charset=utf-8"), data);
+        VERIFY_ARE_EQUAL(vector_data, rsp.extract_vector().get());
+
+        // textual type with utf-16le
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;  charset=utf-16LE"), data);
+        VERIFY_ARE_EQUAL(vector_data, rsp.extract_vector().get());
+
+        // textual type with utf-16be
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;  charset=UTF-16be"), data);
+        VERIFY_ARE_EQUAL(vector_data, rsp.extract_vector().get());
+
+        // textual type with utf-16
+        rsp = send_request_response(scoped.server(), &client, U("text/plain;  charset=utf-16"), data);
+        VERIFY_ARE_EQUAL(vector_data, rsp.extract_vector().get());
+
+        // non textual content type
+        rsp = send_request_response(scoped.server(), &client, U("blah;  charset=utf-16"), data);
+        VERIFY_ARE_EQUAL(vector_data, rsp.extract_vector().get());
+    }
+
+    TEST_FIXTURE(uri_address, set_stream_try_extract_vector)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        http_request request(methods::GET);
+        streams::ostream responseStream = streams::bytestream::open_ostream<std::vector<uint8_t>>();
+        request.set_response_stream(responseStream);
+        scoped.server()->next_request().then([](test_request* req) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[header_names::content_type] = U("text/plain");
+            req->reply(status_codes::OK, U("OK"), headers, U("data"));
+        });
+
+        http_response response = client.request(request).get();
+        VERIFY_THROWS(response.extract_vector().get(), http_exception);
+    }
+
+    TEST_FIXTURE(uri_address, head_response)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        const method method = methods::HEAD;
+        const ::http::status_code code = status_codes::OK;
+        std::map<utility::string_t, utility::string_t> headers;
+        headers[U("Content-Type")] = U("text/plain");
+        headers[U("Content-Length")] = U("100");
+        scoped.server()->next_request().then([&](test_request* p_request) {
+            http_asserts::assert_test_request_equals(p_request, method, U("/"));
+            VERIFY_ARE_EQUAL(0u, p_request->reply(code, U(""), headers));
+        });
+        http_response rsp = client.request(method).get();
+        VERIFY_ARE_EQUAL(0u, rsp.body().streambuf().in_avail());
+    }
+
+} // SUITE(response_extract_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/response_stream_tests.cpp b/Release/tests/functional/http/client/response_stream_tests.cpp
new file mode 100644 (file)
index 0000000..9806d79
--- /dev/null
@@ -0,0 +1,500 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * response_stream_tests.cpp
+ *
+ * Tests cases for covering receiving various responses as a stream with http_client.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if defined(__cplusplus_winrt)
+using namespace Windows::Storage;
+#endif
+
+#ifndef __cplusplus_winrt
+#include "cpprest/http_listener.h"
+#endif
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+template<typename _CharType>
+pplx::task<streams::streambuf<_CharType>> OPENSTR_R(const utility::string_t& name)
+{
+#if !defined(__cplusplus_winrt)
+    return streams::file_buffer<_CharType>::open(name, std::ios_base::in);
+#else
+    auto file =
+        pplx::create_task(KnownFolders::DocumentsLibrary->GetFileAsync(ref new Platform::String(name.c_str()))).get();
+
+    return streams::file_buffer<_CharType>::open(file, std::ios_base::in);
+#endif
+}
+
+template<typename _CharType>
+pplx::task<Concurrency::streams::basic_ostream<_CharType>> OPENSTR_W(const utility::string_t& name,
+                                                                     std::ios_base::openmode mode = std::ios_base::out)
+{
+#if !defined(__cplusplus_winrt)
+    return Concurrency::streams::file_stream<_CharType>::open_ostream(name, mode);
+#else
+    auto file = pplx::create_task(KnownFolders::DocumentsLibrary->CreateFileAsync(
+                                      ref new Platform::String(name.c_str()), CreationCollisionOption::ReplaceExisting))
+                    .get();
+
+    return Concurrency::streams::file_stream<_CharType>::open_ostream(file, mode);
+#endif
+}
+SUITE(response_stream_tests)
+{
+    TEST_FIXTURE(uri_address, set_response_stream_producer_consumer_buffer)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        p_server->next_request().then([&](test_request* p_request) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("Content-Type")] = U("text/plain");
+            p_request->reply(200, U(""), headers, "This is just a bit of a string");
+        });
+
+        streams::producer_consumer_buffer<uint8_t> rwbuf;
+        auto ostr = streams::ostream(rwbuf);
+
+        http_request msg(methods::GET);
+        msg.set_response_stream(ostr);
+        http_response rsp = client.request(msg).get();
+
+        rsp.content_ready().get();
+        VERIFY_ARE_EQUAL(rwbuf.in_avail(), 30u);
+
+        VERIFY_THROWS(rsp.extract_string().get(), http_exception);
+
+        char chars[128];
+        memset(chars, 0, sizeof(chars));
+
+        rwbuf.getn((unsigned char*)chars, rwbuf.in_avail()).get();
+        VERIFY_ARE_EQUAL(0, strcmp("This is just a bit of a string", chars));
+    }
+
+    TEST_FIXTURE(uri_address, set_response_stream_container_buffer)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        p_server->next_request().then([&](test_request* p_request) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("Content-Type")] = U("text/plain");
+            p_request->reply(200, U(""), headers, "This is just a bit of a string");
+        });
+
+        {
+            streams::container_buffer<std::vector<uint8_t>> buf;
+
+            http_request msg(methods::GET);
+            msg.set_response_stream(buf.create_ostream());
+            http_response rsp = client.request(msg).get();
+
+            rsp.content_ready().get();
+            VERIFY_ARE_EQUAL(buf.collection().size(), 30);
+
+            char bufStr[31];
+            memset(bufStr, 0, sizeof(bufStr));
+            memcpy(&bufStr[0], &(buf.collection())[0], 30);
+            VERIFY_ARE_EQUAL(bufStr, "This is just a bit of a string");
+
+            VERIFY_THROWS(rsp.extract_string().get(), http_exception);
+        }
+    }
+
+    TEST_FIXTURE(uri_address, response_stream_file_stream)
+    {
+        std::string message = "A world without string is chaos.";
+
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+        http_client client(m_uri);
+
+        p_server->next_request().then([&](test_request* p_request) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("Content-Type")] = U("text/plain");
+            p_request->reply(200, U(""), headers, message);
+        });
+
+        {
+            auto fstream = OPENSTR_W<uint8_t>(U("response_stream.txt")).get();
+
+            // Write the response into the file
+            http_request msg(methods::GET);
+            msg.set_response_stream(fstream);
+            http_response rsp = client.request(msg).get();
+
+            rsp.content_ready().get();
+            VERIFY_IS_TRUE(fstream.streambuf().is_open());
+            fstream.close().get();
+
+            char chars[128];
+            memset(chars, 0, sizeof(chars));
+
+            streams::rawptr_buffer<uint8_t> buffer(reinterpret_cast<uint8_t*>(chars), sizeof(chars));
+
+            streams::basic_istream<uint8_t> fistream = OPENSTR_R<uint8_t>(U("response_stream.txt")).get();
+            VERIFY_ARE_EQUAL(message.length(), fistream.read_line(buffer).get());
+            VERIFY_ARE_EQUAL(message, std::string(chars));
+            fistream.close().get();
+        }
+    }
+
+    TEST_FIXTURE(uri_address, response_stream_file_stream_close_early)
+    {
+        // The test needs to be a little different between desktop and WinRT.
+        // In the latter case, the server will not see a message, and so the
+        // test will hang. In order to prevent that from happening, we will
+        // not have a server listening on WinRT.
+#if !defined(__cplusplus_winrt)
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+
+        p_server->next_request().then([&](test_request* p_request) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("Content-Type")] = U("text/plain");
+            p_request->reply(200, U(""), headers, "A world without string is chaos.");
+        });
+#endif
+
+        auto fstream = OPENSTR_W<uint8_t>(U("response_stream_file_stream_close_early.txt")).get();
+
+        http_client client(m_uri);
+
+        http_request msg(methods::GET);
+        msg.set_response_stream(fstream);
+        fstream.close(std::make_exception_ptr(std::exception())).wait();
+
+        http_response resp;
+
+        VERIFY_THROWS((resp = client.request(msg).get(), resp.content_ready().get()), std::exception);
+    }
+
+    TEST_FIXTURE(uri_address, response_stream_large_file_stream)
+    {
+        // Send a 100 KB data in the response body, the server will send this in multiple chunks
+        // This data will get sent with content-length
+        const size_t workload_size = 100 * 1024;
+        utility::string_t fname(U("response_stream_large_file_stream.txt"));
+        std::string responseData;
+        responseData.resize(workload_size, 'a');
+
+        test_http_server::scoped_server scoped(m_uri);
+        test_http_server* p_server = scoped.server();
+
+        http_client client(m_uri);
+
+        p_server->next_request().then([&](test_request* p_request) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("Content-Type")] = U("text/plain");
+
+            p_request->reply(200, U(""), headers, responseData);
+        });
+
+        {
+            auto fstream = OPENSTR_W<uint8_t>(fname).get();
+
+            http_request msg(methods::GET);
+            msg.set_response_stream(fstream);
+            http_response rsp = client.request(msg).get();
+
+            rsp.content_ready().get();
+            VERIFY_IS_TRUE(fstream.streambuf().is_open());
+            fstream.close().get();
+
+            std::string rsp_string;
+            rsp_string.resize(workload_size, 0);
+            streams::rawptr_buffer<char> buffer(&rsp_string[0], rsp_string.size());
+            streams::basic_istream<char> fistream = OPENSTR_R<char>(fname).get();
+
+            VERIFY_ARE_EQUAL(fistream.read_to_end(buffer).get(), workload_size);
+            VERIFY_ARE_EQUAL(rsp_string, responseData);
+            fistream.close().get();
+        }
+    }
+
+#if !defined(__cplusplus_winrt)
+
+    template<typename CharType>
+    class basic_throws_buffer : public streams::details::streambuf_state_manager<CharType>
+    {
+    public:
+        basic_throws_buffer() : streams::details::streambuf_state_manager<CharType>(std::ios_base::out) {}
+
+        typedef typename streams::details::basic_streambuf<CharType>::int_type int_type;
+        typedef typename streams::details::basic_streambuf<CharType>::pos_type pos_type;
+        typedef typename streams::details::basic_streambuf<CharType>::off_type off_type;
+
+        bool can_seek() const override { return true; }
+        bool has_size() const override { return false; }
+        size_t buffer_size(std::ios_base::openmode) const override { return 0; }
+        void set_buffer_size(size_t, std::ios_base::openmode) override {}
+        size_t in_avail() const override { return 0; }
+        pos_type getpos(std::ios_base::openmode) const override { return 0; }
+        pos_type seekpos(pos_type, std::ios_base::openmode) override { return 0; }
+        pos_type seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode) override { return 0; }
+        bool acquire(_Out_writes_(count) CharType*&, _In_ size_t&) override { return false; }
+        void release(_Out_writes_(count) CharType*, _In_ size_t) override {}
+
+    protected:
+        pplx::task<int_type> _putc(CharType) override { throw std::runtime_error("error"); }
+        pplx::task<size_t> _putn(const CharType*, size_t) override { throw std::runtime_error("error"); }
+        pplx::task<int_type> _bumpc() override { throw std::runtime_error("error"); }
+        int_type _sbumpc() override { throw std::runtime_error("error"); }
+        pplx::task<int_type> _getc() override { throw std::runtime_error("error"); }
+        int_type _sgetc() override { throw std::runtime_error("error"); }
+        pplx::task<int_type> _nextc() override { throw std::runtime_error("error"); }
+        pplx::task<int_type> _ungetc() override { throw std::runtime_error("error"); }
+        pplx::task<size_t> _getn(_Out_writes_(count) CharType*, _In_ size_t) override
+        {
+            throw std::runtime_error("error");
+        }
+        size_t _scopy(_Out_writes_(count) CharType*, _In_ size_t) override { throw std::runtime_error("error"); }
+        pplx::task<bool> _sync() override { throw std::runtime_error("error"); }
+        CharType* _alloc(size_t) override { throw std::runtime_error("error"); }
+        void _commit(size_t) override { throw std::runtime_error("error"); }
+
+        pplx::task<void> _close_write() override
+        {
+            return pplx::task_from_exception<void>(std::invalid_argument("test"));
+        }
+    };
+
+    template<typename CharType>
+    class close_throws_buffer : public streams::streambuf<CharType>
+    {
+    public:
+        close_throws_buffer()
+            : streams::streambuf<CharType>(
+                  std::shared_ptr<basic_throws_buffer<CharType>>(new basic_throws_buffer<CharType>()))
+        {
+        }
+    };
+
+    // Tests if an exception occurs and close throws an exception that the close
+    // one is ignored and doesn't bring down the process.
+    TEST_FIXTURE(uri_address, response_stream_close_throws_with_exception)
+    {
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+
+        streams::producer_consumer_buffer<uint8_t> buf;
+
+        listener.support([buf](http_request request) {
+            http_response response(200);
+            response.set_body(streams::istream(buf), U("text/plain"));
+            response.headers().add(header_names::connection, U("close"));
+            request.reply(response);
+        });
+
+        http_client_config config;
+        config.set_timeout(utility::seconds(1));
+        http_client client(m_uri, config);
+
+        close_throws_buffer<uint8_t> responseBody;
+        http_request msg(methods::GET);
+        msg.set_response_stream(responseBody.create_ostream());
+        http_response rsp = client.request(msg).get();
+        VERIFY_THROWS(rsp.content_ready().get(), http_exception);
+
+        buf.close(std::ios_base::out).wait();
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, content_ready)
+    {
+        http_client client(m_uri);
+        std::string responseData("Hello world");
+
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+        listener.support([responseData](http_request request) {
+            streams::producer_consumer_buffer<uint8_t> buf;
+            http_response response(200);
+            response.set_body(buf.create_istream(), U("text/plain"));
+            response.headers().add(header_names::connection, U("close"));
+
+            request.reply(response);
+
+            VERIFY_ARE_EQUAL(buf.putn_nocopy((const uint8_t*)responseData.data(), responseData.size()).get(),
+                             responseData.size());
+            buf.close(std::ios_base::out).get();
+        });
+
+        {
+            http_request msg(methods::GET);
+            http_response rsp = client.request(msg).get().content_ready().get();
+
+            auto extract_string_task = rsp.extract_string();
+            VERIFY_ARE_EQUAL(extract_string_task.get(), ::utility::conversions::to_string_t(responseData));
+            rsp.content_ready().wait();
+        }
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, xfer_chunked_with_length)
+    {
+        http_client client(m_uri);
+        utility::string_t responseData(U("Hello world"));
+
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+        listener.support([responseData](http_request request) {
+            http_response response(200);
+
+            // This sets the content_length
+            response.set_body(responseData);
+
+            // overwrite content_length to 0
+            response.headers().add(header_names::content_length, 0);
+
+            // add chunked transfer encoding
+            response.headers().add(header_names::transfer_encoding, U("chunked"));
+
+            // add connection=close header, connection SHOULD NOT be considered persistent' after the current
+            // request/response is complete
+            response.headers().add(header_names::connection, U("close"));
+
+            // respond
+            request.reply(response);
+        });
+
+        {
+            http_request msg(methods::GET);
+            http_response rsp = client.request(msg).get();
+
+            auto rsp_string = rsp.extract_string().get();
+            VERIFY_ARE_EQUAL(rsp_string, responseData);
+        }
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, get_resp_stream)
+    {
+        http_client client(m_uri);
+        std::string responseData("Hello world");
+
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+        listener.support([responseData](http_request request) {
+            streams::producer_consumer_buffer<uint8_t> buf;
+
+            http_response response(200);
+            response.set_body(buf.create_istream(), U("text/plain"));
+            response.headers().add(header_names::connection, U("close"));
+            request.reply(response);
+
+            VERIFY_ARE_EQUAL(buf.putn_nocopy((const uint8_t*)responseData.data(), responseData.size()).get(),
+                             responseData.size());
+            buf.close(std::ios_base::out).get();
+        });
+
+        {
+            http_request msg(methods::GET);
+            http_response rsp = client.request(msg).get();
+
+            streams::stringstreambuf data;
+
+            auto t = rsp.body().read_to_delim(data, (uint8_t)(' '));
+
+            t.then([&data](size_t size) {
+                 VERIFY_ARE_EQUAL(size, 5);
+                 auto s = data.collection();
+                 VERIFY_ARE_EQUAL(s, std::string("Hello"));
+             })
+                .wait();
+            rsp.content_ready().wait();
+        }
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, xfer_chunked_multiple_chunks)
+    {
+        // With chunked transfer-encoding, send 2 chunks of different sizes in the response
+        http_client client(m_uri);
+
+        // Send two chunks, note: second chunk is bigger than the first.
+        std::string firstChunk("abcdefghijklmnopqrst");
+        std::string secondChunk("abcdefghijklmnopqrstuvwxyz");
+
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+        listener.support([firstChunk, secondChunk](http_request request) {
+            streams::producer_consumer_buffer<uint8_t> buf;
+
+            http_response response(200);
+            response.set_body(buf.create_istream(), U("text/plain"));
+            response.headers().add(header_names::connection, U("close"));
+            request.reply(response);
+
+            VERIFY_ARE_EQUAL(buf.putn_nocopy((const uint8_t*)firstChunk.data(), firstChunk.size()).get(),
+                             firstChunk.size());
+            buf.sync().get();
+            VERIFY_ARE_EQUAL(buf.putn_nocopy((const uint8_t*)secondChunk.data(), secondChunk.size()).get(),
+                             secondChunk.size());
+            buf.close(std::ios_base::out).get();
+        });
+
+        {
+            utility::string_t fname(U("xfer_chunked_multiple_chunks.txt"));
+            auto fstream = OPENSTR_W<uint8_t>(fname).get();
+
+            http_request msg(methods::GET);
+            msg.set_response_stream(fstream);
+            http_response rsp = client.request(msg).get();
+
+            rsp.content_ready().wait();
+            VERIFY_IS_TRUE(fstream.streambuf().is_open());
+            fstream.close().get();
+
+            std::string rsp_string;
+            size_t workload_size = firstChunk.size() + secondChunk.size();
+            rsp_string.resize(workload_size, 0);
+            streams::rawptr_buffer<uint8_t> buffer(reinterpret_cast<uint8_t*>(&rsp_string[0]), rsp_string.size());
+            streams::basic_istream<uint8_t> fistream = OPENSTR_R<uint8_t>(fname).get();
+
+            VERIFY_ARE_EQUAL(fistream.read_to_end(buffer).get(), workload_size);
+            VERIFY_ARE_EQUAL(rsp_string, firstChunk + secondChunk);
+            fistream.close().get();
+        }
+
+        listener.close().wait();
+    }
+
+#endif
+
+} // SUITE(responses)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/status_code_reason_phrase_tests.cpp b/Release/tests/functional/http/client/status_code_reason_phrase_tests.cpp
new file mode 100644 (file)
index 0000000..a059db5
--- /dev/null
@@ -0,0 +1,54 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * status_code_reason_phrase_tests.cpp
+ *
+ * Tests cases for covering HTTP status codes and reason phrases.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(status_code_reason_phrase_tests)
+{
+    TEST_FIXTURE(uri_address, status_code)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        // custom status code.
+        test_server_utilities::verify_request(&client, methods::GET, U("/"), scoped.server(), 666);
+    }
+
+    TEST_FIXTURE(uri_address, reason_phrase)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+
+        test_server_utilities::verify_request(
+            &client, methods::GET, U("/"), scoped.server(), status_codes::OK, U("Reasons!!"));
+    }
+
+} // SUITE(status_code_reason_phrase_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/stdafx.cpp b/Release/tests/functional/http/client/stdafx.cpp
new file mode 100644 (file)
index 0000000..a518dbd
--- /dev/null
@@ -0,0 +1,14 @@
+/***
+ * 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 httpclient_test_generate_lib = 0;
+#endif
diff --git a/Release/tests/functional/http/client/stdafx.h b/Release/tests/functional/http/client/stdafx.h
new file mode 100644 (file)
index 0000000..8a7ad8b
--- /dev/null
@@ -0,0 +1,24 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Pre-compiled headers
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/containerstream.h"
+#include "cpprest/filestream.h"
+#include "cpprest/http_client.h"
+#include "cpprest/producerconsumerstream.h"
+#include "cpprest/rawptrstream.h"
+#include "http_client_tests.h"
+#include "http_test_utilities.h"
+#include "os_utilities.h"
+#include "timeout_handler.h"
+#include "unittestpp.h"
diff --git a/Release/tests/functional/http/client/timeout_handler.h b/Release/tests/functional/http/client/timeout_handler.h
new file mode 100644 (file)
index 0000000..4b22d21
--- /dev/null
@@ -0,0 +1,57 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Simple utility for handling timeouts with http client test cases.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/http_client.h"
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+// helper function to check if failure is due to timeout.
+inline bool is_timeout(const std::string& msg)
+{
+    if (msg.find("The operation timed out") != std::string::npos /* WinHTTP */ ||
+        msg.find("The operation was timed out") != std::string::npos /* IXmlHttpRequest2 */)
+    {
+        return true;
+    }
+    return false;
+}
+
+template<typename Func>
+void handle_timeout(const Func& f)
+{
+    try
+    {
+        f();
+    }
+    catch (const web::http::http_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;
+        }
+        throw;
+    }
+}
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/client/to_string_tests.cpp b/Release/tests/functional/http/client/to_string_tests.cpp
new file mode 100644 (file)
index 0000000..8ae964f
--- /dev/null
@@ -0,0 +1,133 @@
+/***
+ * 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 to_string APIs on HTTP request and responses.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+using namespace web::http;
+using namespace web::http::client;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace client
+{
+SUITE(to_string_tests)
+{
+    TEST_FIXTURE(uri_address, request_to_string_without_body)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        const method mtd = methods::GET;
+        const utility::string_t path = U("/pathbaby/");
+        const utility::string_t content_type = U("text/plain; charset= utf-8");
+
+        // to_string
+        http_request msg(mtd);
+        msg.set_request_uri(path);
+        msg.headers()[U("Content-Type")] = content_type;
+
+        std::map<utility::string_t, utility::string_t> expected_headers;
+        expected_headers[U("Content-Type")] = content_type;
+        http_asserts::assert_request_string_equals(msg.to_string(), mtd, path, U("HTTP/1.1"), expected_headers, U(""));
+    }
+
+    TEST_FIXTURE(uri_address, request_to_string_with_body)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        const method mtd = methods::POST;
+        const utility::string_t path = U("/path baby/");
+        const utility::string_t content_type = U("text/plain;charset=utf-8");
+        const utility::string_t body = U("YES THIS IS THE MSG BODY!!!!!");
+
+        // to_string
+        http_request msg(mtd);
+        msg.set_request_uri(uri::encode_uri(path, uri::components::path));
+        msg.headers()[U("Content-Type")] = content_type;
+        msg.set_body(body);
+
+        std::map<utility::string_t, utility::string_t> expected_headers;
+        expected_headers[U("Content-Type")] = content_type;
+        expected_headers[U("Content-Length")] = U("29");
+        http_asserts::assert_request_string_equals(
+            msg.to_string(), mtd, U("/path%20baby/"), U("HTTP/1.1"), expected_headers, body);
+    }
+
+    TEST_FIXTURE(uri_address, response_to_string_without_body)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        const web::http::status_code code = status_codes::OK;
+        const utility::string_t reason = U("OK YEAH!");
+        const utility::string_t content_type = U("not; charset= utf-8");
+
+        // to_string
+        scoped.server()->next_request().then([&](test_request* request) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("Content-Type")] = content_type;
+            request->reply(code, reason, headers);
+        });
+        http_response rsp = client.request(methods::GET).get();
+
+        std::map<utility::string_t, utility::string_t> expected_headers;
+        expected_headers[U("Content-Length")] = U("0");
+        expected_headers[U("Content-Type")] = content_type;
+        http_asserts::assert_response_string_equals(
+            rsp.to_string(), U("HTTP/1.1"), code, U("OK"), expected_headers, U(""));
+
+#ifdef _WIN32
+        // Don't verify the values of each of these headers, but make sure they exist.
+        if (!rsp.headers().has(U("Date")) || !rsp.headers().has(U("Cache-Control")) || !rsp.headers().has(U("Server")))
+        {
+            CHECK(false);
+        }
+#endif
+    }
+
+    TEST_FIXTURE(uri_address, response_to_string_with_body)
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_client client(m_uri);
+        const ::http::status_code code = status_codes::OK;
+        const utility::string_t reason = U("OK YEAH!");
+        const std::string data = "HERE IS THE RESPONSE body!";
+        const utility::string_t content_type = U("text/yeah;charset=utf-8");
+
+        // to_string
+        scoped.server()->next_request().then([&](test_request* request) {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("Content-Type")] = content_type;
+            request->reply(code, reason, headers, data);
+        });
+
+        http_response rsp = client.request(methods::GET).get();
+        rsp.content_ready().wait();
+
+        std::map<utility::string_t, utility::string_t> expected_headers;
+        expected_headers[U("Content-Length")] = U("26");
+        expected_headers[U("Content-Type")] = content_type;
+        http_asserts::assert_response_string_equals(
+            rsp.to_string(), U("HTTP/1.1"), code, U("OK"), expected_headers, ::utility::conversions::to_string_t(data));
+    }
+
+} // SUITE(to_string_tests)
+
+} // namespace client
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/CMakeLists.txt b/Release/tests/functional/http/listener/CMakeLists.txt
new file mode 100644 (file)
index 0000000..58cf86a
--- /dev/null
@@ -0,0 +1,26 @@
+if(NOT WINDOWS_STORE AND NOT WINDOWS_PHONE)
+  set (SOURCES
+    building_response_tests.cpp
+    connections_and_errors.cpp
+    header_tests.cpp
+    listener_construction_tests.cpp
+    reply_helper_tests.cpp
+    request_extract_tests.cpp
+    request_handler_tests.cpp
+    request_relative_uri_tests.cpp
+    request_stream_tests.cpp
+    requests_tests.cpp
+    response_stream_tests.cpp
+    status_code_reason_phrase_tests.cpp
+    to_string_tests.cpp
+  )
+
+  add_casablanca_test(httplistener_test SOURCES)
+  if(TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT")
+    target_include_directories(httplistener_test PRIVATE ../utilities/include)
+  else()
+    target_link_libraries(httplistener_test PRIVATE httptest_utilities)
+  endif()
+
+  configure_pch(httplistener_test stdafx.h stdafx.cpp)
+endif()
diff --git a/Release/tests/functional/http/listener/building_response_tests.cpp b/Release/tests/functional/http/listener/building_response_tests.cpp
new file mode 100644 (file)
index 0000000..a41e80d
--- /dev/null
@@ -0,0 +1,154 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * building_response_tests.cpp
+ *
+ * Tests cases for manually building up HTTP responses with http_listener.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::common::utilities;
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(building_response_tests)
+{
+    TEST_FIXTURE(uri_address, set_body_with_content_type)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            http_response response(status_codes::OK);
+            response.set_body(U("test string"), U("text"));
+            request.reply(response).wait();
+        });
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+#ifdef _UTF16_STRINGS
+                const ::utility::string_t expectedContentType(U("text; charset=utf-8"));
+#else
+                const ::utility::string_t expectedContentType(U("text"));
+#endif
+                http_asserts::assert_test_response_equals(
+                    p_response, status_codes::OK, expectedContentType, U("test string"));
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, set_body_without_content_type)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support([&](http_request request) {
+            http_response response(status_codes::OK);
+            response.set_body(U("test string"));
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            request.reply(response).wait();
+        });
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(
+                    p_response, status_codes::OK, U("text/plain; charset=utf-8"), U("test string"));
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, set_body_string)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            http_response response(status_codes::OK);
+            utility::string_t data(U("test data"));
+            response.set_body(std::move(data));
+            request.reply(response).wait();
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(
+                    p_response, status_codes::OK, U("text/plain; charset=utf-8"), U("test data"));
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST(set_body_string_with_charset)
+    {
+        http_response response;
+        VERIFY_THROWS(response.set_body(::utility::conversions::to_utf16string("body_data"),
+                                        ::utility::conversions::to_utf16string("text/plain;charset=utf-16")),
+                      std::invalid_argument);
+    }
+
+    TEST_FIXTURE(uri_address, set_body_vector)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            http_response response(status_codes::OK);
+            std::vector<unsigned char> v_body;
+            v_body.push_back('A');
+            v_body.push_back('B');
+            v_body.push_back('C');
+            response.set_body(std::move(v_body));
+            request.reply(response).wait();
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(
+                    p_response, status_codes::OK, U("application/octet-stream"), U("ABC"));
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/connections_and_errors.cpp b/Release/tests/functional/http/listener/connections_and_errors.cpp
new file mode 100644 (file)
index 0000000..b3076e4
--- /dev/null
@@ -0,0 +1,420 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * connections_and_errors.cpp
+ *
+ * Tests cases the underlying connections and error cases with the connection using then http_listener.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <cpprest/http_client.h>
+
+// For single_core test case.
+#if defined(_WIN32) && _MSC_VER < 1900
+#include <concrt.h>
+#endif
+
+using namespace utility;
+using namespace web;
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(connections_and_errors)
+{
+    TEST_FIXTURE(uri_address, close_listener_race, "Ignore", "825350")
+    {
+        ::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+
+        listener.support([](http_request) {
+            // Let the connection timeout
+        });
+
+        // close() racing with a new connection
+        auto closeTask = pplx::create_task([&listener]() { listener.close().wait(); });
+
+        auto clientTask = pplx::create_task([this] {
+            ::http::client::http_client_config config;
+            config.set_timeout(utility::seconds(1));
+            ::http::client::http_client client(m_uri, config);
+
+            try
+            {
+                // Depending on timing this might not succeed. The
+                // exception will be caught and ignored below
+                auto rsp = client.request(methods::GET).get();
+
+                // The response body should timeout and we should recieve an exception
+                rsp.content_ready().wait();
+
+                // If we reach here then it is an error
+                VERIFY_IS_FALSE(true);
+            }
+            catch (std::exception)
+            {
+            }
+        });
+
+        (closeTask && clientTask).wait();
+    }
+
+    // Note: Run with admin privileges to listen on default port.
+    // This test will fail with "Access denied: attempting to add Address.." exception if it is not run as admin.
+    TEST(default_port_close, "Ignore", "Manual")
+    {
+        uri address(U("http://localhost/portnotspecified"));
+        http_listener listener(address);
+
+        try
+        {
+            listener.open().wait();
+        }
+        catch (const http_exception& ex)
+        {
+            VERIFY_IS_FALSE(true, ex.what());
+            return;
+        }
+
+        // Verify close does not throw an exception while listening on default port
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, send_response_later)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        std::vector<http_request> requests;
+        pplx::extensibility::event_t request_event;
+        listener.support([&](http_request r) {
+            requests.push_back(r);
+            request_event.set();
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("")));
+        request_event.wait();
+        requests[0].reply(status_codes::OK, "HEHEHE").wait();
+        requests.clear();
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(
+                    p_response, status_codes::OK, U("text/plain; charset=utf-8"), U("HEHEHE"));
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, save_request_reply)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        http_request request;
+        pplx::extensibility::event_t request_event;
+        listener.support([&](http_request r) {
+            request = r;
+            request_event.set();
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("")));
+        request_event.wait();
+        request.reply(status_codes::OK).wait();
+
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        listener.close().wait();
+    }
+
+#if defined(_WIN32) && _MSC_VER < 1900
+    TEST_FIXTURE(uri_address, single_core_request)
+    {
+        // Fake having a scheduler with only 1 core.
+        concurrency::CurrentScheduler::Create(
+            concurrency::SchedulerPolicy(2, 1, Concurrency::MinConcurrency, 1, Concurrency::MaxConcurrency));
+
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support([](http_request request) { request.reply(status_codes::OK).get(); });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("")));
+
+        // Don't wait on the task otherwise it could inline allowing other tasks to run on the scheduler.
+        std::atomic_flag responseEvent = ATOMIC_FLAG_INIT;
+        responseEvent.test_and_set();
+        p_client->next_response().then([&](test_response* p_response) {
+            http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            responseEvent.clear();
+        });
+        while (responseEvent.test_and_set())
+        {
+        }
+
+        listener.close().wait();
+
+        concurrency::CurrentScheduler::Detach();
+    }
+#endif
+
+    TEST_FIXTURE(uri_address, save_request_response)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        http_request request;
+        pplx::extensibility::event_t request_event;
+        listener.support([&](http_request r) {
+            request = r;
+            request_event.set();
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("")));
+        request_event.wait();
+        http_response response(status_codes::OK);
+        request.reply(response).wait();
+
+        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, reply_twice)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support([](http_request request) {
+            request.reply(status_codes::OK);
+            VERIFY_THROWS(request.reply(status_codes::Accepted).get(), http_exception);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    // This test case is manual becuase it requires to be run under and account without admin access.
+    TEST(default_port_admin_access, "Ignore", "Manual")
+    {
+        uri address(U("http://localhost/"));
+        http_listener listener(address);
+        VERIFY_THROWS(listener.open().wait(), http_exception);
+    }
+
+    TEST_FIXTURE(uri_address, try_port_already_in_use, "Ignore:Linux", "Bug 879077", "Ignore:Apple", "Bug 879077")
+    {
+        test_http_server::scoped_server scoped(m_uri);
+        http_listener listener(m_uri);
+        VERIFY_THROWS(listener.open().wait(), http_exception);
+    }
+
+    TEST_FIXTURE(uri_address, reply_after_starting_close, "Ignore", "901808")
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support([&](http_request request) {
+            // Start closing the listener and then send reply.
+            listener.close();
+            request.reply(status_codes::OK).wait();
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path")));
+
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+    }
+
+    static void close_stream_early_with_length_impl(const uri& u, bool useException)
+    {
+        http_listener listener(u);
+        listener.open().wait();
+        listener.support([=](http_request request) {
+            concurrency::streams::producer_consumer_buffer<unsigned char> body;
+            concurrency::streams::istream instream = body.create_istream();
+            body.putc('A').wait();
+            body.putc('B').wait();
+            auto responseTask = request.reply(status_codes::OK, instream, 4);
+
+            if (useException)
+            {
+                body.close(std::ios::out, std::make_exception_ptr(std::invalid_argument("test exception"))).wait();
+                VERIFY_THROWS(responseTask.get(), std::invalid_argument);
+            }
+            else
+            {
+                body.close(std::ios::out).wait();
+                VERIFY_THROWS(responseTask.get(), http_exception);
+            }
+        });
+
+        web::http::client::http_client client(u);
+        client.request(methods::GET, U("/path"))
+            .then([](http_response response) -> pplx::task<std::vector<unsigned char>> {
+                VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+                return response.extract_vector();
+            })
+            .then(
+                [=](pplx::task<std::vector<unsigned char>> bodyTask) { VERIFY_THROWS(bodyTask.get(), http_exception); })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, close_stream_early_with_length)
+    {
+        close_stream_early_with_length_impl(m_uri, true);
+        close_stream_early_with_length_impl(m_uri, false);
+    }
+
+    static void close_stream_early_impl(const uri& u, bool useException)
+    {
+        http_listener listener(u);
+        listener.open().wait();
+        listener.support([=](http_request request) {
+            concurrency::streams::producer_consumer_buffer<unsigned char> body;
+            concurrency::streams::istream instream = body.create_istream();
+            body.putc('A').wait();
+            body.putc('B').wait();
+            auto responseTask = request.reply(status_codes::OK, instream);
+
+            if (useException)
+            {
+                body.close(std::ios::out, std::make_exception_ptr(std::invalid_argument("test exception"))).wait();
+                VERIFY_THROWS(responseTask.get(), std::invalid_argument);
+            }
+            else
+            {
+                body.close(std::ios::out).wait();
+                responseTask.get();
+            }
+        });
+
+        web::http::client::http_client client(u);
+        client.request(methods::GET, U("/path"))
+            .then([](http_response response) -> pplx::task<std::vector<unsigned char>> {
+                VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
+                return response.extract_vector();
+            })
+            .then([=](pplx::task<std::vector<unsigned char>> bodyTask) {
+                if (useException)
+                {
+                    VERIFY_THROWS(bodyTask.get(), http_exception);
+                }
+                else
+                {
+                    std::vector<unsigned char> body = bodyTask.get();
+                    VERIFY_ARE_EQUAL(2, body.size());
+                    VERIFY_ARE_EQUAL('A', body[0]);
+                    VERIFY_ARE_EQUAL('B', body[1]);
+                }
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, close_stream_with_exception)
+    {
+        close_stream_early_impl(m_uri, true);
+        close_stream_early_impl(m_uri, false);
+    }
+
+    // Helper function to verify http_exception and return the error code value.
+    template<typename Func>
+    int verify_http_exception(Func f)
+    {
+        int errorCode = 0;
+        try
+        {
+            f();
+            VERIFY_IS_TRUE(false);
+        }
+        catch (const http_exception& e)
+        {
+            errorCode = e.error_code().value();
+        }
+        return errorCode;
+    }
+
+    TEST_FIXTURE(uri_address,
+                 request_content_ready_timeout,
+                 "Ignore:Linux",
+                 "Unsuitable until 813276",
+                 "Ignore:Apple",
+                 "Unsuitable until 813276")
+    {
+#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+        throw std::runtime_error(
+            "Unsuitable until 813276 -- http_listener on ASIO does not support timeouts nor chunk sizes");
+#endif
+        http_listener_config config;
+        config.set_timeout(utility::seconds(1));
+        http_listener listener(m_uri, config);
+        pplx::extensibility::event_t timedOutEvent;
+        listener.support([&](http_request req) {
+            const int e1 = verify_http_exception([=]() { req.content_ready().wait(); });
+            const int e2 = verify_http_exception([=]() { req.body().read().wait(); });
+            const int e3 = verify_http_exception([=]() { req.reply(status_codes::OK).wait(); });
+            VERIFY_ARE_EQUAL(e1, e2);
+            VERIFY_ARE_EQUAL(e2, e3);
+            timedOutEvent.set();
+        });
+        listener.open().wait();
+
+        // Using our production http_client here because it
+        // allows separation of sending headers and body.
+        ::web::http::client::http_client client(m_uri);
+        concurrency::streams::producer_consumer_buffer<unsigned char> body;
+        auto responseTask = client.request(methods::PUT, U(""), body.create_istream());
+        timedOutEvent.wait();
+        body.close().wait();
+        VERIFY_THROWS(responseTask.get(), http_exception);
+
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/header_tests.cpp b/Release/tests/functional/http/listener/header_tests.cpp
new file mode 100644 (file)
index 0000000..87cf678
--- /dev/null
@@ -0,0 +1,227 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * header_tests.cpp
+ *
+ * Tests cases for using HTTP requests/response headers with http_listener.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(header_tests)
+{
+    TEST_FIXTURE(uri_address, request_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<utility::string_t, utility::string_t> headers;
+
+        // single header value.
+        headers[U("Header1")] = U("Value1");
+        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();
+
+        // empty header value.
+        headers.clear();
+        headers[U("Key1")] = U("");
+        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();
+
+        // 10 headers.
+        headers.clear();
+        headers[U("MyHeader")] = U("hehe;blach");
+        headers[U("Yo1")] = U("You, Too");
+        headers[U("Yo2")] = U("You2");
+        headers[U("Yo3")] = U("You3");
+        headers[U("Yo4")] = U("You4");
+        headers[U("Yo5")] = U("You5");
+        headers[U("Yo6")] = U("You6");
+        headers[U("Yo7")] = U("You7");
+        headers[U("Yo8")] = U("You8");
+        headers[U("Yo9")] = U("You9");
+        headers[U("Yo10")] = U("You10");
+        headers[U("Yo11")] = U("You11");
+        headers[U("Accept")] = U("text/plain");
+        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();
+
+        // several headers different casings.
+        headers.clear();
+        headers[U("CUSTOMHEADER")] = U("value1");
+        headers[U("customHEADER")] = U("value2");
+        headers[U("CUSTOMheaDER")] = U("value3");
+        listener.support([&](http_request request) {
+            std::map<utility::string_t, utility::string_t> h;
+            h[U("CUSTOMHEADER")] = U("value1, value3, value2");
+            http_asserts::assert_request_equals(request, mtd, U("/"), h);
+            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, 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<utility::string_t, utility::string_t> 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);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // No http_request/response classes can be around for close to complete.
+        {
+            // header with empty value
+            http_response response(status_codes::OK);
+            response.headers()[U("Key1")] = U("");
+            listener.support([&](http_request request) {
+                http_asserts::assert_request_equals(request, methods::POST, U("/"));
+                request.reply(response).wait();
+            });
+            VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+            p_client->next_response()
+                .then([&](test_response* p_response) {
+                    http_asserts::assert_test_response_equals(p_response, status_codes::OK, response.headers());
+                })
+                .wait();
+
+            // 10 headers
+            response = http_response(status_codes::Accepted);
+            response.headers()[U("MyHeader")] = U("hehe;blach");
+            response.headers()[U("Yo1")] = U("You, Too");
+            response.headers()[U("Yo2")] = U("You2");
+            response.headers()[U("Yo3")] = U("You3");
+            response.headers()[U("Yo4")] = U("You4");
+            response.headers()[U("Yo5")] = U("You5");
+            response.headers()[U("Yo6")] = U("You6");
+            response.headers()[U("Yo7")] = U("You7");
+            response.headers()[U("Yo8")] = U("You8");
+            response.headers()[U("Yo9")] = U("You9");
+            response.headers()[U("Yo10")] = U("You10");
+            response.headers()[U("Yo11")] = U("You11");
+            response.headers()[U("Accept")] = U("text/plain");
+            listener.support([&](http_request request) {
+                http_asserts::assert_request_equals(request, methods::POST, U("/"));
+                request.reply(response).wait();
+            });
+            VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+            p_client->next_response()
+                .then([&](test_response* p_response) {
+                    http_asserts::assert_test_response_equals(p_response, status_codes::Accepted, response.headers());
+                })
+                .wait();
+
+            // several headers in different casings
+            response = http_response(status_codes::BadGateway);
+            response.headers().add(U("Key1"), U("value1"));
+            response.headers()[U("KEY1")] += U("value2");
+            listener.support([&](http_request request) {
+                http_asserts::assert_request_equals(request, methods::POST, U("/"));
+                request.reply(response).wait();
+            });
+            VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+            p_client->next_response()
+                .then([&](test_response* p_response) {
+                    http_asserts::assert_test_response_equals(p_response, status_codes::BadGateway, response.headers());
+                })
+                .wait();
+
+            // duplicate headers fields
+            response = http_response(status_codes::BadGateway);
+            response.headers().add(U("Key1"), U("value1"));
+            response.headers().add(U("Key1"), U("value2"));
+            listener.support([&](http_request request) {
+                http_asserts::assert_request_equals(request, methods::POST, U("/"));
+                request.reply(response).wait();
+            });
+            VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+            p_client->next_response()
+                .then([&](test_response* p_response) {
+                    http_asserts::assert_test_response_equals(p_response, status_codes::BadGateway, response.headers());
+                })
+                .wait();
+        }
+
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/http_listener_tests.h b/Release/tests/functional/http/listener/http_listener_tests.h
new file mode 100644 (file)
index 0000000..5e50a58
--- /dev/null
@@ -0,0 +1,49 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * http_listener_tests.h
+ *
+ * Common declarations and helper functions for http_listener test cases.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/http_listener.h"
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+class uri_address
+{
+public:
+    uri_address() : m_uri(U("http://localhost:34567/")), m_secure_uri(U("https://localhost:8443/"))
+    {
+        if (!s_dummy_listener)
+            s_dummy_listener =
+                std::make_shared<web::http::experimental::listener::http_listener>(U("http://localhost:30000/"));
+    }
+
+    // By introducing an additional listener, we can avoid having to close the
+    // server after each unit test.
+
+    static std::shared_ptr<web::http::experimental::listener::http_listener> s_dummy_listener;
+    web::http::uri m_uri;
+    web::http::uri m_secure_uri;
+};
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/listener_construction_tests.cpp b/Release/tests/functional/http/listener/listener_construction_tests.cpp
new file mode 100644 (file)
index 0000000..c6d9587
--- /dev/null
@@ -0,0 +1,576 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * listener_construction_tests.cpp
+ *
+ * Tests cases for covering creating http_listeners in various ways.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::common::utilities;
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(listener_construction_tests)
+{
+    TEST_FIXTURE(uri_address, default_constructor)
+    {
+        // Test that the default ctor works.
+        http_listener listener;
+
+        VERIFY_IS_TRUE(listener.uri().is_empty());
+        VERIFY_THROWS(listener.open().wait(), std::invalid_argument);
+    }
+
+    TEST_FIXTURE(uri_address, move_operations)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // move constructor
+        http_listener listener2 = std::move(listener);
+        listener2.support(methods::PUT, [](http_request request) {
+            http_asserts::assert_request_equals(request, U("PUT"), U("/"));
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(U("PUT"), U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        // move assignment
+        listener = std::move(listener2);
+        listener.support(methods::PUT, [](http_request request) {
+            http_asserts::assert_request_equals(request, U("PUT"), U("/"));
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(U("PUT"), U("/")));
+        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, various_uris)
+    {
+        http_listener listener(web::http::uri_builder(m_uri).append_path(U("path1")).to_uri());
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // Path that matches exactly
+        listener.support([](http_request request) {
+            http_asserts::assert_request_equals(request, U("GET"), U(""));
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        // Path that matches but is more specific.
+        listener.support([](http_request request) {
+            http_asserts::assert_request_equals(request, U("GET"), U("/path2"));
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1/path2")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        // Try a request with a path that doesn't match.
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path3/path2")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::NotFound);
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, uri_routing)
+    {
+        http_listener listener1(web::http::uri_builder(m_uri).append_path(U("path1")).to_uri());
+        http_listener listener2(web::http::uri_builder(m_uri).append_path(U("path2")).to_uri());
+        http_listener listener3(web::http::uri_builder(m_uri).append_path(U("path1/path2")).to_uri());
+
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // Path that matches exactly
+        listener1.support([](http_request request) { request.reply(status_codes::OK); });
+        listener1.open().wait();
+
+        listener2.support([](http_request request) { request.reply(status_codes::Created); });
+        listener2.open().wait();
+
+        listener3.support([](http_request request) { request.reply(status_codes::Accepted); });
+        listener3.open().wait();
+
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path2")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::Created);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1/path2")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::Accepted);
+            })
+            .wait();
+
+        // Try a request with a path that doesn't match.
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path3/path2")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::NotFound);
+            })
+            .wait();
+
+        listener1.close().wait();
+        listener2.close().wait();
+        listener3.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, uri_error_cases)
+    {
+        // non HTTP scheme
+        VERIFY_THROWS(http_listener(U("ftp://localhost:456/")), std::invalid_argument);
+
+        // empty HTTP host
+        VERIFY_THROWS(http_listener(U("http://:456/")), std::invalid_argument);
+
+        // try specifying a query
+        VERIFY_THROWS(http_listener(U("http://localhost:45678/path?key1=value")), std::invalid_argument);
+
+        // try specifing a fragment
+        VERIFY_THROWS(http_listener(U("http://localhost:4563/path?key1=value#frag")), std::invalid_argument);
+    }
+
+    TEST_FIXTURE(uri_address, create_listener_get)
+    {
+        http_listener listener(m_uri);
+
+        listener.support(methods::GET, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/"));
+            request.reply(status_codes::OK);
+        });
+
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::MethodNotAllowed);
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, create_listener_get_put)
+    {
+        http_listener listener(m_uri);
+
+        listener.support(methods::GET, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/"));
+            request.reply(status_codes::OK);
+        });
+
+        listener.support(methods::PUT, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::PUT, U("/"));
+            request.reply(status_codes::OK);
+        });
+
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::MethodNotAllowed);
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, create_listener_get_put_post)
+    {
+        http_listener listener(m_uri);
+
+        listener.support(methods::GET, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/"));
+            request.reply(status_codes::OK);
+        });
+
+        listener.support(methods::PUT, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::PUT, U("/"));
+            request.reply(status_codes::OK);
+        });
+
+        listener.support(methods::POST, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            request.reply(status_codes::OK);
+        });
+
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::DEL, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::MethodNotAllowed);
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, create_listener_get_put_post_delete)
+    {
+        http_listener listener(m_uri);
+
+        listener.support(methods::GET, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/"));
+            request.reply(status_codes::OK);
+        });
+
+        listener.support(methods::PUT, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::PUT, U("/"));
+            request.reply(status_codes::OK);
+        });
+
+        listener.support(methods::POST, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            request.reply(status_codes::OK);
+        });
+
+        listener.support(methods::DEL, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::DEL, U("/"));
+            request.reply(status_codes::OK);
+        });
+
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::DEL, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::HEAD, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::MethodNotAllowed);
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, get_listener_config)
+    {
+        // Verify default configuration.
+        {
+            http_listener listener(m_uri);
+            VERIFY_ARE_EQUAL(utility::seconds(120), listener.configuration().timeout());
+            listener.open().wait();
+            listener.close().wait();
+        }
+
+        // Verify specified config values.
+        {
+            http_listener_config config;
+            utility::seconds t(1);
+            config.set_timeout(t);
+            http_listener listener(m_uri, config);
+            listener.open().wait();
+            listener.close().wait();
+            VERIFY_ARE_EQUAL(t, listener.configuration().timeout());
+        }
+    }
+
+    TEST_FIXTURE(uri_address, listener_config_creation)
+    {
+        // copy constructor
+        {
+            http_listener_config config;
+            config.set_timeout(utility::seconds(2));
+            http_listener_config copy(config);
+            VERIFY_ARE_EQUAL(utility::seconds(2), copy.timeout());
+        }
+
+        // move constructor
+        {
+            http_listener_config config;
+            config.set_timeout(utility::seconds(2));
+            http_listener_config ctorMove(std::move(config));
+            VERIFY_ARE_EQUAL(utility::seconds(2), ctorMove.timeout());
+        }
+
+        // assignment
+        {
+            http_listener_config config;
+            config.set_timeout(utility::seconds(2));
+            http_listener_config assign;
+            assign = config;
+            VERIFY_ARE_EQUAL(utility::seconds(2), assign.timeout());
+        }
+
+        // move assignment
+        {
+            http_listener_config config;
+            config.set_timeout(utility::seconds(2));
+            http_listener_config assignMove;
+            assignMove = std::move(config);
+            VERIFY_ARE_EQUAL(utility::seconds(2), assignMove.timeout());
+        }
+    }
+
+#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
+
+    TEST_FIXTURE(uri_address, create_https_listener_get, "Ignore", "github 209")
+    {
+        const char* self_signed_cert = R"(
+-----BEGIN CERTIFICATE-----
+MIIDlzCCAn+gAwIBAgIJAP9ZV+1X94UjMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNV
+BAYTAkNOMQswCQYDVQQIDAJTSDELMAkGA1UEBwwCU0gxEjAQBgNVBAoMCU1JQ1JP
+U09GVDERMA8GA1UECwwISFBDLVBBQ0sxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0x
+NTA4MTkwOTA0MjhaFw00MzAxMDMwOTA0MjhaMGIxCzAJBgNVBAYTAkNOMQswCQYD
+VQQIDAJTSDELMAkGA1UEBwwCU0gxEjAQBgNVBAoMCU1JQ1JPU09GVDERMA8GA1UE
+CwwISFBDLVBBQ0sxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBALLv7AAPa+4wYpa+3tqc9HIHhh8kv/MpV2Dm+oKG27iH
+zOugMNAPqLzMAaWCzDRyw27I+jPS3pzAAu6rQ0v2H6XNrie1YEEV27j1WOUS9iFy
+vcf6Y+ywUKXvFlN/VM/ZFz9Z8U3jc7Y6unIyoUs8UdX/RRITspb2m7SUxlmLJ+4c
+qiLrHwstNB2NHIZN72oc8DaS5eBqBdT9h6NO62RSBTrAlR7Vk9eU/5trYkd5+PoC
+pispvU+7Fe24uVerGgU6Yoyd7DMj+3BpbG3g/VkOlGhgH0DNtbKu3v/XOmnzdZn6
+dzoOoGFNpG1NeH2Xv0vnvEZP6WG4h/TFSafBJMONNnMCAwEAAaNQME4wHQYDVR0O
+BBYEFO1mAjAmLk1J0iT93xfczAE5mxgzMB8GA1UdIwQYMBaAFO1mAjAmLk1J0iT9
+3xfczAE5mxgzMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFB8AACf
+5O+sPe3PZ8IPgwZb+BCXdoXc2rngR/gOaYO019TZyNLuHRzW9FtplzW25IbQ9Jnc
+b+jmY2Ill7Zf3TX4OhHEwscJ1G2LBaqZfQlwSbYJmCzvRNSzSbF3RigNQD5Qhdph
+vVBdvVGTZnVeatjTOFKUyfhcXf4DMb6eMfaU6il/VJCSMW0j3hYNQjPm3V/PLxnG
+fd9T4hpCUd8MK2XG4RqJAzh6x/6v0fc6mRHBS5+qTWYSDGFwITrU1pP2L9qFegpm
+aNAom7bdENU8uivd+vrLnG2fKvFSssjVfaXpFLKAICfTJY9A3/CWnZ1AcbE5El7A
+adctopihoUrlAb0=
+-----END CERTIFICATE-----
+        )";
+        const char* private_key = R"(
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCy7+wAD2vuMGKW
+vt7anPRyB4YfJL/zKVdg5vqChtu4h8zroDDQD6i8zAGlgsw0csNuyPoz0t6cwALu
+q0NL9h+lza4ntWBBFdu49VjlEvYhcr3H+mPssFCl7xZTf1TP2Rc/WfFN43O2Orpy
+MqFLPFHV/0USE7KW9pu0lMZZiyfuHKoi6x8LLTQdjRyGTe9qHPA2kuXgagXU/Yej
+TutkUgU6wJUe1ZPXlP+ba2JHefj6AqYrKb1PuxXtuLlXqxoFOmKMnewzI/twaWxt
+4P1ZDpRoYB9AzbWyrt7/1zpp83WZ+nc6DqBhTaRtTXh9l79L57xGT+lhuIf0xUmn
+wSTDjTZzAgMBAAECggEAenzd8lScL1qTwlk6ODAE7SHVX/BKLWv5Um4KwdsLAVCE
+qC7p+yMdANAtuFzG6Ig+29Fb5KnOlUKjPzmhQZhjpZ4cPzZbg3IxDHV2uqi2L8NZ
+wlDWoik3q770a4fYSMd0sHsjQYwXo4CkLJQX8WaDJpgtcehl8g0yHPVSqe0mEkoL
+dxdqaZnxprchscxefWaGaysIxEO+V+ZOBaPNf4i8PmBKoMNczWZbLcdKhRL7aLeW
+ngPQp1xSWYoN8fPoonpL2qTSop3Nsc2INpwGcYPAj3vxdasC3+DZ8JEJI2AmxpVB
+13BLkd3nDzOwimZIlu9Fv+NMJ1vb9XdC249ZOqo68QKBgQDigkws1W429nqDtEtQ
+Dr5ebHTdP4gZlNt6vWx5obGLCMBAzoyubfNCCBTCYsCPj8hXxNfiPArPFFkIgEx9
++w0n7BlaYL6SD2xD4q+YzA1/j4Loakxc7N9z8Cyu+/YHifvLhzwqgFnkLfFnVq9N
+TF8TatHUYcrbcpawJLz0wr/cnQKBgQDKPAYNTzqPLOOBaE4DfnJNn2zctGU8G5Xp
+0L/ED8O1t9AjjV2xVO8PDPNDZAxMzgnIbWeU9iWRSLbr7NloXElKh/QlITjAbSXe
+HsUruq1SmDgiaUhEtDaaJ1SqSZZWY2BZqNXMdILOCgvZGnOyyBR2U49zuNaRHyhm
+kmZMdIIKTwKBgQDezAk/hEQfvfuuNpZpzcbEu+uLgKVPfFMSfOYJEdnAB0CLvl80
+Z6QBzE8XEOmVjHkkk9NBjYuYOsyEhyY2OM2s+hfKBSUOKCt27q+IHRYd5bx+/afV
+M41rzc8141ISAlBw1rmAmLVSszojSmmuH7PZNpXkULineCPuaISQQEtWJQKBgQDD
+laVsvdEuowUsJEo+ys2VELhiAv1dUnh79u1fmrd2SV085P1WAYRqE+Y4qMvUg/em
+JVjmEeBnT+HI7fmdGpOvRyjxt92BDI5w8WVTU2lI1fqEHTpNZ9Te5WbWgfCpf9ax
+H74VzCCtT74Bq7l1kFdp0IqOKpcpJu8VtETHcG5LtQKBgQC4Tx7El1Xb4hsI4dvE
+h43j3KBb3evlz6vaqgz0BArahYAz2UkkOYDSOPs4K6aOxxXjO0BjqQqCx/tCPcU5
+AvLsTlswO+wDLXM1DoKxzFBZL5o8927niqW+vZpzyGc1uPmC1MG7+MDKdZsR+e+9
+XzJTD4slrGSJrcpLt/g/Jqqdjg==
+-----END PRIVATE KEY-----
+        )";
+
+        auto body = utility::string_t {U("body content")};
+        http_headers all_headers;
+        all_headers.add(U("Accept"), U("text/plain"));
+        all_headers.add(U("Accept-Charset"), U("utf-8"));
+        all_headers.add(U("Accept-Encoding"), U("gzip, deflate"));
+        all_headers.add(U("Accept-Language"), U("en-US"));
+        all_headers.add(U("Accept-Datetime"), U("Thu, 31 May 2007 20:35:00 GMT"));
+        all_headers.add(U("Authorization"), U("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="));
+        all_headers.add(U("Cache-Control"), U("no-cache"));
+        all_headers.add(U("Cookie"), U("$Version=1; Skin=new;"));
+        all_headers.add(U("Content-Length"), body.size());
+        all_headers.add(U("Content-MD5"), U("Q2hlY2sgSW50ZWdyaXR5IQ=="));
+        all_headers.add(U("Content-Type"), U("application/x-www-form-urlencoded"));
+        all_headers.add(U("Date"), U("Tue, 15 Nov 1994 08:12:31 GMT"));
+        all_headers.add(U("Expect"), U("100-continue"));
+        all_headers.add(U("Forwarded"),
+                        U("for=192.0.2.60;proto=http;by=203.0.113.43Forwarded: for=192.0.2.43, for=198.51.100.17"));
+        all_headers.add(U("From"), U("user@example.com"));
+        all_headers.add(U("Host"), U("en.wikipedia.org"));
+        all_headers.add(U("If-Match"), U("\"737060cd8c284d8af7ad3082f209582d\""));
+        all_headers.add(U("If-Modified-Since"), U("Sat, 29 Oct 1994 19:43:31 GMT"));
+        all_headers.add(U("If-None-Match"), U("\"737060cd8c284d8af7ad3082f209582d\""));
+        all_headers.add(U("If-Range"), U("\"737060cd8c284d8af7ad3082f209582d\""));
+        all_headers.add(U("If-Unmodified-Since"), U("Sat, 29 Oct 1994 19:43:31 GMT"));
+        all_headers.add(U("Max-Forwards"), U("10"));
+        all_headers.add(U("Origin"), U("http://www.example-social-network.com"));
+        all_headers.add(U("Pragma"), U("no-cache"));
+        all_headers.add(U("Proxy-Authorization"), U("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="));
+        all_headers.add(U("Range"), U("bytes=500-999"));
+        all_headers.add(U("Referer"), U("http://en.wikipedia.org/wiki/Main_Page"));
+        all_headers.add(U("TE"), U("trailers,Ā deflate"));
+        all_headers.add(U("User-Agent"), U("Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0"));
+        all_headers.add(U("Upgrade"), U("HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11"));
+        all_headers.add(U("Via"), U("1.0 fred, 1.1 example.com (Apache/1.1)"));
+        all_headers.add(U("Warning"), U("199 Miscellaneous warning"));
+
+        boost::asio::const_buffer cert(self_signed_cert, std::strlen(self_signed_cert));
+        boost::asio::const_buffer key(private_key, std::strlen(private_key));
+
+        http_listener_config server_config;
+        server_config.set_ssl_context_callback([&](boost::asio::ssl::context& ctx) {
+            ctx.set_options(boost::asio::ssl::context::default_workarounds);
+            ctx.use_certificate_chain(cert);
+            ctx.use_private_key(key, boost::asio::ssl::context::pem);
+        });
+
+        http_listener listener(m_secure_uri, server_config);
+
+        listener.support(methods::GET, [&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/"));
+
+            for (auto&& h : all_headers)
+            {
+                VERIFY_IS_TRUE(request.headers().has(h.first));
+                VERIFY_ARE_EQUAL(h.second, request.headers().find(h.first)->second);
+            }
+
+            VERIFY_ARE_EQUAL(body, request.extract_string(true).get());
+
+            request.reply(status_codes::OK);
+        });
+
+        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("/"));
+
+        msg.headers() = all_headers;
+        msg.set_body(body);
+
+        http_asserts::assert_response_equals(client.request(msg).get(), status_codes::OK);
+
+        listener.close().wait();
+    }
+#endif
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/reply_helper_tests.cpp b/Release/tests/functional/http/listener/reply_helper_tests.cpp
new file mode 100644 (file)
index 0000000..0eb6733
--- /dev/null
@@ -0,0 +1,104 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * reply_helper_tests.cpp
+ *
+ * Tests cases covering the reply helper functions on HTTP response.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace utility;
+using namespace web;
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(reply_helper_tests)
+{
+    TEST_FIXTURE(uri_address, json)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support(
+            [](http_request request) { request.reply(status_codes::OK, json::value::parse(U("true"))).wait(); });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(
+                    p_response, status_codes::OK, U("application/json"), U("true"));
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, string)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support([](http_request request) {
+            std::string body("test str");
+            request.reply(status_codes::OK, body).wait();
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(
+                    p_response, status_codes::OK, U("text/plain; charset=utf-8"), U("test str"));
+            })
+            .wait();
+
+        // content type and string body
+        listener.support([](http_request request) {
+            utility::string_t s(U("test str"));
+            request.reply(status_codes::OK, s, U("custom content")).wait();
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(
+                    p_response, status_codes::OK, U("custom content"), U("test str"));
+            })
+            .wait();
+
+        // content type and rvalue reference string body
+        listener.support(
+            [](http_request request) { request.reply(status_codes::OK, "test str", "text/plain").wait(); });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK, U("text/plain"), U("test str"));
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/request_extract_tests.cpp b/Release/tests/functional/http/listener/request_extract_tests.cpp
new file mode 100644 (file)
index 0000000..910c979
--- /dev/null
@@ -0,0 +1,192 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * request_extract_tests.cpp
+ *
+ * Tests cases for covering calling extract_ overloads on HTTP request.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace utility;
+using namespace web;
+using namespace web::http;
+using namespace web::http::experimental::listener;
+using namespace utility::conversions;
+
+using namespace tests::common::utilities;
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(request_extract_tests)
+{
+    TEST_FIXTURE(uri_address, extract_string)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+        std::string data("HEHEHE");
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, U("PUT"), U("/"), to_string_t(data));
+            VERIFY_ARE_EQUAL(U("text/plain"), request.headers().content_type());
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U(""), U("text/plain"), data));
+        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, extract_string_force)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+        std::string data("HEHEHE");
+
+        listener.support([&](http_request request) {
+            VERIFY_ARE_EQUAL(to_string_t(data), request.extract_string(true).get());
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U(""), U("unknown charset"), data));
+        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, extract_json)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        json::value j(true);
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, U("PUT"), U("/"));
+            VERIFY_ARE_EQUAL(U("application/json"), request.headers().content_type());
+            const json::value j_found = request.extract_json().get();
+            VERIFY_ARE_EQUAL(j.serialize(), j_found.serialize());
+            request.reply(status_codes::OK);
+        });
+        std::string data = to_utf8string(j.serialize());
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U(""), U("application/json"), data));
+        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, extract_json_force)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        json::value j(true);
+        listener.support([&](http_request request) {
+            const json::value j_found = request.extract_json(true).get();
+            VERIFY_ARE_EQUAL(j.serialize(), j_found.serialize());
+            request.reply(status_codes::OK);
+        });
+        std::string data = to_utf8string(j.serialize());
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U(""), U("unknown charset"), data));
+        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, empty_vector)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+        std::string data("");
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, U("PUT"), U("/"));
+            VERIFY_ARE_EQUAL(U("text/plain"), request.headers().content_type());
+            std::vector<unsigned char> vec = request.extract_vector().get();
+            VERIFY_ARE_EQUAL(vec.size(), 0);
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U(""), U("text/plain"), data));
+        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, extract_vector)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+        std::string data("HEHEHE");
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, U("PUT"), U("/"));
+            VERIFY_ARE_EQUAL(U("text/plain"), request.headers().content_type());
+            std::vector<unsigned char> vec = request.extract_vector().get();
+            VERIFY_ARE_EQUAL(vec.size(), data.size());
+            VERIFY_ARE_EQUAL('H', vec[0]);
+            VERIFY_ARE_EQUAL('E', vec[1]);
+            VERIFY_ARE_EQUAL('H', vec[2]);
+            VERIFY_ARE_EQUAL('E', vec[3]);
+            VERIFY_ARE_EQUAL('H', vec[4]);
+            VERIFY_ARE_EQUAL('E', vec[5]);
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U(""), U("text/plain"), data));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/request_handler_tests.cpp b/Release/tests/functional/http/listener/request_handler_tests.cpp
new file mode 100644 (file)
index 0000000..afd9142
--- /dev/null
@@ -0,0 +1,557 @@
+/***
+ * 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 covering the http_listener class itself.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::common::utilities;
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+std::shared_ptr<web::http::experimental::listener::http_listener> uri_address::s_dummy_listener;
+
+SUITE(request_handler_tests)
+{
+    TEST_FIXTURE(uri_address, support)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support(U("CUSTOM"), [](http_request request) {
+            http_asserts::assert_request_equals(request, U("CUSTOM"), U("/"));
+            request.reply(status_codes::OK);
+        });
+        listener.support(methods::PUT, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::PUT, U("/"));
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::DEL, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::MethodNotAllowed);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(U("CUSTOM"), U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        // Add one with a different case.
+        listener.support(U("CUSToM"), [](http_request request) {
+            http_asserts::assert_request_equals(request, U("CUSToM"), U("/"));
+            request.reply(status_codes::Gone);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(U("CUSToM"), U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::Gone);
+            })
+            .wait();
+
+        // Add a general handler
+        listener.support([](http_request request) {
+            http_asserts::assert_request_equals(request, U("CuSToM"), U("/"));
+            request.reply(status_codes::Created);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(U("CUSToM"), U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::Gone);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, p_client->request(U("CuSToM"), U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::Created);
+            })
+            .wait();
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, exceptions_in_handler)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // throw exception
+        listener.support(methods::GET, [](http_request request) {
+            http_asserts::assert_request_equals(request, U("GET"), U("/"));
+            throw std::runtime_error("");
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(U("GET"), U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::InternalError);
+            })
+            .wait();
+
+        // throw exception, after replying first
+        listener.support(methods::PUT, [](http_request request) {
+            http_asserts::assert_request_equals(request, U("PUT"), U("/"));
+            request.reply(status_codes::OK);
+            throw 55;
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(U("PUT"), U("/")));
+        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, handle_options)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        listener.support(methods::GET, [](http_request) {});
+        listener.support(methods::PUT, [](http_request) {});
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::OPTIONS, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+                VERIFY_ARE_EQUAL(U("GET, PUT"), p_response->m_headers[U("Allow")]);
+            })
+            .wait();
+
+        // try overridding the default OPTIONS handler
+        listener.support(methods::OPTIONS, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::OPTIONS, U("/"));
+            request.reply(status_codes::NoContent);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::OPTIONS, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::NoContent);
+            })
+            .wait();
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, handle_trace)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::TRCE, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+                std::string utf8_response;
+                utf8_response.assign(p_response->m_data.begin(), p_response->m_data.end());
+#ifdef _WIN32
+                VERIFY_ARE_EQUAL("TRACE / HTTP/1.1\r\nConnection: Keep-Alive\r\nHost: localhost:34567\r\nUser-Agent: "
+                                 "test_http_client\r\n\r\n",
+                                 utf8_response);
+#else
+                VERIFY_ARE_EQUAL(
+                    "TRACE / HTTP/1.1\r\nConnection: Keep-Alive\r\nContent-Length: 0\r\nContent-Type: text/plain; "
+                    "charset=utf-8\r\nHost: localhost:34567\r\nUser-Agent: test_http_client\r\n\r\n",
+                    utf8_response);
+#endif
+            })
+            .wait();
+
+        // try overridding the default OPTIONS handler
+        listener.support(methods::TRCE, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::TRCE, U("/"));
+            request.reply(status_codes::NoContent);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::TRCE, U("/")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::NoContent);
+            })
+            .wait();
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, async_request_handler)
+    {
+        http_listener listener(m_uri);
+        pplx::extensibility::event_t e;
+        listener.support([&e](http_request request) {
+            e.set();
+            request.reply(status_codes::OK).wait();
+        });
+        listener.open().wait();
+
+        client::http_client client(m_uri);
+        auto buf = streams::producer_consumer_buffer<uint8_t>();
+        pplx::task<http_response> response =
+            client.request(methods::PUT, U("/"), buf.create_istream(), U("text/plain"));
+
+        e.wait();
+        buf.close(std::ios_base::out).wait();
+        response.wait();
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, multiple_listeners)
+    {
+        http_listener listener1(U("http://localhost:45678/path1"));
+        http_listener listener2(U("http://localhost:45678/path1/path2"));
+        http_listener listener3(U("http://localhost:45678/path3"));
+        listener1.open().wait();
+        listener2.open().wait();
+        listener3.open().wait();
+
+        test_http_client::scoped_client client(U("http://localhost:45678"));
+        test_http_client* p_client = client.client();
+
+        // send a request to the first listener
+        listener1.support(methods::GET, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/"));
+            request.reply(status_codes::NoContent);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::NoContent);
+            })
+            .wait();
+
+        // send a request to the second listener
+        listener2.support(methods::PUT, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::PUT, U("/path4"));
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U("/path1/path2/path4")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        // send a request to the third listener
+        listener3.support(methods::POST, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            request.reply(status_codes::Created);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::POST, U("/path3")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::Created);
+            })
+            .wait();
+
+        // Remove the second listener and send a request again.
+        listener2.close().wait();
+        listener1.support(methods::GET, [](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/path2/path4"));
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1/path2/path4")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        listener3.close().wait();
+        listener1.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, unregister_while_processing)
+    {
+        http_listener listener1(U("http://localhost:45679/path1"));
+        http_listener listener2(U("http://localhost:45679/path1/path2"));
+        listener1.open().wait();
+        listener2.open().wait();
+
+        test_http_client::scoped_client client1(U("http://localhost:45679"));
+        test_http_client* p_client1 = client1.client();
+        test_http_client::scoped_client client2(U("http://localhost:45679"));
+        test_http_client* p_client2 = client2.client();
+
+        // first listener is used to wait until a request comes into the second
+        // and then will try to close the second.
+        pplx::extensibility::event_t secondRequest;
+        listener1.support(methods::GET, [&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/"));
+            secondRequest.wait();
+            listener2.close().wait();
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client1->request(methods::GET, U("/path1")));
+        listener2.support(methods::GET, [&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/"));
+            secondRequest.set();
+            os_utilities::sleep(200);
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client2->request(methods::GET, U("/path1/path2/")));
+        p_client1->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        p_client2->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        listener1.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, multiple_requests)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+        test_http_client::scoped_client client2(m_uri);
+        test_http_client* p_client2 = client2.client();
+        test_http_client::scoped_client client3(m_uri);
+        test_http_client* p_client3 = client3.client();
+
+        volatile unsigned long requestCount = 0;
+        listener.support(methods::GET, [&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/path1"));
+            os_utilities::interlocked_increment(&requestCount);
+            while (requestCount != 3)
+            {
+                os_utilities::sleep(1);
+            }
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1")));
+        VERIFY_ARE_EQUAL(0, p_client2->request(methods::GET, U("/path1")));
+        VERIFY_ARE_EQUAL(0, p_client3->request(methods::GET, U("/path1")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        p_client2->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        p_client3->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, multiple_clients_multiple_requests)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        const size_t NUM_CLIENTS = 10;
+        std::vector<std::unique_ptr<test_http_client>> clients;
+        for (size_t i = 0; i < NUM_CLIENTS; ++i)
+        {
+            std::unique_ptr<test_http_client> client(new test_http_client(m_uri));
+            VERIFY_ARE_EQUAL(0, client->open());
+            clients.push_back(std::move(client));
+        }
+
+        listener.support(methods::GET, [&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::GET, U("/"));
+            request.reply(status_codes::OK);
+        });
+        for (size_t j = 0; j < 10; ++j)
+        {
+            std::vector<pplx::task<void>> requests;
+            for (size_t i = 0; i < NUM_CLIENTS; ++i)
+            {
+                VERIFY_ARE_EQUAL(0, clients[i]->request(methods::GET, U("/")));
+                requests.push_back(clients[i]->next_response().then([&](test_response* p_response) {
+                    http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+                }));
+            }
+            pplx::when_all(requests.begin(), requests.end()).wait();
+        }
+
+        for (size_t i = 0; i < NUM_CLIENTS; ++i)
+        {
+            VERIFY_ARE_EQUAL(0, clients[i]->close());
+        }
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, test_leaks)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // pick a large number to see leaks easier
+        const size_t nbytes = 1024 * 1000;
+
+        listener.support(methods::PUT, [&](http_request message) {
+            while (message.body().streambuf().in_avail() < nbytes)
+                ;
+
+            utility::string_t request = U("unknown");
+            auto it = message.headers().find(U("ClientID"));
+            if (it != message.headers().end())
+            {
+                message.reply(status_codes::OK, U("Unknown command"));
+            }
+            else
+            {
+                message.reply(status_codes::OK, U("ClientID missing"));
+            }
+        });
+
+        const int N = 1; // use large number of iterations to test for leaks
+        for (int i = 0; i < N; ++i)
+        {
+            std::map<utility::string_t, utility::string_t> headers;
+            headers[U("ClientID")] = U("123");
+            headers[U("Request")] = U("Upload");
+            headers[U("ImgNr")] = U("1");
+
+            // this help recognizing the leaked memory in the CRT/VLD dump
+            std::string data;
+            for (int j = 0; j < nbytes; j++)
+                data.push_back('a' + (j % 26));
+            VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U("/path1"), headers, data));
+            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, http_version)
+    {
+        // formatting should succeed
+        VERIFY_IS_TRUE("HTTP/0.9" == http_versions::HTTP_0_9.to_utf8string());
+        VERIFY_IS_TRUE("HTTP/1.0" == http_versions::HTTP_1_0.to_utf8string());
+        VERIFY_IS_TRUE("HTTP/1.1" == http_versions::HTTP_1_1.to_utf8string());
+        VERIFY_IS_TRUE("HTTP/12.3" == (http_version {12, 3}).to_utf8string());
+        // parsing should succeed
+        VERIFY_IS_TRUE(http_version::from_string("HTTP/0.9") == http_versions::HTTP_0_9);
+        VERIFY_IS_TRUE(http_version::from_string("HTTP/1.0") == http_versions::HTTP_1_0);
+        VERIFY_IS_TRUE(http_version::from_string("HTTP/1.1") == http_versions::HTTP_1_1);
+        VERIFY_IS_TRUE((http_version::from_string("HTTP/12.3") == http_version {12, 3}));
+        // parsing should fail
+        http_version unknown = {0, 0};
+        VERIFY_IS_TRUE(http_version::from_string("http/12.3") == unknown);
+        VERIFY_IS_TRUE(http_version::from_string("HTTP/12.3foo") == unknown);
+        VERIFY_IS_TRUE(http_version::from_string("HTTP/12.") == unknown);
+        VERIFY_IS_TRUE(http_version::from_string("HTTP/12") == unknown);
+        VERIFY_IS_TRUE(http_version::from_string("HTTP/.3") == unknown);
+        VERIFY_IS_TRUE(http_version::from_string("HTTP/") == unknown);
+        VERIFY_IS_TRUE(http_version::from_string("HTTP") == unknown);
+        VERIFY_IS_TRUE(http_version::from_string("HTTP") == unknown);
+        VERIFY_IS_TRUE(http_version::from_string("foo") == unknown);
+        VERIFY_IS_TRUE(http_version::from_string("") == unknown);
+
+        http_listener listener(U("http://localhost:45678/path1"));
+        listener.open().wait();
+
+        test_http_client::scoped_client client(U("http://localhost:45678"));
+        test_http_client* p_client = client.client();
+
+        volatile unsigned long requestCount = 0;
+
+        listener.support(methods::GET, [&requestCount](http_request request) {
+            const auto& httpVersion = request.http_version();
+
+            // All clients currently use HTTP/1.1
+            VERIFY_IS_TRUE(httpVersion == http_versions::HTTP_1_1);
+
+            os_utilities::interlocked_increment(&requestCount);
+            request.reply(status_codes::NoContent);
+        });
+
+        // Send a request to the listener
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1")));
+
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::NoContent);
+            })
+            .wait();
+
+        VERIFY_IS_TRUE(requestCount >= 1);
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, remote_address)
+    {
+        http_listener listener(U("http://localhost:45678/path1"));
+        listener.open().wait();
+
+        test_http_client::scoped_client client(U("http://localhost:45678"));
+        test_http_client* p_client = client.client();
+
+        volatile unsigned long requestCount = 0;
+
+        listener.support(methods::GET, [&requestCount](http_request request) {
+            const string_t& remoteAddr = request.remote_address();
+            const string_t& localhost4 = string_t(U("127.0.0.1"));
+            const string_t& localhost6 = string_t(U("::1"));
+
+            // We can't guarantee that the host has both IPv4 and IPv6 available, so check for either IP
+            VERIFY_IS_TRUE((remoteAddr == localhost4) || (remoteAddr == localhost6));
+
+            os_utilities::interlocked_increment(&requestCount);
+            request.reply(status_codes::NoContent);
+        });
+
+        // Send a request to the listener
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1")));
+
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::NoContent);
+            })
+            .wait();
+
+        VERIFY_IS_TRUE(requestCount >= 1);
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/request_relative_uri_tests.cpp b/Release/tests/functional/http/listener/request_relative_uri_tests.cpp
new file mode 100644 (file)
index 0000000..4b8aaf0
--- /dev/null
@@ -0,0 +1,144 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * request_relative_uri_tests.cpp
+ *
+ * Tests cases the combinations of base uri and relative uri with incoming requests to the http_listener.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(request_relative_uri_tests)
+{
+    TEST_FIXTURE(uri_address, empty_base_uri)
+    {
+        // listen on empty, request /path1/path2
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client client(m_uri);
+        VERIFY_ARE_EQUAL(0, client.open());
+        listener.support([](http_request request) {
+            VERIFY_ARE_EQUAL(U("/path1/path2"), request.request_uri().path());
+            VERIFY_ARE_EQUAL(U("/path1/path2"), request.relative_uri().to_string());
+            request.reply(status_codes::OK).wait();
+        });
+        VERIFY_ARE_EQUAL(0, client.request(methods::GET, U("/path1/path2")));
+        client.next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, client.close());
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, nested_paths)
+    {
+        // listen on /path1, request /path1/path2
+        http_listener listener(web::http::uri_builder(m_uri).append_path(U("/path1")).to_uri());
+        listener.open().wait();
+        test_http_client client(m_uri);
+        VERIFY_ARE_EQUAL(0, client.open());
+        listener.support([](http_request request) {
+            VERIFY_ARE_EQUAL(U("/path1/path2"), request.request_uri().path());
+            VERIFY_ARE_EQUAL(U("/path2"), request.relative_uri().to_string());
+            request.reply(status_codes::OK).wait();
+        });
+        VERIFY_ARE_EQUAL(0, client.request(methods::GET, U("/path1/path2")));
+        client.next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, client.close());
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, nested_paths_encoding)
+    {
+        // listen on /path1%20/path2%20, request /path1%20/path2%20/path%203
+        http_listener listener(web::http::uri_builder(m_uri).append_path(U("/path1%20/path2%20")).to_uri());
+        listener.open().wait();
+        test_http_client client(m_uri);
+        VERIFY_ARE_EQUAL(0, client.open());
+        listener.support([](http_request request) {
+            VERIFY_ARE_EQUAL(U("/path1%20/path2%20/path3%20"), request.request_uri().path());
+            VERIFY_ARE_EQUAL(U("/path3 "), web::http::uri::decode(request.relative_uri().to_string()));
+            request.reply(status_codes::OK).wait();
+        });
+        VERIFY_ARE_EQUAL(0, client.request(methods::GET, U("/path1%20/path2%20/path3%20")));
+        client.next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+        VERIFY_ARE_EQUAL(0, client.close());
+
+        listener.close().wait();
+    }
+
+    TEST(listener_uri_empty_path)
+    {
+        uri address(U("http://localhost:45678"));
+        http_listener listener(address);
+        listener.open().wait();
+        test_http_client::scoped_client client(address);
+        test_http_client* p_client = client.client();
+
+        listener.support([](http_request request) { request.reply(status_codes::OK); });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/")));
+        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(listener_invalid_encoded_uri)
+    {
+        uri address(U("http://localhost:45678"));
+        http_listener listener(address);
+        listener.open().wait();
+        test_http_client::scoped_client client(address);
+        test_http_client* p_client = client.client();
+
+        listener.support([](http_request request) { request.reply(status_codes::OK); });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/%invalid/uri")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::BadRequest);
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/request_stream_tests.cpp b/Release/tests/functional/http/listener/request_stream_tests.cpp
new file mode 100644 (file)
index 0000000..052f317
--- /dev/null
@@ -0,0 +1,103 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * request_stream_tests.cpp
+ *
+ * Tests cases for streaming HTTP requests with http_listener.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(request_stream_tests)
+{
+    TEST_FIXTURE(uri_address, large_body)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        std::string data_piece("abcdefghijklmnopqrstuvwxyz");
+        std::string send_data;
+        // 26 * 160 is greater than 4k which is the chunk size.
+        for (int i = 0; i < 160; ++i)
+        {
+            send_data.append(data_piece);
+        }
+        size_t length = send_data.size();
+
+        listener.support([&](http_request request) {
+            auto stream = request.body();
+            streams::stringstreambuf strbuf;
+
+            VERIFY_ARE_EQUAL(stream.read_to_end(strbuf).get(), length);
+            VERIFY_ARE_EQUAL(strbuf.collection(), send_data);
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U(""), U("text/plain"), send_data));
+        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, test_chunked_transfer)
+    {
+        const size_t num_bytes = 1024 * 1024 * 10;
+        http_listener listener(m_uri);
+        listener.support([num_bytes](http_request request) { request.reply(status_codes::OK); });
+        listener.open().wait();
+
+        ::http::client::http_client client(m_uri);
+        auto buf = streams::producer_consumer_buffer<uint8_t>();
+        pplx::task<http_response> response =
+            client.request(methods::PUT, U("/"), buf.create_istream(), U("text/plain"));
+
+        const size_t four_mb = 1024 * 1024 * 4;
+        std::vector<uint8_t> buffer;
+        buffer.resize(num_bytes);
+        memset(&buffer[0], (int)'A', num_bytes);
+        size_t start = 0, end;
+        while (start < num_bytes)
+        {
+            end = start + four_mb < num_bytes ? four_mb : num_bytes - start;
+            size_t num_written = buf.putn_nocopy(&buffer[start], end).get();
+            start += num_written;
+        }
+        buf.close(std::ios_base::out).wait();
+
+        response.wait();
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/requests_tests.cpp b/Release/tests/functional/http/listener/requests_tests.cpp
new file mode 100644 (file)
index 0000000..db29359
--- /dev/null
@@ -0,0 +1,278 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * requests_tests.cpp
+ *
+ * Tests cases for covering sending various requests to http_listener.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <cpprest/http_client.h>
+
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(requests_tests)
+{
+    TEST_FIXTURE(uri_address, http_methods)
+    {
+        http_listener listener(m_uri);
+
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // Don't include 'CONNECT' it has a special meaning.
+        utility::string_t send_methods[] = {methods::GET,
+                                            U("GET"),
+                                            methods::DEL,
+                                            methods::HEAD,
+                                            U("HeAd"),
+                                            methods::POST,
+                                            methods::PUT,
+                                            U("CUstomMETHOD")};
+        utility::string_t recv_methods[] = {
+            U("GET"), U("GET"), U("DELETE"), U("HEAD"), U("HEAD"), U("POST"), U("PUT"), U("CUstomMETHOD")};
+        const size_t num_methods = sizeof(send_methods) / sizeof(send_methods[0]);
+
+        utility::string_t actual_method;
+        listener.support([&](http_request request) {
+            actual_method = request.method();
+            request.reply(status_codes::OK).wait();
+        });
+
+        for (int i = 0; i < num_methods; ++i)
+        {
+            pplx::extensibility::event_t ev;
+            VERIFY_ARE_EQUAL(0, p_client->request(send_methods[i], U("")));
+            p_client->next_response()
+                .then([&ev](test_response* p_response) {
+                    http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+                    ev.set();
+                })
+                .wait();
+            VERIFY_ARE_EQUAL(recv_methods[i], actual_method);
+            ev.wait();
+        }
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, http_body_and_body_size)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // request with no body
+        listener.support([](http_request request) {
+            http_asserts::assert_request_equals(request, U("GET"), U("/"));
+            VERIFY_ARE_EQUAL(0, request.body().streambuf().in_avail());
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        // request with body size explicitly 0
+        listener.support([](http_request request) {
+            http_asserts::assert_request_equals(request, U("GET"), U("/"));
+            VERIFY_ARE_EQUAL(0, request.body().streambuf().in_avail());
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U(""), ""));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        // request with body data
+        std::string data("HEHE");
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, U("GET"), U("/"));
+
+            auto stream = request.body();
+            VERIFY_IS_TRUE(stream.is_valid());
+            auto buf = stream.streambuf();
+            VERIFY_IS_TRUE(buf);
+
+            request.content_ready().wait();
+
+            VERIFY_ARE_EQUAL(data.size(), buf.in_avail());
+            VERIFY_ARE_EQUAL('H', (char)buf.sbumpc());
+            VERIFY_ARE_EQUAL('E', (char)buf.sbumpc());
+            VERIFY_ARE_EQUAL('H', (char)buf.sbumpc());
+            VERIFY_ARE_EQUAL('E', (char)buf.sbumpc());
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U(""), data));
+        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, large_body)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        std::string data_piece("abcdefghijklmnopqrstuvwxyz");
+        std::string send_data;
+        // 26 * 160 is greater than 4k which is the chunk size.
+        for (int i = 0; i < 160; ++i)
+        {
+            send_data.append(data_piece);
+        }
+        listener.support([&](http_request request) {
+            std::string recv_data = utility::conversions::to_utf8string(request.extract_string().get());
+            VERIFY_ARE_EQUAL(send_data, recv_data);
+            request.reply(status_codes::OK);
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U(""), U("text/plain"), send_data));
+        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_order)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+
+        client::http_client_config config;
+        // our product client would be able to pipe multiple requests on one connection
+        client::http_client client(m_uri, config);
+
+        const int num_requests = 50;
+
+        listener.support([](http_request request) {
+            auto str = request.extract_string().get();
+            // intentionally break order
+            if (str == U("0")) tests::common::utilities::os_utilities::sleep(500);
+            request.reply(status_codes::OK, str);
+        });
+
+        std::vector<pplx::task<web::http::http_response>> responses;
+
+        for (int i = 0; i < num_requests; ++i)
+        {
+            utility::ostringstream_t ss;
+            ss << i;
+            responses.push_back(client.request(web::http::methods::PUT, U(""), ss.str()));
+        }
+
+        // wait for requests.
+        for (size_t i = 0; i < num_requests; ++i)
+        {
+            utility::ostringstream_t ss;
+            ss << i;
+            auto response = responses[i].get();
+
+            // verify the requests and responses are still match
+            VERIFY_ARE_EQUAL(response.status_code(), status_codes::OK);
+            VERIFY_ARE_EQUAL(response.extract_string().get(), ss.str());
+        }
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, uri_encoding, "Ignore", "Codeplex 201")
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        client::http_client client(m_uri);
+        utility::string_t encoded_uri;
+
+        listener.support([&](http_request request) {
+            VERIFY_ARE_EQUAL(encoded_uri, request.relative_uri().to_string());
+            request.reply(status_codes::OK);
+        });
+
+        // Wrap in try catch to print out more information to help with a sporadic failure.
+        try
+        {
+            encoded_uri = uri::encode_uri(U("/path 1/path 2")); // Path component contains encoded characters
+            client.request(methods::GET, encoded_uri).wait();
+            encoded_uri = uri::encode_uri(
+                U("/test?Text=J'ai besoin de trouver un personnage")); // Query string contains encoded characters
+            client.request(methods::GET, encoded_uri).wait();
+            encoded_uri = uri::encode_uri(U("/path 1/path 2#fragment1")); // URI has path and fragment components
+            client.request(methods::GET, encoded_uri).wait();
+            encoded_uri = uri::encode_uri(
+                U("/path 1/path 2?key1=val1 val2#fragment1")); // URI has path, query and fragment components
+            client.request(methods::GET, encoded_uri).wait();
+        }
+        catch (const http_exception& e)
+        {
+            std::cout << "http_exception caught" << std::endl
+                      << "what():" << e.what() << std::endl
+                      << "error_code msg:" << e.error_code().message() << std::endl
+                      << "error_code value:" << e.error_code().value() << std::endl;
+            VERIFY_IS_TRUE(false);
+        }
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, https_listener, "Ignore", "Manual")
+    {
+        // Requires a certificate for execution.
+        // Here are instructions for creating a self signed cert. Full instructions can be located here:
+        // http://blogs.msdn.com/b/haoxu/archive/2009/04/30/one-time-set-up-for-wwsapi-security-examples.aspx
+        // From an elevated admin prompt:
+        // 1. MakeCert.exe -ss Root -sr LocalMachine -n "CN=Fake-Test-CA" -cy authority -r -sk "CAKeyContainer"
+        // 2. MakeCert.exe -ss My -sr LocalMachine -n "CN=localhost" -sky exchange -is Root -ir LocalMachine -in
+        // Fake-Test-CA -sk "ServerKeyContainer"
+        // 3. Find corresponding SHA-1 hash with CertUtil.exe -store My localhost
+        // 4. Netsh.exe http add sslcert ipport=0.0.0.0:8443 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
+        // certhash=<40CharacterThumbprintWithNoSpaces>
+
+        http_listener listener(m_secure_uri);
+        listener.open().wait();
+        client::http_client client(m_secure_uri);
+
+        listener.support([&](http_request request) { request.reply(status_codes::OK); });
+
+        http_asserts::assert_response_equals(client.request(methods::GET, U("")).get(), status_codes::OK);
+
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/response_stream_tests.cpp b/Release/tests/functional/http/listener/response_stream_tests.cpp
new file mode 100644 (file)
index 0000000..a16a9fd
--- /dev/null
@@ -0,0 +1,316 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * response_stream_tests.cpp
+ *
+ * Tests cases for streaming with HTTP response with http_listener.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "cpprest/rawptrstream.h"
+
+using namespace web;
+using namespace utility;
+using namespace concurrency;
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::common::utilities;
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(response_stream_tests)
+{
+    // Used to prepare data for read tests
+    void fill_file(const utility::string_t& name, size_t repetitions = 1)
+    {
+        std::fstream stream(name, std::ios_base::out | std::ios_base::trunc);
+
+        for (size_t i = 0; i < repetitions; i++)
+            stream << "abcdefghijklmnopqrstuvwxyz";
+    }
+
+    TEST_FIXTURE(uri_address, set_body_stream_small)
+    {
+        utility::string_t fname = U("set_response_stream_small.txt");
+        fill_file(fname);
+
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // Try sending data straight from a file.
+        http_response response(status_codes::OK);
+
+        auto stream = streams::file_stream<uint8_t>::open_istream(fname).get();
+        response.set_body(stream);
+
+        auto length = stream.seek(0, std::ios_base::end);
+        stream.seek(0);
+
+        response.headers().set_content_type(U("text/plain; charset=utf-8"));
+        response.headers().set_content_length((size_t)length);
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            request.reply(response).wait();
+        });
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(
+                    p_response, status_codes::OK, U("text/plain; charset=utf-8"), U("abcdefghijklmnopqrstuvwxyz"));
+            })
+            .wait();
+
+        stream.close().get();
+    }
+
+    TEST_FIXTURE(uri_address, set_body_stream_large)
+    {
+        utility::string_t fname = U("set_response_stream_large.txt");
+        fill_file(fname, 200);
+
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // Try sending data straight from a file.
+        http_response response(status_codes::OK);
+
+        auto stream = streams::file_stream<uint8_t>::open_istream(fname).get();
+        response.set_body(stream);
+
+        auto length = stream.seek(0, std::ios_base::end);
+        stream.seek(0);
+
+        response.headers().set_content_type(U("text/plain; charset=utf-8"));
+        response.headers().set_content_length((size_t)length);
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            request.reply(response).wait();
+        });
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+                VERIFY_ARE_EQUAL((size_t)length, p_response->m_data.size());
+            })
+            .wait();
+
+        stream.close().get();
+    }
+
+    TEST_FIXTURE(uri_address, set_body_stream_partial)
+    {
+        utility::string_t fname = U("set_response_stream_partial.txt");
+        fill_file(fname, 200);
+
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // Try sending data straight from a file.
+        http_response response(status_codes::OK);
+
+        auto stream = streams::file_stream<uint8_t>::open_istream(fname).get();
+        response.set_body(stream);
+
+        response.headers().set_content_type(U("text/plain; charset=utf-8"));
+        response.headers().set_content_length(4500);
+
+        // We shouldn't be sending more than the content-length.
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            request.reply(response).wait();
+        });
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+                VERIFY_ARE_EQUAL(4500, p_response->m_data.size());
+            })
+            .wait();
+
+        // We should only have read the first 4500 bytes.
+        auto length = stream.seek(0, std::ios_base::cur);
+        VERIFY_ARE_EQUAL((size_t)length, (size_t)4500);
+
+        stream.close().get();
+    }
+
+    TEST_FIXTURE(uri_address, set_body_filestream_chunked)
+    {
+        utility::string_t fname = U("set_response_stream_chunked.txt");
+        fill_file(fname, 200);
+
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // Try sending data straight from a file.
+        http_response response(status_codes::OK);
+
+        auto stream = streams::file_stream<uint8_t>::open_istream(fname).get();
+        response.set_body(stream);
+
+        auto length = stream.seek(0, std::ios_base::end);
+        stream.seek(0);
+
+        response.headers().set_content_type(U("text/plain; charset=utf-8"));
+        // Not setting the content length forces "transfer-encoding: chunked"
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+            request.reply(response).wait();
+        });
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+                VERIFY_ARE_EQUAL((size_t)length, p_response->m_data.size());
+            })
+            .wait();
+
+        stream.close().get();
+    }
+
+    TEST_FIXTURE(uri_address, set_body_memorystream_chunked)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // Try sending data straight from a file.
+        http_response response(status_codes::OK);
+
+        std::string text1 = "This is a test";
+        size_t length = text1.size();
+
+        response.headers().set_content_type(U("text/plain; charset=utf-8"));
+        // Not setting the content length forces "transfer-encoding: chunked"
+
+        listener.support([&](http_request request) {
+            http_asserts::assert_request_equals(request, methods::POST, U("/"));
+
+            streams::producer_consumer_buffer<char> rwbuf;
+
+            streams::basic_istream<uint8_t> stream(rwbuf);
+            response.set_body(stream);
+
+            auto rep = request.reply(response);
+
+            os_utilities::sleep(100);
+
+            rwbuf.putn_nocopy(&text1[0], length).wait();
+            rwbuf.putn_nocopy(&text1[0], length).wait();
+            rwbuf.sync().wait();
+            rwbuf.putn_nocopy(&text1[0], length).wait();
+            rwbuf.close(std::ios_base::out).wait();
+
+            rep.wait();
+        });
+
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::POST, U("")));
+        p_client->next_response()
+            .then([&](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+                VERIFY_ARE_EQUAL((size_t)length * 3, p_response->m_data.size());
+            })
+            .wait();
+    }
+
+    TEST_FIXTURE(uri_address, reply_transfer_encoding_4k)
+    {
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+
+        streams::container_buffer<std::vector<uint8_t>> buf;
+
+        // Write 4K - the exact internal chunk size
+        unsigned char ptr[4 * 1024] = {'a', 'b', 'c'};
+        VERIFY_ARE_EQUAL(buf.putn_nocopy(ptr, sizeof(ptr)).get(), sizeof(ptr));
+
+        listener.support([&buf](http_request request) {
+            // Ensure that it is transfer-encoded
+            auto collection = buf.collection();
+            streams::container_buffer<std::vector<uint8_t>> buf2(std::move(collection), std::ios_base::in);
+            request.reply(200, streams::istream(buf2), U("text/plain"));
+            buf.close(std::ios_base::out);
+        });
+
+        {
+            ::http::client::http_client client(m_uri);
+            http_request msg(methods::GET);
+
+            // Wait for headers
+            auto resp = client.request(msg).get();
+
+            // Wait for data
+            resp.content_ready().wait();
+
+            // Now verify that we've got the right data
+            auto s = resp.extract_string().get();
+            VERIFY_ARE_EQUAL(s.c_str(), U("abc"));
+        }
+        listener.close().wait();
+    }
+
+    // Fails sporadically, Codeplex #158
+    TEST_FIXTURE(uri_address, reply_chunked_4k, "Ignore", "Codeplex 158")
+    {
+        web::http::experimental::listener::http_listener listener(m_uri);
+        listener.open().wait();
+
+        streams::producer_consumer_buffer<uint8_t> buf;
+
+        // Write 4K - the exact internal chunk size
+        unsigned char ptr[4 * 1024];
+        VERIFY_ARE_EQUAL(buf.putn_nocopy(ptr, sizeof(ptr)).get(), sizeof(ptr));
+        buf.close(std::ios_base::out);
+
+        listener.support([&buf](http_request request) {
+            // Ensure that it is transfer-encoded
+            request.reply(200, streams::istream(buf), 4096, U("text/plain"));
+        });
+
+        {
+            ::http::client::http_client client(m_uri);
+            http_request msg(methods::GET);
+
+            // Wait for headers
+            auto resp = client.request(msg).get();
+
+            // Wait for data
+            resp.content_ready().wait();
+        }
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/status_code_reason_phrase_tests.cpp b/Release/tests/functional/http/listener/status_code_reason_phrase_tests.cpp
new file mode 100644 (file)
index 0000000..b555a82
--- /dev/null
@@ -0,0 +1,107 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * status_code_reason_phrase_tests.cpp
+ *
+ * Tests cases for using HTTP status codes and reason phrases with http_listener.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(status_code_reason_phrase_tests)
+{
+    TEST_FIXTURE(uri_address, status_codes)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // known status code
+        listener.support([&](http_request request) { request.reply(status_codes::Conflict).wait(); });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U("")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::Conflict);
+            })
+            .wait();
+
+        // user defined status code
+        listener.support([&](http_request request) { request.reply(867).wait(); });
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::PUT, U("")));
+        p_client->next_response()
+            .then([](test_response* p_response) { http_asserts::assert_test_response_equals(p_response, 867); })
+            .wait();
+
+        listener.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, reason_phrase)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // standard status code, no reason phrase
+        listener.support([](http_request request) { request.reply(status_codes::NotModified).wait(); });
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::PUT, U("")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::NotModified);
+                VERIFY_ARE_EQUAL(U("Not Modified"), p_response->m_reason_phrase);
+            })
+            .wait();
+
+        // standard status code, with reason phrase
+        listener.support([](http_request request) {
+            http_response response(status_codes::NotModified);
+            response.set_reason_phrase(U("Custom"));
+            request.reply(response).wait();
+        });
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::PUT, U("")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::NotModified);
+                VERIFY_ARE_EQUAL(U("Custom"), p_response->m_reason_phrase);
+            })
+            .wait();
+
+        // non standard status code, no reason phrase
+        listener.support([](http_request request) { request.reply(987); });
+        VERIFY_ARE_EQUAL(0u, p_client->request(methods::PUT, U("")));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, 987);
+                VERIFY_ARE_EQUAL(U(""), p_response->m_reason_phrase);
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/listener/stdafx.cpp b/Release/tests/functional/http/listener/stdafx.cpp
new file mode 100644 (file)
index 0000000..b17ac3d
--- /dev/null
@@ -0,0 +1,8 @@
+// stdafx.cpp :
+// Include the standard header and generate the precompiled header.
+
+#include "stdafx.h"
+
+#if WIN32
+__declspec(dllexport) int httplistener_test_generate_lib = 0;
+#endif
diff --git a/Release/tests/functional/http/listener/stdafx.h b/Release/tests/functional/http/listener/stdafx.h
new file mode 100644 (file)
index 0000000..26e4636
--- /dev/null
@@ -0,0 +1,27 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * stdafx.h
+ *
+ * Pre-compiled headers
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/filestream.h"
+#include "cpprest/http_client.h"
+#include "cpprest/http_listener.h"
+#include "cpprest/producerconsumerstream.h"
+#include "http_listener_tests.h"
+#include "http_test_utilities.h"
+#include "os_utilities.h"
+#include "unittestpp.h"
+#include <fstream>
diff --git a/Release/tests/functional/http/listener/to_string_tests.cpp b/Release/tests/functional/http/listener/to_string_tests.cpp
new file mode 100644 (file)
index 0000000..726a690
--- /dev/null
@@ -0,0 +1,84 @@
+/***
+ * ==++==
+ *
+ * Copyright (c) Microsoft Corporation.  All rights reserved.
+ *
+ * ==--==
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * to_string_tests.cpp
+ *
+ * Tests cases for to_string on HTTP requests/responses with http_listener.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web::http;
+using namespace web::http::experimental::listener;
+
+using namespace tests::common::utilities;
+using namespace tests::functional::http::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace listener
+{
+SUITE(to_string_tests)
+{
+    TEST_FIXTURE(uri_address, response_to_string)
+    {
+        // to string
+        http_response resp(status_codes::PartialContent);
+        resp.set_body(U("data"));
+        VERIFY_ARE_EQUAL(U("HTTP/1.1 206 Partial Content\r\nContent-Length: 4\r\nContent-Type: text/plain; ")
+                         U("charset=utf-8\r\n\r\ndata"),
+                         resp.to_string());
+    }
+
+    TEST_FIXTURE(uri_address, request_to_string)
+    {
+        http_listener listener(m_uri);
+        listener.open().wait();
+
+        test_http_client::scoped_client client(m_uri);
+        test_http_client* p_client = client.client();
+
+        // to_string
+        std::string data("hehehe");
+        listener.support([&](http_request request) {
+            std::map<utility::string_t, utility::string_t> expected_headers;
+            expected_headers[U("Connection")] = U("Keep-Alive");
+            expected_headers[U("Content-Length")] = U("6");
+            expected_headers[U("Content-Type")] = U("text/plain");
+            expected_headers[U("Host")] = U("localhost:34567");
+            expected_headers[U("User-Agent")] = U("test_http_client");
+
+            // maybe to_string() should wait for the request to complete?
+            // in the mean time...
+            request.content_ready().wait();
+
+            http_asserts::assert_request_string_equals(
+                request.to_string(), U("GET"), U("/pa%20th1"), U("HTTP/1.1"), expected_headers, U("hehehe"));
+            request.reply(status_codes::OK).wait();
+        });
+        VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("pa%20th1"), U("text/plain"), data));
+        p_client->next_response()
+            .then([](test_response* p_response) {
+                http_asserts::assert_test_response_equals(p_response, status_codes::OK);
+            })
+            .wait();
+
+        listener.close().wait();
+    }
+}
+
+} // namespace listener
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/utilities/CMakeLists.txt b/Release/tests/functional/http/utilities/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3edf329
--- /dev/null
@@ -0,0 +1,20 @@
+set(SOURCES
+  http_asserts.cpp
+  test_http_client.cpp
+  test_http_server.cpp
+  test_server_utilities.cpp
+)
+
+add_library(httptest_utilities ${SOURCES})
+if(WIN32)
+  target_compile_definitions(httptest_utilities PRIVATE -DHTTPTESTUTILITY_EXPORTS)
+endif()
+target_include_directories(httptest_utilities PUBLIC include)
+target_link_libraries(httptest_utilities PUBLIC
+  cpprest
+  unittestpp
+  common_utilities
+)
+if(WINDOWS_STORE)
+  target_compile_options(httptest_utilities PRIVATE /DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP)
+endif()
diff --git a/Release/tests/functional/http/utilities/http_asserts.cpp b/Release/tests/functional/http/utilities/http_asserts.cpp
new file mode 100644 (file)
index 0000000..7dc626d
--- /dev/null
@@ -0,0 +1,312 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * http_asserts.cpp - Utility class to help verify assertions about http requests and responses.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+using namespace utility::conversions;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace utilities
+{
+utility::string_t percent_encode_pound(utility::string_t str)
+{
+    size_t index;
+    while ((index = str.find_first_of(U("#"))) != str.npos)
+    {
+        str.insert(index, U("%23"));
+        str.erase(index + 3, 1);
+    }
+    return str;
+}
+
+// Helper function to verify all given headers are present.
+template<typename T1, typename T2>
+static void verify_headers(const T1& expected, const T2& actual)
+{
+    for (auto iter = expected.begin(); iter != expected.end(); ++iter)
+    {
+        VERIFY_ARE_EQUAL(iter->second, actual.find(iter->first)->second);
+    }
+}
+
+void http_asserts::assert_request_equals(::http::http_request request,
+                                         const ::http::method& mtd,
+                                         const utility::string_t& relative_path)
+{
+    VERIFY_ARE_EQUAL(mtd, request.method());
+    if (relative_path == U(""))
+    {
+        VERIFY_ARE_EQUAL(U("/"), request.relative_uri().to_string());
+    }
+    else
+    {
+        VERIFY_ARE_EQUAL(relative_path, request.relative_uri().to_string());
+    }
+}
+
+void http_asserts::assert_request_equals(::http::http_request request,
+                                         const ::http::method& mtd,
+                                         const utility::string_t& relative_uri,
+                                         const std::map<utility::string_t, utility::string_t>& headers)
+{
+    assert_request_equals(request, mtd, relative_uri);
+    verify_headers(headers, request.headers());
+}
+
+void http_asserts::assert_request_equals(::http::http_request request,
+                                         const ::http::method& mtd,
+                                         const utility::string_t& relative_path,
+                                         const utility::string_t& body)
+{
+    assert_request_equals(request, mtd, relative_path);
+    auto request_data = request.extract_string().get();
+    VERIFY_ARE_EQUAL(body, request_data);
+}
+
+void http_asserts::assert_response_equals(::http::http_response response, const ::http::status_code& code)
+{
+    VERIFY_ARE_EQUAL(response.status_code(), code);
+}
+
+void http_asserts::assert_response_equals(::http::http_response response,
+                                          const ::http::status_code& code,
+                                          const utility::string_t& reason)
+{
+    VERIFY_ARE_EQUAL(code, response.status_code());
+    VERIFY_ARE_EQUAL(reason, response.reason_phrase());
+}
+
+void http_asserts::assert_response_equals(::http::http_response response,
+                                          const ::http::status_code& code,
+                                          const std::map<utility::string_t, utility::string_t>& headers)
+{
+    VERIFY_ARE_EQUAL(code, response.status_code());
+    verify_headers(headers, response.headers());
+}
+
+void http_asserts::assert_http_headers_equals(const ::http::http_headers& actual, const ::http::http_headers& expected)
+{
+    verify_headers(actual, expected);
+}
+
+void http_asserts::assert_test_request_equals(const test_request* const p_request,
+                                              const ::http::method& mtd,
+                                              const utility::string_t& path)
+{
+    VERIFY_ARE_EQUAL(mtd, p_request->m_method);
+    VERIFY_ARE_EQUAL(path, p_request->m_path);
+}
+
+void http_asserts::assert_test_request_equals(const test_request* const p_request,
+                                              const ::http::method& mtd,
+                                              const utility::string_t& path,
+                                              const utility::string_t& content_type)
+{
+    VERIFY_ARE_EQUAL(mtd, p_request->m_method);
+    VERIFY_ARE_EQUAL(path, p_request->m_path);
+
+    // verify that content-type key exists in the header and the value matches the one provided
+    auto iter = p_request->m_headers.find(U("Content-Type"));
+    if (content_type.empty())
+    {
+        VERIFY_ARE_EQUAL(iter, p_request->m_headers.end());
+    }
+    else
+    {
+        VERIFY_IS_TRUE(iter != p_request->m_headers.end());
+        VERIFY_ARE_EQUAL(iter->second.find(content_type), 0);
+    }
+}
+
+void http_asserts::assert_test_request_contains_headers(const test_request* const p_request,
+                                                        const ::http::http_headers& headers)
+{
+    verify_headers(headers, p_request->m_headers);
+}
+
+void http_asserts::assert_test_request_contains_headers(const test_request* const p_request,
+                                                        const std::map<utility::string_t, utility::string_t>& headers)
+{
+    verify_headers(headers, p_request->m_headers);
+}
+
+// Helper function to parse HTTP headers from a stringstream.
+static std::map<utility::string_t, utility::string_t> parse_headers(utility::istringstream_t& ss)
+{
+    // Keep parsing until CRLF is encountered.
+    std::map<utility::string_t, utility::string_t> headers;
+    utility::string_t header_line;
+    while (getline(ss, header_line).good())
+    {
+        const size_t colon_index = header_line.find(U(":"));
+        const utility::string_t header_name = header_line.substr(0, colon_index);
+        utility::string_t header_value = header_line.substr(colon_index + 1);
+        tests::functional::http::utilities::trim_whitespace(header_value);
+        headers[header_name] = header_value;
+
+        char c1 = (char)ss.get(), c2 = (char)ss.get();
+        if (c1 == '\r' && c2 == '\n')
+        {
+            break;
+        }
+        ss.unget();
+        ss.unget();
+    }
+    return headers;
+}
+
+void http_asserts::assert_request_string_equals(const utility::string_t& request,
+                                                const ::http::method& mtd,
+                                                const utility::string_t& path,
+                                                const utility::string_t& version,
+                                                const std::map<utility::string_t, utility::string_t>& headers,
+                                                const utility::string_t& body)
+{
+    utility::istringstream_t ss(request);
+
+    // Parse request line.
+    utility::string_t actual_method, actual_path, actual_version;
+    ss >> actual_method >> actual_path >> actual_version;
+
+    // Parse headers.
+    utility::string_t temp;
+    getline(ss, temp);
+    std::map<utility::string_t, utility::string_t> actual_headers = parse_headers(ss);
+
+    // Parse in any message body
+    utility::string_t actual_body = ss.str().substr((size_t)ss.tellg());
+
+    VERIFY_ARE_EQUAL(mtd, actual_method);
+    VERIFY_ARE_EQUAL(path, actual_path);
+    VERIFY_ARE_EQUAL(version, actual_version);
+    verify_headers(headers, actual_headers);
+    VERIFY_ARE_EQUAL(body, actual_body);
+}
+
+void http_asserts::assert_response_string_equals(const utility::string_t& response,
+                                                 const utility::string_t& version,
+                                                 const ::http::status_code& code,
+                                                 const utility::string_t& phrase,
+                                                 const std::map<utility::string_t, utility::string_t>& headers,
+                                                 const utility::string_t& body)
+{
+    utility::istringstream_t ss(response);
+
+    // Parse response line.
+    utility::string_t actual_version, actual_phrase;
+    ::http::status_code actual_code;
+    ss >> actual_version >> actual_code >> actual_phrase;
+
+    // Parse headers.
+    utility::string_t temp;
+    getline(ss, temp);
+    std::map<utility::string_t, utility::string_t> actual_headers = parse_headers(ss);
+
+    // Prase in any message body.
+    utility::string_t actual_body = ss.str().substr((size_t)ss.tellg());
+
+    VERIFY_ARE_EQUAL(version, actual_version);
+    VERIFY_ARE_EQUAL(code, actual_code);
+    VERIFY_ARE_EQUAL(phrase, actual_phrase);
+    verify_headers(headers, actual_headers);
+    VERIFY_ARE_EQUAL(body, actual_body);
+}
+
+void http_asserts::assert_test_request_equals(const test_request* const p_request,
+                                              const ::http::method& mtd,
+                                              const utility::string_t& path,
+                                              const utility::string_t& content_type,
+                                              const utility::string_t& body)
+{
+    assert_test_request_equals(p_request, mtd, path, content_type);
+    // Textual response is always sent as UTF-8, hence the converison to string_t
+    std::string s((char*)&p_request->m_body[0], p_request->m_body.size());
+    utility::string_t extracted_body = to_string_t(s);
+
+    VERIFY_ARE_EQUAL(body, extracted_body);
+}
+
+void http_asserts::assert_test_response_equals(const test_response* const p_response, const ::http::status_code& code)
+{
+    VERIFY_ARE_EQUAL(code, p_response->m_status_code);
+}
+
+void http_asserts::assert_test_response_equals(const test_response* const p_response,
+                                               const ::http::status_code& code,
+                                               const std::map<utility::string_t, utility::string_t>& headers)
+{
+    VERIFY_ARE_EQUAL(code, p_response->m_status_code);
+    verify_headers(headers, p_response->m_headers);
+}
+
+void http_asserts::assert_test_response_equals(const test_response* const p_response,
+                                               const ::http::status_code& code,
+                                               const ::http::http_headers& headers)
+{
+    VERIFY_ARE_EQUAL(code, p_response->m_status_code);
+    verify_headers(headers, p_response->m_headers);
+}
+
+void http_asserts::assert_test_response_equals(test_response* p_response,
+                                               const ::http::status_code& code,
+                                               const utility::string_t& content_type)
+{
+    VERIFY_ARE_EQUAL(code, p_response->m_status_code);
+    utility::string_t found_content;
+    p_response->match_header(U("Content-Type"), found_content);
+    VERIFY_ARE_EQUAL(content_type, found_content);
+}
+
+void http_asserts::assert_test_response_equals(test_response* p_response,
+                                               const ::http::status_code& code,
+                                               const utility::string_t& content_type,
+                                               const utility::string_t data)
+{
+    VERIFY_ARE_EQUAL(code, p_response->m_status_code);
+    utility::string_t found_content;
+    p_response->match_header(U("Content-Type"), found_content);
+    VERIFY_ARE_EQUAL(found_content.find(content_type), 0);
+
+    // Beware: what kind of string this is? <-- stringhack until we tighten up wide/narrow string business
+    utility::string_t extracted_body;
+    if (p_response->m_data.size() == 0)
+    {
+        extracted_body = U("");
+    }
+    else
+    {
+        auto actualRawData = (char*)&p_response->m_data[0];
+        if (p_response->m_data.size() > 1 && *(actualRawData + 1) == '\0')
+        {
+            // We have more than one byte of data, but it's null-terminated at byte 1.
+            // Therefore, this is a wide string
+            extracted_body.assign((utility::char_t*)actualRawData, p_response->m_data.size() / sizeof(utility::char_t));
+        }
+        else
+        {
+            std::string s(actualRawData, p_response->m_data.size());
+            extracted_body = to_string_t(s);
+        }
+    }
+
+    VERIFY_ARE_EQUAL(data, extracted_body);
+}
+
+} // namespace utilities
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/utilities/include/http_asserts.h b/Release/tests/functional/http/utilities/include/http_asserts.h
new file mode 100644 (file)
index 0000000..79aee00
--- /dev/null
@@ -0,0 +1,238 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * http_asserts.h - Utility class to help verify assertions about http requests and responses.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "http_test_utilities_public.h"
+#include "test_http_client.h"
+#include "test_http_server.h"
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace utilities
+{
+template<class Char>
+void trim_whitespace(std::basic_string<Char>& 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);
+}
+
+/// <summary>
+/// Helper function to do percent encoding of just the '#' character, when running under WinRT.
+/// The WinRT http client implementation performs percent encoding on the '#'.
+/// </summary>
+TEST_UTILITY_API utility::string_t __cdecl percent_encode_pound(utility::string_t str);
+
+/// <summary>
+/// Static class containing various http request and response asserts.
+/// </summary>
+class http_asserts
+{
+public:
+    /// <summary>
+    /// Asserts that the specified request is equal to given arguments.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_request_equals(web::http::http_request request,
+                                                               const web::http::method& mtd,
+                                                               const utility::string_t& relative_uri);
+
+    TEST_UTILITY_API static void __cdecl assert_request_equals(
+        web::http::http_request request,
+        const web::http::method& mtd,
+        const utility::string_t& relative_uri,
+        const std::map<utility::string_t, utility::string_t>& headers);
+
+    TEST_UTILITY_API static void __cdecl assert_request_equals(web::http::http_request request,
+                                                               const web::http::method& mtd,
+                                                               const utility::string_t& relative_uri,
+                                                               const utility::string_t& body);
+
+    /// <summary>
+    /// Asserts that the specified response is equal to given arguments.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_response_equals(web::http::http_response response,
+                                                                const web::http::status_code& code);
+
+    TEST_UTILITY_API static void __cdecl assert_response_equals(web::http::http_response response,
+                                                                const web::http::status_code& code,
+                                                                const utility::string_t& reason);
+
+    TEST_UTILITY_API static void __cdecl assert_response_equals(
+        web::http::http_response response,
+        const web::http::status_code& code,
+        const std::map<utility::string_t, utility::string_t>& headers);
+
+    /// <summary>
+    /// Asserts the given http_headers contains the given values.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_http_headers_equals(const web::http::http_headers& actual,
+                                                                    const web::http::http_headers& expected);
+
+    /// <summary>
+    /// Asserts the specified test_request is equal to its arguments.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_test_request_equals(const test_request* const p_request,
+                                                                    const web::http::method& mtd,
+                                                                    const utility::string_t& path);
+
+    /// <summary>
+    /// Asserts the specified test_request is equal to its arguments.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_test_request_equals(const test_request* const p_request,
+                                                                    const web::http::method& mtd,
+                                                                    const utility::string_t& path,
+                                                                    const utility::string_t& content_type);
+
+    /// <summary>
+    /// Asserts the specified test_request is equal to its arguments.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_test_request_contains_headers(const test_request* const p_request,
+                                                                              const web::http::http_headers& headers);
+
+    /// <summary>
+    /// Asserts the specified test_request is equal to its arguments.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_test_request_contains_headers(
+        const test_request* const p_request, const std::map<utility::string_t, utility::string_t>& headers);
+
+    /// <summary>
+    /// Asserts the given HTTP request string is equal to its arguments.
+    /// NOTE: this function only makes sure the specified headers exist, not that they are the only ones.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_request_string_equals(
+        const utility::string_t& request,
+        const web::http::method& mtd,
+        const utility::string_t& path,
+        const utility::string_t& version,
+        const std::map<utility::string_t, utility::string_t>& headers,
+        const utility::string_t& body);
+
+    /// <summary>
+    /// Asserts the given HTTP response string is equal to its arguments.
+    /// NOTE: this function only makes sure the specified headers exist, not that they are the only ones.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_response_string_equals(
+        const utility::string_t& response,
+        const utility::string_t& version,
+        const web::http::status_code& code,
+        const utility::string_t& phrase,
+        const std::map<utility::string_t, utility::string_t>& headers,
+        const utility::string_t& body);
+
+    /// <summary>
+    /// Asserts the specified test_request is equal to its arguments.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_test_request_equals(const test_request* const p_request,
+                                                                    const web::http::method& mtd,
+                                                                    const utility::string_t& path,
+                                                                    const utility::string_t& content_type,
+                                                                    const utility::string_t& body);
+
+    /// <summary>
+    /// Asserts the specified test_response is equal to its arguments.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl assert_test_response_equals(const test_response* const p_response,
+                                                                     const web::http::status_code& code);
+
+    TEST_UTILITY_API static void __cdecl assert_test_response_equals(
+        const test_response* const p_response,
+        const web::http::status_code& code,
+        const std::map<utility::string_t, utility::string_t>& headers);
+
+    TEST_UTILITY_API static void __cdecl assert_test_response_equals(const test_response* const p_response,
+                                                                     const web::http::status_code& code,
+                                                                     const web::http::http_headers& headers);
+
+    TEST_UTILITY_API static void __cdecl assert_test_response_equals(test_response* p_response,
+                                                                     const web::http::status_code& code,
+                                                                     const utility::string_t& content_type);
+
+    TEST_UTILITY_API static void __cdecl assert_test_response_equals(test_response* p_response,
+                                                                     const web::http::status_code& code,
+                                                                     const utility::string_t& content_type,
+                                                                     const utility::string_t data);
+
+private:
+    http_asserts() {}
+    ~http_asserts() {}
+};
+
+#if defined(_WIN32)
+#if _MSC_VER >= 1900
+#include <winapifamily.h>
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
+// For IXMLHttpRequest with Windows 10, the error codes don't directly compare equal anymore.
+// Relax verification for now.
+#define HTTP_ERROR_CHECK_IMPL(__code)
+#else
+#define HTTP_ERROR_CHECK_IMPL(__code)                                                                                  \
+    if (__code != _exc.error_code())                                                                                   \
+    {                                                                                                                  \
+        VERIFY_IS_TRUE(false, "Unexpected error code encountered.");                                                   \
+    }
+#endif
+#else
+// The reason we can't directly compare with the given std::errc code is because
+// on Windows the STL implementation of error categories are NOT unique across
+// dll boundaries, until VS2015.
+#define HTTP_ERROR_CHECK_IMPL(__code)                                                                                  \
+    VERIFY_ARE_EQUAL(static_cast<int>(__code), _exc.error_code().default_error_condition().value());
+#endif
+#else
+#define HTTP_ERROR_CHECK_IMPL(__code) VERIFY_ARE_EQUAL(_exc.error_code(), __code, "Unexpected error code encountered.")
+#endif
+
+// Helper function to verify http_exception is thrown with correct error code
+#define VERIFY_THROWS_HTTP_ERROR_CODE(__expression, __code)                                                            \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    try                                                                                                                \
+    {                                                                                                                  \
+        __expression;                                                                                                  \
+        UnitTest::CurrentTest::Results()->OnTestFailure(                                                               \
+            UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__),                                        \
+            "Expected exception: \"web::http::http_exception\" not thrown");                                           \
+    }                                                                                                                  \
+    catch (const web::http::http_exception& _exc)                                                                      \
+    {                                                                                                                  \
+        VERIFY_IS_TRUE(std::string(_exc.what()).size() > 0);                                                           \
+        HTTP_ERROR_CHECK_IMPL(__code);                                                                                 \
+    }                                                                                                                  \
+    catch (const std::exception& _exc)                                                                                 \
+    {                                                                                                                  \
+        std::string _msg("(" #__expression ") threw exception: ");                                                     \
+        _msg.append(_exc.what());                                                                                      \
+        UnitTest::CurrentTest::Results()->OnTestFailure(                                                               \
+            UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), _msg.c_str());                         \
+    }                                                                                                                  \
+    catch (...)                                                                                                        \
+    {                                                                                                                  \
+        std::string _msg("(" #__expression ") threw exception: <...>");                                                \
+        UnitTest::CurrentTest::Results()->OnTestFailure(                                                               \
+            UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), _msg.c_str());                         \
+    }                                                                                                                  \
+    UNITTEST_MULTILINE_MACRO_END
+
+} // namespace utilities
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/utilities/include/http_test_utilities.h b/Release/tests/functional/http/utilities/include/http_test_utilities.h
new file mode 100644 (file)
index 0000000..2f66e56
--- /dev/null
@@ -0,0 +1,18 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * httpt_test_utilities.h -- This is the "one-stop-shop" header for including http test dependencies
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "http_asserts.h"
+#include "http_test_utilities_public.h"
+#include "test_http_client.h"
+#include "test_http_server.h"
+#include "test_server_utilities.h"
diff --git a/Release/tests/functional/http/utilities/include/http_test_utilities_public.h b/Release/tests/functional/http/utilities/include/http_test_utilities_public.h
new file mode 100644 (file)
index 0000000..9e2821c
--- /dev/null
@@ -0,0 +1,24 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * http_test_utilities_public.h -- Common definitions for public http test utility headers
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if !defined(_WIN32) && !defined(__cplusplus_winrt)
+#define TEST_UTILITY_API
+#endif // !_WIN32 && !__cplusplus_winrt
+
+#ifndef TEST_UTILITY_API
+#ifdef HTTPTESTUTILITY_EXPORTS
+#define TEST_UTILITY_API __declspec(dllexport)
+#else // HTTPTESTUTILITIES_EXPORTS
+#define TEST_UTILITY_API __declspec(dllimport)
+#endif // HTTPTESTUTILITIES_EXPORTS
+#endif // TEST_UTILITY_API
diff --git a/Release/tests/functional/http/utilities/include/test_http_client.h b/Release/tests/functional/http/utilities/include/test_http_client.h
new file mode 100644 (file)
index 0000000..0e840e5
--- /dev/null
@@ -0,0 +1,151 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * test_http_client.h -- Defines a test client to handle requests and sending responses.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/uri.h"
+#include "http_test_utilities_public.h"
+#include <map>
+#include <unittestpp.h>
+#include <vector>
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace utilities
+{
+class _test_http_client;
+
+/// <summary>
+/// Structure for storing information about an HTTP response.
+/// <summary>
+class test_response
+{
+public:
+    test_response(_test_http_client* client) : m_client(client) {}
+
+    // API to check if a specific header exists and get it.
+    template<typename T>
+    bool match_header(const utility::string_t& header_name, T& header_value)
+    {
+        auto iter = m_headers.find(header_name);
+
+        if (iter != m_headers.end())
+        {
+            utility::istringstream_t iss(iter->second);
+            iss >> header_value;
+            if (iss.fail() || !iss.eof())
+            {
+                return false;
+            }
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    bool match_header(const utility::string_t& header_name, utility::string_t& header_value)
+    {
+        auto iter = m_headers.find(header_name);
+        if (iter != m_headers.end())
+        {
+            header_value = m_headers[header_name];
+            return true;
+        }
+        return false;
+    }
+
+    // Response data.
+    unsigned short m_status_code;
+    utility::string_t m_reason_phrase;
+    std::map<utility::string_t, utility::string_t> m_headers;
+    std::vector<unsigned char> m_data;
+
+    friend class _test_http_client;
+    _test_http_client* m_client;
+};
+
+/// <summary>
+/// Basic HTTP client for testing. Supports sending multiple requests.
+///
+/// NOTE: this HTTP client is not concurrency safe. I.e. only one thread at a time should use it.
+/// </summary>
+class test_http_client
+{
+public:
+    TEST_UTILITY_API test_http_client(const web::http::uri& uri);
+    TEST_UTILITY_API ~test_http_client();
+    TEST_UTILITY_API test_http_client(test_http_client&& other);
+    TEST_UTILITY_API test_http_client& operator=(test_http_client&& other);
+
+    // APIs to open and close requests.
+    TEST_UTILITY_API unsigned long open();
+    TEST_UTILITY_API unsigned long close();
+
+    // APIs to send requests.
+    TEST_UTILITY_API unsigned long request(const utility::string_t& method, const utility::string_t& path);
+    TEST_UTILITY_API unsigned long request(const utility::string_t& method,
+                                           const utility::string_t& path,
+                                           const std::map<utility::string_t, utility::string_t>& headers);
+    TEST_UTILITY_API unsigned long request(const utility::string_t& method,
+                                           const utility::string_t& path,
+                                           const std::string& data);
+    TEST_UTILITY_API unsigned long request(const utility::string_t& method,
+                                           const utility::string_t& path,
+                                           const utility::string_t& content_type,
+                                           const std::string& data);
+    TEST_UTILITY_API unsigned long request(const utility::string_t& method,
+                                           const utility::string_t& path,
+                                           const std::map<utility::string_t, utility::string_t>& headers,
+                                           const std::string& data);
+
+    // APIs to receive responses.
+    TEST_UTILITY_API test_response* wait_for_response();
+    TEST_UTILITY_API pplx::task<test_response*> next_response();
+    TEST_UTILITY_API std::vector<test_response*> wait_for_responses(const size_t count);
+    TEST_UTILITY_API std::vector<pplx::task<test_response*>> next_responses(const size_t count);
+
+    // RAII pattern for test_http_client.
+    class scoped_client
+    {
+    public:
+        scoped_client(const web::http::uri& uri)
+        {
+            m_p_client = new test_http_client(uri);
+            VERIFY_ARE_EQUAL(0u, m_p_client->open());
+        }
+        ~scoped_client()
+        {
+            VERIFY_ARE_EQUAL(0u, m_p_client->close());
+            delete m_p_client;
+        }
+        test_http_client* client() { return m_p_client; }
+
+    private:
+        test_http_client* m_p_client;
+    };
+
+private:
+    test_http_client& operator=(const test_http_client&);
+    test_http_client(const test_http_client&);
+
+    std::unique_ptr<_test_http_client> m_impl;
+};
+
+} // namespace utilities
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/utilities/include/test_http_server.h b/Release/tests/functional/http/utilities/include/test_http_server.h
new file mode 100644 (file)
index 0000000..872fd59
--- /dev/null
@@ -0,0 +1,141 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * test_http_server.h -- Defines a test server to handle requests and sending responses.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/uri.h"
+#include "http_test_utilities_public.h"
+#include "unittestpp.h"
+#include <map>
+#include <sstream>
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace utilities
+{
+/// <summary>
+/// Actual implementation of test_http_server is in this class.
+/// This wrapping is done to hide the fact we are using Windows HTTP Server APIs
+/// from users of this test library.
+/// </summary>
+class _test_http_server;
+
+/// <summary>
+/// Structure for storing HTTP request information and responding to requests.
+/// </summary>
+class test_request
+{
+    friend class _test_http_server;
+
+public:
+    test_request(unsigned long long reqid, _test_http_server* p_server) : m_request_id(reqid), m_p_server(p_server) {}
+
+    // APIs to send responses.
+    unsigned long reply(const unsigned short status_code,
+                        const utility::string_t& reason_phrase = U(""),
+                        const std::map<utility::string_t, utility::string_t>& headers =
+                            std::map<utility::string_t, utility::string_t>(),
+                        const utf8string& data = "")
+    {
+        return reply_impl(status_code, reason_phrase, headers, (void*)&data[0], data.size() * sizeof(utf8char));
+    }
+
+    unsigned long reply(const unsigned short status_code,
+                        const utility::string_t& reason_phrase,
+                        const std::map<utility::string_t, utility::string_t>& headers,
+                        const std::vector<uint8_t>& data)
+    {
+        return reply_impl(status_code, reason_phrase, headers, (void*)&data[0], data.size());
+    }
+
+    unsigned long reply(const unsigned short status_code,
+                        const utility::string_t& reason_phrase,
+                        const std::map<utility::string_t, utility::string_t>& headers,
+                        const utf16string& data)
+    {
+        return reply_impl(status_code, reason_phrase, headers, (void*)&data[0], data.size() * sizeof(utf16char));
+    }
+
+    // API to check if a specific header exists and get it.
+    template<typename T>
+    bool match_header(const utility::string_t& header_name, T& header_value)
+    {
+        auto iter = m_headers.find(header_name);
+        if (iter == m_headers.end())
+        {
+            return false;
+        }
+
+        return web::http::details::bind_impl(iter->second, header_value) || iter->second.empty();
+    }
+
+    // Request data.
+    utility::string_t m_method;
+    utility::string_t m_path;
+    std::map<utility::string_t, utility::string_t> m_headers;
+    std::vector<unsigned char> m_body;
+
+private:
+    // This is the HTTP Server API Request Id, we don't want to bring in the header file.
+    unsigned long long m_request_id;
+    _test_http_server* m_p_server;
+
+    // Helper to send replies.
+    TEST_UTILITY_API unsigned long reply_impl(const unsigned short status_code,
+                                              const utility::string_t& reason_phrase,
+                                              const std::map<utility::string_t, utility::string_t>& headers,
+                                              void* data,
+                                              size_t data_length);
+};
+
+/// <summary>
+/// Basic HTTP server for testing. Supports waiting and collecting together requests.
+///
+/// NOTE: this HTTP server is not concurrency safe. I.e. only one thread at a time should use it.
+/// </summary>
+class test_http_server
+{
+public:
+    TEST_UTILITY_API test_http_server(const web::http::uri& uri);
+    TEST_UTILITY_API ~test_http_server();
+
+    // APIs to receive requests.
+    TEST_UTILITY_API pplx::task<test_request*> next_request();
+    TEST_UTILITY_API std::vector<pplx::task<test_request*>> next_requests(const size_t count);
+
+    // Enable early close
+    TEST_UTILITY_API void close();
+
+    // RAII pattern for test_http_server.
+    class scoped_server;
+
+private:
+    std::unique_ptr<_test_http_server> m_p_impl;
+};
+
+class test_http_server::scoped_server
+{
+public:
+    scoped_server(const web::http::uri& uri) : m_p_server(uri) {}
+    test_http_server* server() { return &m_p_server; }
+
+private:
+    test_http_server m_p_server;
+};
+
+} // namespace utilities
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/utilities/include/test_server_utilities.h b/Release/tests/functional/http/utilities/include/test_server_utilities.h
new file mode 100644 (file)
index 0000000..06559b7
--- /dev/null
@@ -0,0 +1,67 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * test_server_utilities.h - Utility class to send and verify requests and responses working with the http_test_server.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/http_client.h"
+#include "http_test_utilities_public.h"
+#include "test_http_server.h"
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace utilities
+{
+class test_server_utilities
+{
+public:
+    /// <summary>
+    /// Sends request with specified values using given http_client and verifies
+    /// they are properly received by the test server.
+    /// </summary>
+    TEST_UTILITY_API static void __cdecl verify_request(web::http::client::http_client* p_client,
+                                                        const utility::string_t& method,
+                                                        const utility::string_t& path,
+                                                        test_http_server* p_server,
+                                                        unsigned short code);
+
+    TEST_UTILITY_API static void __cdecl verify_request(web::http::client::http_client* p_client,
+                                                        const utility::string_t& method,
+                                                        const utility::string_t& path,
+                                                        test_http_server* p_server,
+                                                        unsigned short code,
+                                                        const utility::string_t& reason);
+
+    TEST_UTILITY_API static void __cdecl verify_request(web::http::client::http_client* p_client,
+                                                        const utility::string_t& method,
+                                                        const utility::string_t& path,
+                                                        const utility::string_t& request_content_type,
+                                                        const utility::string_t& request_data,
+                                                        test_http_server* p_server,
+                                                        unsigned short code,
+                                                        const utility::string_t& reason);
+
+    TEST_UTILITY_API static void __cdecl verify_request(
+        web::http::client::http_client* p_client,
+        const utility::string_t& method,
+        const utility::string_t& path,
+        test_http_server* p_server,
+        unsigned short code,
+        const std::map<utility::string_t, utility::string_t>& response_headers);
+};
+
+} // namespace utilities
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/utilities/stdafx.cpp b/Release/tests/functional/http/utilities/stdafx.cpp
new file mode 100644 (file)
index 0000000..c8f4d74
--- /dev/null
@@ -0,0 +1,10 @@
+/***
+ * 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"
diff --git a/Release/tests/functional/http/utilities/stdafx.h b/Release/tests/functional/http/utilities/stdafx.h
new file mode 100644 (file)
index 0000000..23e69e4
--- /dev/null
@@ -0,0 +1,26 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Pre-compiled headers
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+
+#include <Windows.h>
+#endif
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/http_client.h"
+#include "cpprest/http_msg.h"
+#include "cpprest/uri.h"
+#include "include/http_asserts.h"
+#include "unittestpp.h"
diff --git a/Release/tests/functional/http/utilities/test_http_client.cpp b/Release/tests/functional/http/utilities/test_http_client.cpp
new file mode 100644 (file)
index 0000000..4073691
--- /dev/null
@@ -0,0 +1,537 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Defines a test client to handle requests and sending responses.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "test_http_client.h"
+
+#include "cpprest/details/http_helpers.h"
+#include "cpprest/uri.h"
+#ifdef _WIN32
+#include <winhttp.h>
+#pragma comment(lib, "winhttp.lib")
+#pragma warning(push)
+#pragma warning(disable : 4457)
+#include <agents.h>
+#pragma warning(pop)
+#endif
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace utilities
+{
+// Flatten the http_headers into a name:value pairs separated by a carriage return and line feed.
+utility::string_t flatten_http_headers(const std::map<utility::string_t, utility::string_t>& headers)
+{
+    utility::string_t flattened_headers;
+    for (auto iter = headers.begin(); iter != headers.end(); ++iter)
+    {
+        utility::string_t temp((*iter).first + U(":") + (*iter).second + U("\r\n"));
+        flattened_headers.append(utility::string_t(temp.begin(), temp.end()));
+    }
+    return flattened_headers;
+}
+
+#ifdef _WIN32
+
+// Helper function to query for the size of header values.
+static void query_header_length(HINTERNET request_handle, DWORD header, DWORD& length)
+{
+    WinHttpQueryHeaders(request_handle,
+                        header,
+                        WINHTTP_HEADER_NAME_BY_INDEX,
+                        WINHTTP_NO_OUTPUT_BUFFER,
+                        &length,
+                        WINHTTP_NO_HEADER_INDEX);
+}
+
+// Helper function to get the status code from a WinHTTP response.
+static void parse_status_code(HINTERNET request_handle, unsigned short& code)
+{
+    DWORD length = 0;
+    query_header_length(request_handle, WINHTTP_QUERY_STATUS_CODE, length);
+    utility::string_t buffer;
+    buffer.resize(length);
+    WinHttpQueryHeaders(request_handle,
+                        WINHTTP_QUERY_STATUS_CODE,
+                        WINHTTP_HEADER_NAME_BY_INDEX,
+                        &buffer[0],
+                        &length,
+                        WINHTTP_NO_HEADER_INDEX);
+    code = (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);
+    index;
+    for (index = str.size(); index > 0 && str[index - 1] == 0; --index)
+        ;
+    str.erase(index);
+}
+
+// Helper function to get the reason phrase from a WinHTTP response.
+static void parse_reason_phrase(HINTERNET request_handle, utility::string_t& phrase)
+{
+    DWORD length = 0;
+    query_header_length(request_handle, WINHTTP_QUERY_STATUS_TEXT, length);
+    phrase.resize(length);
+    WinHttpQueryHeaders(request_handle,
+                        WINHTTP_QUERY_STATUS_TEXT,
+                        WINHTTP_HEADER_NAME_BY_INDEX,
+                        &phrase[0],
+                        &length,
+                        WINHTTP_NO_HEADER_INDEX);
+    // WinHTTP reports back the wrong length, trim any null characters.
+    trim_nulls(phrase);
+}
+
+/// <summary>
+/// Parses a string containing Http headers.
+/// </summary>
+static void parse_winhttp_headers(HINTERNET request_handle, utf16char* headersStr, test_response* p_response)
+{
+    // Status code and reason phrase.
+    parse_status_code(request_handle, p_response->m_status_code);
+    parse_reason_phrase(request_handle, p_response->m_reason_phrase);
+
+    utf16char* context = nullptr;
+    utf16char* line = wcstok_s(headersStr, U("\r\n"), &context);
+    while (line != nullptr)
+    {
+        const utility::string_t header_line(line);
+        const size_t colonIndex = header_line.find_first_of(U(":"));
+        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);
+            tests::functional::http::utilities::trim_whitespace(key);
+            tests::functional::http::utilities::trim_whitespace(value);
+            p_response->m_headers[key] = value;
+        }
+        line = wcstok_s(nullptr, U("\r\n"), &context);
+    }
+}
+
+class _test_http_client
+{
+public:
+    _test_http_client(const utility::string_t& uri) : m_uri(uri), m_hSession(nullptr), m_hConnection(nullptr) {}
+
+    unsigned long open()
+    {
+        // Open session.
+        m_hSession = WinHttpOpen(U("test_http_client"),
+                                 WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+                                 WINHTTP_NO_PROXY_NAME,
+                                 WINHTTP_NO_PROXY_BYPASS,
+                                 WINHTTP_FLAG_ASYNC);
+        if (!m_hSession)
+        {
+            return GetLastError();
+        }
+
+        // Set timeouts.
+        int multiplier = 10;
+        if (!WinHttpSetTimeouts(
+                m_hSession, 60000 * multiplier, 60000 * multiplier, 30000 * multiplier, 30000 * multiplier))
+        {
+            return GetLastError();
+        }
+
+        // Set max connection to use per server to 1.
+        DWORD maxConnections = 1;
+        if (!WinHttpSetOption(m_hSession, WINHTTP_OPTION_MAX_CONNS_PER_SERVER, &maxConnections, sizeof(maxConnections)))
+        {
+            return GetLastError();
+        }
+
+        // Register asynchronous callback.
+        if (WINHTTP_INVALID_STATUS_CALLBACK ==
+            WinHttpSetStatusCallback(
+                m_hSession, &_test_http_client::completion_callback, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS, 0))
+        {
+            return GetLastError();
+        }
+
+        // Open connection.
+        ::http::uri u(m_uri);
+        unsigned int port = u.is_port_default() ? INTERNET_DEFAULT_PORT : u.port();
+        m_hConnection = WinHttpConnect(m_hSession, u.host().c_str(), (INTERNET_PORT)port, 0);
+        if (m_hConnection == nullptr)
+        {
+            return GetLastError();
+        }
+        return 0;
+    }
+
+    unsigned long close()
+    {
+        // Release memory for each request.
+        std::for_each(
+            m_responses_memory.begin(), m_responses_memory.end(), [](test_response* p_response) { delete p_response; });
+
+        if (m_hConnection != nullptr)
+        {
+            if (WinHttpCloseHandle(m_hConnection) == NULL)
+            {
+                return GetLastError();
+            }
+        }
+
+        if (m_hSession != nullptr)
+        {
+            // Unregister the callback.
+            if (!WinHttpSetStatusCallback(m_hSession, NULL, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL))
+            {
+                return GetLastError();
+            }
+
+            if (WinHttpCloseHandle(m_hSession) == NULL)
+            {
+                return GetLastError();
+            }
+        }
+        return 0;
+    }
+
+    unsigned long request(const utility::string_t& method,
+                          const utility::string_t& path,
+                          const std::map<utility::string_t, utility::string_t> headers,
+                          void* data,
+                          size_t data_length)
+    {
+        HINTERNET request_handle = WinHttpOpenRequest(
+            m_hConnection, method.c_str(), path.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
+        if (request_handle == nullptr)
+        {
+            return GetLastError();
+        }
+
+        // Add headers.
+        if (!headers.empty())
+        {
+            utility::string_t flattened_headers = flatten_http_headers(headers);
+            if (!WinHttpAddRequestHeaders(request_handle,
+                                          flattened_headers.c_str(),
+                                          (DWORD)flattened_headers.length(),
+                                          WINHTTP_ADDREQ_FLAG_ADD))
+            {
+                return GetLastError();
+            }
+        }
+
+        if (!WinHttpSendRequest(request_handle,
+                                WINHTTP_NO_ADDITIONAL_HEADERS,
+                                0,
+                                data,
+                                (DWORD)data_length,
+                                (DWORD)data_length,
+                                (DWORD_PTR) new test_response(this)))
+        {
+            return GetLastError();
+        }
+        return 0;
+    }
+
+    test_response* wait_for_response() { return wait_for_responses(1)[0]; }
+
+    pplx::task<test_response*> next_response()
+    {
+        return pplx::create_task([this]() -> test_response* { return wait_for_response(); });
+    }
+
+    std::vector<test_response*> wait_for_responses(const size_t count)
+    {
+        std::vector<test_response*> m_test_responses;
+        for (size_t i = 0; i < count; ++i)
+        {
+            m_test_responses.push_back(Concurrency::receive(m_responses));
+        }
+        return m_test_responses;
+    }
+
+    std::vector<pplx::task<test_response*>> next_responses(const size_t count)
+    {
+        std::vector<pplx::task_completion_event<test_response*>> events;
+        std::vector<pplx::task<test_response*>> responses;
+        for (size_t i = 0; i < count; ++i)
+        {
+            events.push_back(pplx::task_completion_event<test_response*>());
+            responses.push_back(pplx::create_task(events[i]));
+        }
+        pplx::create_task([this, count, events]() {
+            for (size_t i = 0; i < count; ++i)
+            {
+                events[i].set(wait_for_response());
+            }
+        });
+        return responses;
+    }
+
+private:
+    // WinHTTP callback.
+    static void CALLBACK
+    completion_callback(HINTERNET hRequestHandle, DWORD_PTR context, DWORD statusCode, void* statusInfo, DWORD)
+    {
+        test_response* p_response = reinterpret_cast<test_response*>(context);
+        if (p_response != nullptr)
+        {
+            if (statusCode == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR)
+            {
+                WINHTTP_ASYNC_RESULT* pStatusInfo = static_cast<WINHTTP_ASYNC_RESULT*>(statusInfo);
+                pStatusInfo;
+                throw std::exception("Error in WinHTTP callback");
+            }
+            else if (statusCode == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE)
+            {
+                if (!WinHttpReceiveResponse(hRequestHandle, NULL))
+                {
+                    throw std::exception("Error receiving response");
+                }
+            }
+            else if (statusCode == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE)
+            {
+                DWORD headers_length;
+                WinHttpQueryHeaders(hRequestHandle,
+                                    WINHTTP_QUERY_RAW_HEADERS_CRLF,
+                                    WINHTTP_HEADER_NAME_BY_INDEX,
+                                    WINHTTP_NO_OUTPUT_BUFFER,
+                                    &headers_length,
+                                    WINHTTP_NO_HEADER_INDEX);
+
+                // Now allocate buffer for headers and query for them.
+                std::vector<unsigned char> header_raw_buffer;
+                header_raw_buffer.resize(headers_length);
+                utf16char* header_buffer = reinterpret_cast<utf16char*>(&header_raw_buffer[0]);
+                if (!WinHttpQueryHeaders(hRequestHandle,
+                                         WINHTTP_QUERY_RAW_HEADERS_CRLF,
+                                         WINHTTP_HEADER_NAME_BY_INDEX,
+                                         header_buffer,
+                                         &headers_length,
+                                         WINHTTP_NO_HEADER_INDEX))
+                {
+                    throw std::exception("Error querying for headers");
+                }
+                parse_winhttp_headers(hRequestHandle, header_buffer, p_response);
+
+                // Check to see if the response has a body or not.
+                if (!WinHttpQueryDataAvailable(hRequestHandle, nullptr))
+                {
+                    throw std::exception("Error reading response body");
+                }
+            }
+            else if (statusCode == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE)
+            {
+                DWORD num_bytes = *(PDWORD)statusInfo;
+
+                if (num_bytes > 0)
+                {
+                    size_t current_size = p_response->m_data.size();
+                    p_response->m_data.resize(current_size + (size_t)num_bytes);
+
+                    // Actual WinHTTP call to read in body.
+                    if (!WinHttpReadData(hRequestHandle, &p_response->m_data[current_size], (DWORD)num_bytes, NULL))
+                    {
+                        throw std::exception("Error reading response body");
+                    }
+                }
+                else
+                {
+                    WinHttpCloseHandle(hRequestHandle);
+                    p_response->m_client->m_responses_memory.push_back(p_response);
+                    Concurrency::asend(p_response->m_client->m_responses, p_response);
+                }
+            }
+            else if (statusCode == WINHTTP_CALLBACK_STATUS_READ_COMPLETE)
+            {
+                if (!WinHttpQueryDataAvailable(hRequestHandle, nullptr))
+                {
+                    throw std::exception("Error reading response body");
+                }
+            }
+        }
+    }
+
+    Concurrency::unbounded_buffer<test_response*> m_responses;
+
+    // Used to store all requests to simplify memory management.
+    std::vector<test_response*> m_responses_memory;
+
+    const utility::string_t m_uri;
+    HINTERNET m_hSession;
+    HINTERNET m_hConnection;
+};
+#else
+class _test_http_client
+{
+private:
+    const web::http::uri m_uri;
+    typename web::http::client::http_client m_client;
+    std::vector<pplx::task<web::http::http_response>> m_responses;
+    std::vector<test_response*> m_test_responses;
+
+public:
+    _test_http_client(utility::string_t uri) : m_uri(web::http::uri::encode_uri(uri)), m_client(m_uri.authority()) {}
+
+    unsigned long open() { return 0; }
+    unsigned long close() { return 0; }
+
+    unsigned long request(const utility::string_t& method,
+                          const utility::string_t& path,
+                          const std::map<utility::string_t, utility::string_t>& headers,
+                          void* data,
+                          size_t data_length)
+    {
+        auto localHeaders = headers;
+        localHeaders["User-Agent"] = "test_http_client";
+        web::http::http_request request;
+        request.set_method(method);
+        request.set_request_uri(web::http::uri_builder(m_uri).append_path(path).to_uri());
+        auto& hDest = request.headers();
+        for (auto it = localHeaders.begin(); it != localHeaders.end(); ++it)
+        {
+            auto& currentValue = hDest[it->first];
+            if (currentValue.empty())
+                currentValue = it->second;
+            else
+                currentValue = currentValue + U(", ") + it->second;
+        }
+        request.set_body(utility::string_t(reinterpret_cast<const char*>(data), data_length));
+
+        m_responses.push_back(m_client.request(request));
+        return 0;
+    }
+
+    test_response* wait_for_response() { return wait_for_responses(1)[0]; }
+
+    pplx::task<test_response*> next_response()
+    {
+        return pplx::create_task([this]() -> test_response* { return wait_for_response(); });
+    }
+
+    std::vector<test_response*> wait_for_responses(const size_t count)
+    {
+        if (count > m_responses.size()) throw std::logic_error("count too big");
+
+        std::vector<test_response*> m_test_responses;
+        for (size_t i = 0; i < count; ++i)
+        {
+            auto response = m_responses[0].get();
+
+            auto tr = new test_response(this);
+            tr->m_status_code = response.status_code();
+            tr->m_reason_phrase = response.reason_phrase();
+            for (auto it = response.headers().begin(); it != response.headers().end(); ++it)
+            {
+                tr->m_headers[it->first] = it->second;
+            }
+            tr->m_data = response.extract_vector().get();
+
+            m_test_responses.push_back(tr);
+            m_responses.erase(m_responses.begin());
+        }
+        return m_test_responses;
+    }
+
+    std::vector<pplx::task<test_response*>> next_responses(const size_t count)
+    {
+        std::vector<pplx::task<test_response*>> result;
+        for (size_t i = 0; i < count; ++i)
+        {
+            result.push_back(next_response());
+        }
+        return result;
+    }
+};
+#endif
+
+test_http_client::test_http_client(const web::http::uri& uri)
+{
+    m_impl = std::unique_ptr<_test_http_client>(new _test_http_client(uri.to_string()));
+}
+
+test_http_client::~test_http_client() {}
+
+test_http_client::test_http_client(test_http_client&& other) : m_impl(std::move(other.m_impl)) {}
+
+test_http_client& test_http_client::operator=(test_http_client&& other)
+{
+    if (this != &other)
+    {
+        this->m_impl = std::move(other.m_impl);
+    }
+    return *this;
+}
+
+unsigned long test_http_client::open() { return m_impl->open(); }
+unsigned long test_http_client::close() { return m_impl->close(); }
+
+unsigned long test_http_client::request(const utility::string_t& method, const utility::string_t& path)
+{
+    return request(method, path, std::map<utility::string_t, utility::string_t>());
+}
+unsigned long test_http_client::request(const utility::string_t& method,
+                                        const utility::string_t& path,
+                                        const std::map<utility::string_t, utility::string_t>& headers)
+{
+    return request(method, path, headers, std::string());
+}
+unsigned long test_http_client::request(const utility::string_t& method,
+                                        const utility::string_t& path,
+                                        const std::string& data)
+{
+    return request(method, path, std::map<utility::string_t, utility::string_t>(), data);
+}
+unsigned long test_http_client::request(const utility::string_t& method,
+                                        const utility::string_t& path,
+                                        const utility::string_t& content_type,
+                                        const std::string& data)
+{
+    std::map<utility::string_t, utility::string_t> headers;
+    headers[U("Content-Type")] = content_type;
+    return request(method, path, headers, data);
+}
+
+unsigned long test_http_client::request(const utility::string_t& method,
+                                        const utility::string_t& path,
+                                        const std::map<utility::string_t, utility::string_t>& headers,
+                                        const std::string& data)
+{
+    return m_impl->request(method, path, headers, (void*)&data[0], data.size());
+}
+
+test_response* test_http_client::wait_for_response() { return m_impl->wait_for_response(); }
+pplx::task<test_response*> test_http_client::next_response() { return m_impl->next_response(); }
+std::vector<test_response*> test_http_client::wait_for_responses(const size_t count)
+{
+    return m_impl->wait_for_responses(count);
+}
+std::vector<pplx::task<test_response*>> test_http_client::next_responses(const size_t count)
+{
+    return m_impl->next_responses(count);
+}
+
+} // namespace utilities
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/utilities/test_http_server.cpp b/Release/tests/functional/http/utilities/test_http_server.cpp
new file mode 100644 (file)
index 0000000..3abb691
--- /dev/null
@@ -0,0 +1,602 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Defines a test server to handle requests and sending responses.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#ifdef _WIN32
+#include <http.h>
+#pragma comment(lib, "httpapi.lib")
+#pragma warning(push)
+#pragma warning(disable : 4457)
+#include <agents.h>
+#pragma warning(pop)
+#else
+#include "cpprest/http_listener.h"
+#endif
+#include "cpprest/uri.h"
+#include "test_http_server.h"
+#include <algorithm>
+#include <mutex>
+#include <os_utilities.h>
+#include <thread>
+
+using namespace web;
+using namespace utility;
+using namespace utility::conversions;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace utilities
+{
+struct test_server_queue
+{
+    std::mutex m_lock;
+    std::deque<pplx::task_completion_event<test_request*>> m_requests;
+    std::vector<std::unique_ptr<test_request>> m_requests_memory;
+
+    void close()
+    {
+        std::lock_guard<std::mutex> lk(m_lock);
+        for (auto&& tce : m_requests)
+            tce.set_exception(std::runtime_error("test_http_server closed."));
+    }
+
+    ~test_server_queue() { close(); }
+
+    void on_request(std::unique_ptr<test_request> req)
+    {
+        std::lock_guard<std::mutex> lk(m_lock);
+        VERIFY_IS_FALSE(m_requests.empty(), "There are no pending calls to next_request.");
+        if (m_requests.empty()) return;
+        auto tce = std::move(m_requests.front());
+        m_requests.pop_front();
+        m_requests_memory.push_back(std::move(req));
+        tce.set(m_requests_memory.back().get());
+    }
+
+    pplx::task<test_request*> next_request()
+    {
+        pplx::task_completion_event<test_request*> tce;
+        std::lock_guard<std::mutex> lock(m_lock);
+        m_requests.push_back(tce);
+        return pplx::create_task(tce);
+    }
+};
+
+#if defined(_WIN32)
+// Helper function to parse verb from Windows HTTP Server API.
+static utility::string_t parse_verb(const HTTP_REQUEST* p_http_request)
+{
+    utility::string_t method;
+    std::string temp;
+    switch (p_http_request->Verb)
+    {
+        case HttpVerbGET: method = U("GET"); break;
+        case HttpVerbPOST: method = U("POST"); break;
+        case HttpVerbPUT: method = U("PUT"); break;
+        case HttpVerbDELETE: method = U("DELETE"); break;
+        case HttpVerbHEAD: method = U("HEAD"); break;
+        case HttpVerbOPTIONS: method = U("OPTIONS"); break;
+        case HttpVerbTRACE: method = U("TRACE"); break;
+        case HttpVerbCONNECT: method = U("CONNECT"); break;
+        case HttpVerbUnknown: temp = p_http_request->pUnknownVerb; method = utility::string_t(temp.begin(), temp.end());
+        default: break;
+    }
+    return method;
+}
+
+/// <summary>
+/// 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.
+/// </summary>
+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)
+{
+    if (src == nullptr)
+    {
+        return utility::string_t();
+    }
+    std::string temp(src);
+    return utility::string_t(temp.begin(), temp.end());
+}
+
+static std::map<utility::string_t, utility::string_t> parse_http_headers(const HTTP_REQUEST_HEADERS& headers)
+{
+    std::map<utility::string_t, utility::string_t> headers_map;
+    for (USHORT i = 0; i < headers.UnknownHeaderCount; ++i)
+    {
+        headers_map[char_to_wstring(headers.pUnknownHeaders[i].pName)] =
+            char_to_wstring(headers.pUnknownHeaders[i].pRawValue);
+    }
+    for (int i = 0; i < HttpHeaderMaximum; ++i)
+    {
+        if (headers.KnownHeaders[i].RawValueLength != 0)
+        {
+            headers_map[HttpServerAPIRequestKnownHeaders[i]] = char_to_wstring(headers.KnownHeaders[i].pRawValue);
+        }
+    }
+    return headers_map;
+}
+
+struct ConcRTOversubscribe
+{
+    ConcRTOversubscribe()
+    {
+#if _MSC_VER >= 1800
+        concurrency::Context::Oversubscribe(true);
+#endif
+    }
+    ~ConcRTOversubscribe()
+    {
+#if _MSC_VER >= 1800
+        concurrency::Context::Oversubscribe(false);
+#endif
+    }
+};
+
+class _test_http_server
+{
+    inline bool is_error_code(ULONG error_code)
+    {
+        return error_code == ERROR_OPERATION_ABORTED || error_code == ERROR_CONNECTION_INVALID ||
+               error_code == ERROR_NETNAME_DELETED || m_closing == 1;
+    }
+
+public:
+    _test_http_server(const web::uri& uri) : m_uri(uri), m_session(0), m_url_group(0), m_request_queue(nullptr)
+    {
+        // Open server session.
+        HTTPAPI_VERSION httpApiVersion = HTTPAPI_VERSION_2;
+        HttpInitialize(httpApiVersion, HTTP_INITIALIZE_SERVER, NULL);
+        ULONG error_code = HttpCreateServerSession(httpApiVersion, &m_session, 0);
+        if (error_code)
+        {
+            throw std::runtime_error("error code: " + std::to_string(error_code));
+        }
+
+        // Create Url group.
+        error_code = HttpCreateUrlGroup(m_session, &m_url_group, 0);
+        if (error_code)
+        {
+            throw std::runtime_error("error code: " + std::to_string(error_code));
+        }
+
+        // Create request queue.
+        error_code = HttpCreateRequestQueue(httpApiVersion, U("test_http_server"), NULL, NULL, &m_request_queue);
+        if (error_code)
+        {
+            throw std::runtime_error("error code: " + std::to_string(error_code));
+        }
+
+        // Windows HTTP Server API will not accept a uri with an empty path, it must have a '/'.
+        auto host_uri = uri.to_string();
+        if (uri.is_path_empty() && host_uri[host_uri.length() - 1] != '/' && uri.query().empty() &&
+            uri.fragment().empty())
+        {
+            host_uri.append(U("/"));
+        }
+
+        // Add Url.
+        error_code = HttpAddUrlToUrlGroup(m_url_group, host_uri.c_str(), (HTTP_URL_CONTEXT)this, 0);
+        if (error_code)
+        {
+            throw std::runtime_error("error code: " + std::to_string(error_code));
+        }
+
+        // Associate Url group with request queue.
+        HTTP_BINDING_INFO bindingInfo;
+        bindingInfo.RequestQueueHandle = m_request_queue;
+        bindingInfo.Flags.Present = 1;
+        error_code =
+            HttpSetUrlGroupProperty(m_url_group, HttpServerBindingProperty, &bindingInfo, sizeof(HTTP_BINDING_INFO));
+        if (error_code)
+        {
+            throw std::runtime_error("error code: " + std::to_string(error_code));
+        }
+
+        // Launch listener thread
+        m_thread = std::thread(
+            [](_test_http_server* self) {
+                for (;;)
+                {
+                    auto req = self->sync_get_request();
+                    if (req == nullptr) break;
+
+                    self->m_queue.on_request(std::move(req));
+                }
+            },
+            this);
+    }
+
+    ~_test_http_server()
+    {
+        close();
+
+        m_thread.join();
+
+        HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
+    }
+
+    std::unique_ptr<test_request> sync_get_request()
+    {
+        ConcRTOversubscribe osubs; // Oversubscription for long running ConcRT tasks
+        const ULONG buffer_length = 1024 * 4;
+        char buffer[buffer_length];
+        ULONG bytes_received = 0;
+        HTTP_REQUEST* p_http_request = (HTTP_REQUEST*)buffer;
+
+        // Read in everything except the body.
+        ULONG error_code2 =
+            HttpReceiveHttpRequest(m_request_queue, HTTP_NULL_ID, 0, p_http_request, buffer_length, &bytes_received, 0);
+        if (error_code2 != 0)
+        {
+            return nullptr;
+        }
+
+        // Now create request structure.
+        auto p_test_request = std::unique_ptr<test_request>(new test_request(p_http_request->RequestId, this));
+        p_test_request->m_path = utf8_to_utf16(p_http_request->pRawUrl);
+        p_test_request->m_method = parse_verb(p_http_request);
+        p_test_request->m_headers = parse_http_headers(p_http_request->Headers);
+
+        // Read in request body.
+        ULONG content_length;
+        const bool has_content_length = p_test_request->match_header(U("Content-Length"), content_length);
+        if (has_content_length && content_length > 0)
+        {
+            p_test_request->m_body.resize(content_length);
+            auto result = HttpReceiveRequestEntityBody(m_request_queue,
+                                                       p_http_request->RequestId,
+                                                       HTTP_RECEIVE_REQUEST_ENTITY_BODY_FLAG_FILL_BUFFER,
+                                                       &p_test_request->m_body[0],
+                                                       content_length,
+                                                       &bytes_received,
+                                                       NULL);
+            if (result != 0) return nullptr;
+        }
+
+        utility::string_t transfer_encoding;
+        const bool has_transfer_encoding = p_test_request->match_header(U("Transfer-Encoding"), transfer_encoding);
+        if (has_transfer_encoding && transfer_encoding.find(U("chunked")) != std::string::npos)
+        {
+            content_length = 0;
+            char buf[4096];
+            auto result = HttpReceiveRequestEntityBody(m_request_queue,
+                                                       p_http_request->RequestId,
+                                                       HTTP_RECEIVE_REQUEST_ENTITY_BODY_FLAG_FILL_BUFFER,
+                                                       (LPVOID)buf,
+                                                       4096,
+                                                       &bytes_received,
+                                                       NULL);
+
+            while (result == NO_ERROR)
+            {
+                content_length += bytes_received;
+                p_test_request->m_body.resize(content_length);
+                memcpy(&p_test_request->m_body[content_length - bytes_received], buf, bytes_received);
+
+                result = HttpReceiveRequestEntityBody(m_request_queue,
+                                                      p_http_request->RequestId,
+                                                      HTTP_RECEIVE_REQUEST_ENTITY_BODY_FLAG_FILL_BUFFER,
+                                                      (LPVOID)buf,
+                                                      4096,
+                                                      &bytes_received,
+                                                      NULL);
+            }
+
+            if (is_error_code(result))
+                return nullptr;
+            else
+                VERIFY_ARE_EQUAL(ERROR_HANDLE_EOF, result);
+        }
+
+        return p_test_request;
+    }
+
+    unsigned long close()
+    {
+        m_closing = 1;
+
+        // Windows HTTP Server API will not accept a uri with an empty path, it must have a '/'.
+        utility::string_t host_uri = m_uri.to_string();
+        if (m_uri.is_path_empty() && host_uri[host_uri.length() - 1] != '/' && m_uri.query().empty() &&
+            m_uri.fragment().empty())
+        {
+            host_uri.append(U("/"));
+        }
+
+        // Remove Url.
+        ULONG error_code = HttpRemoveUrlFromUrlGroup(m_url_group, host_uri.c_str(), 0);
+        if (error_code)
+        {
+            return error_code;
+        }
+
+        // Stop request queue.
+        error_code = HttpShutdownRequestQueue(m_request_queue);
+        if (error_code)
+        {
+            return error_code;
+        }
+
+        // Close all resources.
+        HttpCloseRequestQueue(m_request_queue);
+        HttpCloseUrlGroup(m_url_group);
+        HttpCloseServerSession(m_session);
+
+        m_queue.close();
+
+        return 0;
+    }
+
+    unsigned long send_reply(const unsigned long long request_id,
+                             const unsigned short status_code,
+                             const utility::string_t& reason_phrase,
+                             const std::map<utility::string_t, utility::string_t>& headers,
+                             void* data,
+                             size_t data_length)
+    {
+        ConcRTOversubscribe osubs; // Oversubscription for long running ConcRT tasks
+        HTTP_RESPONSE response;
+        ZeroMemory(&response, sizeof(HTTP_RESPONSE));
+        response.StatusCode = status_code;
+#pragma warning(suppress: 4244) // intentionally narrow wchar_t -> char
+        std::string reason(reason_phrase.begin(), reason_phrase.end());
+        response.pReason = reason.c_str();
+        response.ReasonLength = (USHORT)reason.length();
+
+        // Add headers.
+        std::vector<std::string> headers_buffer;
+        response.Headers.UnknownHeaderCount = (USHORT)headers.size() + 1;
+        response.Headers.pUnknownHeaders = new HTTP_UNKNOWN_HEADER[headers.size() + 1];
+        headers_buffer.resize(headers.size() * 2 + 2);
+
+        // Add the no cache header.
+        headers_buffer[0] = "Cache-Control";
+        headers_buffer[1] = "no-cache";
+        response.Headers.pUnknownHeaders[0].NameLength = (USHORT)headers_buffer[0].size();
+        response.Headers.pUnknownHeaders[0].pName = headers_buffer[0].c_str();
+        response.Headers.pUnknownHeaders[0].RawValueLength = (USHORT)headers_buffer[1].size();
+        response.Headers.pUnknownHeaders[0].pRawValue = headers_buffer[1].c_str();
+
+        // Add all other headers.
+        if (!headers.empty())
+        {
+            int headerIndex = 1;
+            for (auto iter = headers.begin(); iter != headers.end(); ++iter, ++headerIndex)
+            {
+                headers_buffer[headerIndex * 2] = utf16_to_utf8(iter->first);
+                headers_buffer[headerIndex * 2 + 1] = utf16_to_utf8(iter->second);
+
+                // TFS 624150
+#pragma warning(push)
+#pragma warning(disable : 6386)
+                response.Headers.pUnknownHeaders[headerIndex].NameLength =
+                    (USHORT)headers_buffer[headerIndex * 2].size();
+#pragma warning(pop)
+
+                response.Headers.pUnknownHeaders[headerIndex].pName = headers_buffer[headerIndex * 2].c_str();
+                response.Headers.pUnknownHeaders[headerIndex].RawValueLength =
+                    (USHORT)headers_buffer[headerIndex * 2 + 1].size();
+                response.Headers.pUnknownHeaders[headerIndex].pRawValue = headers_buffer[headerIndex * 2 + 1].c_str();
+            }
+        }
+
+        // Add body.
+        response.EntityChunkCount = 0;
+        HTTP_DATA_CHUNK dataChunk;
+        if (data_length != 0)
+        {
+            response.EntityChunkCount = 1;
+            dataChunk.DataChunkType = HttpDataChunkFromMemory;
+            dataChunk.FromMemory.pBuffer = (void*)data;
+            dataChunk.FromMemory.BufferLength = (ULONG)data_length;
+            response.pEntityChunks = &dataChunk;
+        }
+
+        // Synchronously sending the request.
+        unsigned long error_code = HttpSendHttpResponse(m_request_queue,
+                                                        request_id,
+                                                        HTTP_SEND_RESPONSE_FLAG_DISCONNECT,
+                                                        &response,
+                                                        NULL,
+                                                        NULL,
+                                                        NULL,
+                                                        NULL,
+                                                        NULL,
+                                                        NULL);
+
+        // Free memory needed for headers.
+        if (response.Headers.UnknownHeaderCount != 0)
+        {
+            delete[] response.Headers.pUnknownHeaders;
+        }
+
+        return error_code;
+    }
+
+public:
+    test_server_queue m_queue;
+
+private:
+    std::atomic<int> m_closing = 0;
+
+    web::uri m_uri;
+    HTTP_SERVER_SESSION_ID m_session;
+    HTTP_URL_GROUP_ID m_url_group;
+    HANDLE m_request_queue;
+
+    std::thread m_thread;
+};
+#else
+class _test_http_server
+{
+public:
+    test_server_queue m_queue;
+
+private:
+    web::http::experimental::listener::http_listener m_listener;
+
+    std::atomic<unsigned long> m_last_request_id;
+
+    std::mutex m_response_lock;
+    std::unordered_map<unsigned long long, web::http::http_request> m_responding_requests;
+
+public:
+    _test_http_server(const web::uri& uri) : m_listener(uri), m_last_request_id(0)
+    {
+        auto handler = [this](web::http::http_request result) -> void {
+            auto tr = std::unique_ptr<test_request>(new test_request(this->m_last_request_id++, this));
+            tr->m_method = result.method();
+            tr->m_path = result.request_uri().resource().to_string();
+            if (tr->m_path.empty()) tr->m_path = U("/");
+
+            for (auto it = result.headers().begin(); it != result.headers().end(); ++it)
+                tr->m_headers[it->first] = it->second;
+
+            tr->m_body = result.extract_vector().get();
+
+            {
+                std::lock_guard<std::mutex> lock(m_response_lock);
+                m_responding_requests[tr->m_request_id] = result;
+            }
+
+            m_queue.on_request(std::move(tr));
+        };
+        m_listener.support(handler);
+        m_listener.support(web::http::methods::OPTIONS, handler);
+        m_listener.support(web::http::methods::TRCE, handler);
+
+        m_listener.open().wait();
+    }
+
+    ~_test_http_server() { close(); }
+
+    void close()
+    {
+        m_listener.close().wait();
+        m_queue.close();
+    }
+
+    unsigned long send_reply(unsigned long long request_id,
+                             const unsigned short status_code,
+                             const utility::string_t& reason_phrase,
+                             const std::map<utility::string_t, utility::string_t>& headers,
+                             void* data,
+                             size_t data_length)
+    {
+        web::http::http_request request;
+        {
+            std::lock_guard<std::mutex> lock(m_response_lock);
+            auto it = m_responding_requests.find(request_id);
+            if (it == m_responding_requests.end()) throw std::runtime_error("no such request awaiting response");
+            request = it->second;
+            m_responding_requests.erase(it);
+        }
+
+        web::http::http_response response;
+        response.set_status_code(status_code);
+        response.set_reason_phrase(reason_phrase);
+
+        for (auto it = headers.begin(); it != headers.end(); ++it)
+            response.headers().add(it->first, it->second);
+
+        unsigned char* data_bytes = reinterpret_cast<unsigned char*>(data);
+        std::vector<unsigned char> body_data(data_bytes, data_bytes + data_length);
+        response.set_body(std::move(body_data));
+
+        request.reply(response).get();
+
+        return 0;
+    }
+};
+#endif
+
+unsigned long test_request::reply_impl(const unsigned short status_code,
+                                       const utility::string_t& reason_phrase,
+                                       const std::map<utility::string_t, utility::string_t>& headers,
+                                       void* data,
+                                       size_t data_length)
+{
+    return m_p_server->send_reply(m_request_id, status_code, reason_phrase, headers, data, data_length);
+}
+
+test_http_server::test_http_server(const web::http::uri& uri)
+{
+    m_p_impl = std::unique_ptr<_test_http_server>(new _test_http_server(uri));
+}
+
+test_http_server::~test_http_server() {}
+
+pplx::task<test_request*> test_http_server::next_request() { return m_p_impl->m_queue.next_request(); }
+
+std::vector<pplx::task<test_request*>> test_http_server::next_requests(const size_t count)
+{
+    std::vector<pplx::task<test_request*>> ret;
+    ret.reserve(count);
+    for (size_t x = 0; x < count; ++x)
+        ret.push_back(next_request());
+    return ret;
+}
+
+void test_http_server::close() { m_p_impl->close(); }
+
+} // namespace utilities
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/http/utilities/test_server_utilities.cpp b/Release/tests/functional/http/utilities/test_server_utilities.cpp
new file mode 100644 (file)
index 0000000..d3456ec
--- /dev/null
@@ -0,0 +1,90 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * test_server_utilities.h - Utility class to send and verify requests and responses working with the http_test_server.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include "test_server_utilities.h"
+
+#include "http_asserts.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace http
+{
+namespace utilities
+{
+void test_server_utilities::verify_request(::http::client::http_client* p_client,
+                                           const utility::string_t& method,
+                                           const utility::string_t& path,
+                                           test_http_server* p_server,
+                                           unsigned short code)
+{
+    p_server->next_request().then([&](test_request* p_request) {
+        http_asserts::assert_test_request_equals(p_request, method, path);
+        VERIFY_ARE_EQUAL(0, p_request->reply(code));
+    });
+    http_asserts::assert_response_equals(p_client->request(method, path).get(), code);
+}
+
+void test_server_utilities::verify_request(::http::client::http_client* p_client,
+                                           const utility::string_t& method,
+                                           const utility::string_t& path,
+                                           test_http_server* p_server,
+                                           unsigned short code,
+                                           const utility::string_t& reason)
+{
+    p_server->next_request().then([&](test_request* p_request) {
+        http_asserts::assert_test_request_equals(p_request, method, path);
+        VERIFY_ARE_EQUAL(0, p_request->reply(code, reason));
+    });
+    http_asserts::assert_response_equals(p_client->request(method, path).get(), code, reason);
+}
+
+void test_server_utilities::verify_request(::http::client::http_client* p_client,
+                                           const utility::string_t& method,
+                                           const utility::string_t& path,
+                                           const utility::string_t& request_content_type,
+                                           const utility::string_t& request_data,
+                                           test_http_server* p_server,
+                                           unsigned short code,
+                                           const utility::string_t& reason)
+{
+    p_server->next_request().then([&](test_request* p_request) {
+        http_asserts::assert_test_request_equals(p_request, method, path, request_content_type, request_data);
+        VERIFY_ARE_EQUAL(0, p_request->reply(code, reason));
+    });
+    http_asserts::assert_response_equals(
+        p_client->request(method, path, request_data, request_content_type).get(), code, reason);
+}
+
+void test_server_utilities::verify_request(::http::client::http_client* p_client,
+                                           const utility::string_t& method,
+                                           const utility::string_t& path,
+                                           test_http_server* p_server,
+                                           unsigned short code,
+                                           const std::map<utility::string_t, utility::string_t>& response_headers)
+{
+    p_server->next_request().then([&](test_request* p_request) {
+        http_asserts::assert_test_request_equals(p_request, method, path);
+        VERIFY_ARE_EQUAL(0, p_request->reply(code, U(""), response_headers));
+    });
+    http_asserts::assert_response_equals(p_client->request(method, path).get(), code, response_headers);
+}
+
+} // namespace utilities
+} // namespace http
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/json/CMakeLists.txt b/Release/tests/functional/json/CMakeLists.txt
new file mode 100644 (file)
index 0000000..25c90f2
--- /dev/null
@@ -0,0 +1,17 @@
+set(SOURCES
+  construction_tests.cpp
+  negative_parsing_tests.cpp
+  parsing_tests.cpp
+  to_as_and_operators_tests.cpp
+  iterator_tests.cpp
+  json_numbers_tests.cpp
+)
+if(NOT WINDOWS_STORE AND NOT WINDOWS_PHONE)
+  list(APPEND SOURCES fuzz_tests.cpp)
+endif()
+
+add_casablanca_test(json_test SOURCES)
+if(UNIX AND NOT APPLE)
+  cpprest_find_boost()
+  target_link_libraries(json_test PRIVATE cpprestsdk_boost_internal)
+endif()
diff --git a/Release/tests/functional/json/construction_tests.cpp b/Release/tests/functional/json/construction_tests.cpp
new file mode 100644 (file)
index 0000000..111b145
--- /dev/null
@@ -0,0 +1,501 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * construction_tests.cpp
+ *
+ * Tests creating JSON values.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "cpprest/json.h"
+#include "unittestpp.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace json_tests
+{
+SUITE(construction_tests)
+{
+#if defined(__cplusplus_winrt)
+
+    TEST(winrt_platform_string)
+    {
+        Platform::String ^ platformStr = "Hello!";
+        json::value jstr = json::value::string(platformStr->Data());
+        CHECK(jstr.is_string());
+        CHECK_EQUAL(jstr.serialize(), U("\"Hello!\""));
+    }
+
+#endif
+
+    TEST(assignment_op)
+    {
+        json::value arr = json::value::array();
+        arr[0] = json::value(true);
+
+        json::value ass_copy = arr;
+        VERIFY_IS_TRUE(ass_copy.is_array());
+        VERIFY_ARE_EQUAL(U("true"), ass_copy[0].serialize());
+        ass_copy[1] = json::value(false);
+        VERIFY_ARE_EQUAL(U("false"), ass_copy[1].serialize());
+        VERIFY_ARE_EQUAL(U("null"), arr[1].serialize());
+    }
+
+    TEST(copy_ctor_array)
+    {
+        json::value arr = json::value::array();
+        arr[0] = json::value(true);
+
+        json::value copy(arr);
+        VERIFY_IS_TRUE(copy.is_array());
+        VERIFY_ARE_EQUAL(U("true"), copy[0].serialize());
+        copy[1] = json::value(false);
+        VERIFY_ARE_EQUAL(U("false"), copy[1].serialize());
+        VERIFY_ARE_EQUAL(U("null"), arr[1].serialize());
+    }
+
+    TEST(copy_ctor_object)
+    {
+        json::value obj = json::value::object();
+        utility::string_t keyName(U("key"));
+        obj[keyName] = json::value(false);
+
+        // Copy object that has values added.
+        json::value copy(obj);
+        VERIFY_IS_TRUE(copy.is_object());
+        VERIFY_ARE_EQUAL(U("false"), copy[keyName].serialize());
+        obj[keyName] = json::value(true);
+        VERIFY_ARE_EQUAL(U("false"), copy[keyName].serialize());
+        VERIFY_ARE_EQUAL(U("true"), obj[keyName].serialize());
+
+        // Copy object that parses with value, but none additional added.
+        obj = json::value::parse(U("{\"key\": true}"));
+        json::value copy2(obj);
+        VERIFY_IS_TRUE(copy2.is_object());
+        obj[keyName] = json::value(false);
+        VERIFY_IS_TRUE(copy2.size() == 1);
+        VERIFY_ARE_EQUAL(U("false"), obj[keyName].serialize());
+        VERIFY_ARE_EQUAL(U("true"), copy2[keyName].serialize());
+    }
+
+    TEST(copy_ctor_string)
+    {
+        utility::string_t strValue(U("teststr"));
+        json::value str = json::value::string(strValue);
+
+        json::value copy(str);
+        VERIFY_IS_TRUE(copy.is_string());
+        VERIFY_ARE_EQUAL(strValue, copy.as_string());
+        str = json::value::string(U("teststr2"));
+        VERIFY_ARE_EQUAL(strValue, copy.as_string());
+        VERIFY_ARE_EQUAL(U("teststr2"), str.as_string());
+    }
+
+    TEST(copy_ctor_with_escaped)
+    {
+        auto str = json::value::parse(U("\"\\n\""));
+        VERIFY_ARE_EQUAL(U("\n"), str.as_string());
+
+        auto copy = str;
+        VERIFY_ARE_EQUAL(U("\n"), copy.as_string());
+    }
+
+    TEST(move_ctor)
+    {
+        json::value obj;
+        obj[U("A")] = json::value(true);
+
+        json::value moved(std::move(obj));
+        VERIFY_IS_TRUE(moved.is_object());
+        VERIFY_ARE_EQUAL(U("true"), moved[U("A")].serialize());
+        moved[U("B")] = json::value(false);
+        VERIFY_ARE_EQUAL(U("false"), moved[U("B")].serialize());
+    }
+
+    TEST(move_assignment_op)
+    {
+        json::value obj;
+        obj[U("A")] = json::value(true);
+
+        json::value moved;
+        moved = std::move(obj);
+        VERIFY_IS_TRUE(moved.is_object());
+        VERIFY_ARE_EQUAL(U("true"), moved[U("A")].serialize());
+        moved[U("B")] = json::value(false);
+        VERIFY_ARE_EQUAL(U("false"), moved[U("B")].serialize());
+    }
+
+    TEST(constructor_overloads)
+    {
+        json::value v0;
+        json::value v1(17);
+        json::value v2(3.1415);
+        json::value v3(true);
+        const utility::char_t* p4 = U("Hello!");
+        json::value v4(p4);
+
+        json::value v5(U("Hello Again!"));
+        json::value v6(U("YES YOU KNOW IT"));
+        json::value v7(U("HERE ID IS"));
+
+        const utility::char_t* p9 = U("Hello not-escaped!");
+        json::value v8(p9, true);
+        json::value v9(p9, false);
+
+        VERIFY_ARE_EQUAL(v0.type(), json::value::Null);
+        VERIFY_IS_TRUE(v0.is_null());
+        VERIFY_ARE_EQUAL(v1.type(), json::value::Number);
+        VERIFY_IS_TRUE(v1.is_number());
+        VERIFY_IS_TRUE(v1.is_integer());
+        VERIFY_IS_FALSE(v1.is_double());
+        VERIFY_ARE_EQUAL(v2.type(), json::value::Number);
+        VERIFY_IS_TRUE(v2.is_number());
+        VERIFY_IS_TRUE(v2.is_double());
+        VERIFY_IS_FALSE(v2.is_integer());
+        VERIFY_ARE_EQUAL(v3.type(), json::value::Boolean);
+        VERIFY_IS_TRUE(v3.is_boolean());
+        VERIFY_ARE_EQUAL(v4.type(), json::value::String);
+        VERIFY_IS_TRUE(v4.is_string());
+        VERIFY_ARE_EQUAL(v5.type(), json::value::String);
+        VERIFY_IS_TRUE(v5.is_string());
+        VERIFY_ARE_EQUAL(v6.type(), json::value::String);
+        VERIFY_IS_TRUE(v6.is_string());
+        VERIFY_ARE_EQUAL(v7.type(), json::value::String);
+        VERIFY_IS_TRUE(v7.is_string());
+        VERIFY_ARE_EQUAL(v8.type(), json::value::String);
+        VERIFY_IS_TRUE(v8.is_string());
+        VERIFY_ARE_EQUAL(v9.type(), json::value::String);
+        VERIFY_IS_TRUE(v9.is_string());
+    }
+
+    TEST(factory_overloads)
+    {
+        json::value v0 = json::value::null();
+        json::value v1 = json::value::number(17);
+        json::value v2 = json::value::number(3.1415);
+        json::value v3 = json::value::boolean(true);
+        json::value v4 = json::value::string(U("Hello!"));
+        json::value v5 = json::value::string(U("Hello Again!"));
+        json::value v6 = json::value::string(U("Hello!"));
+        json::value v7 = json::value::string(U("Hello Again!"));
+        json::value v8 = json::value::string(U("Hello not-escaped!"), true);
+        json::value v9 = json::value::string(U("Hello not-escaped!"), false);
+        json::value v10 = json::value::object();
+        json::value v11 = json::value::array();
+
+        VERIFY_ARE_EQUAL(v0.type(), json::value::Null);
+        VERIFY_ARE_EQUAL(v1.type(), json::value::Number);
+        VERIFY_ARE_EQUAL(v2.type(), json::value::Number);
+        VERIFY_ARE_EQUAL(v3.type(), json::value::Boolean);
+        VERIFY_ARE_EQUAL(v4.type(), json::value::String);
+        VERIFY_ARE_EQUAL(v5.type(), json::value::String);
+        VERIFY_ARE_EQUAL(v6.type(), json::value::String);
+        VERIFY_ARE_EQUAL(v7.type(), json::value::String);
+        VERIFY_ARE_EQUAL(v8.type(), json::value::String);
+        VERIFY_ARE_EQUAL(v9.type(), json::value::String);
+        VERIFY_ARE_EQUAL(v10.type(), json::value::Object);
+        VERIFY_IS_TRUE(v10.is_object());
+        VERIFY_ARE_EQUAL(v11.type(), json::value::Array);
+        VERIFY_IS_TRUE(v11.is_array());
+    }
+
+    TEST(object_construction)
+    {
+        // Factory which takes a vector.
+        std::vector<std::pair<string_t, json::value>> f;
+        f.push_back(std::make_pair(U("abc"), json::value(true)));
+        f.push_back(std::make_pair(U("xyz"), json::value(44)));
+        json::value obj = json::value::object(f);
+
+        VERIFY_ARE_EQUAL(f.size(), obj.size());
+
+        obj[U("abc")] = json::value::string(U("str"));
+        obj[U("123")] = json::value(false);
+
+        VERIFY_ARE_NOT_EQUAL(f.size(), obj.size());
+        VERIFY_ARE_EQUAL(json::value::string(U("str")).serialize(), obj[U("abc")].serialize());
+        VERIFY_ARE_EQUAL(json::value(false).serialize(), obj[U("123")].serialize());
+
+        // Tests constructing empty and adding.
+        auto val1 = json::value::object();
+        val1[U("A")] = 44;
+        val1[U("hahah")] = json::value(true);
+        VERIFY_ARE_EQUAL(2u, val1.size());
+        VERIFY_ARE_EQUAL(U("44"), val1[U("A")].serialize());
+        VERIFY_ARE_EQUAL(U("true"), val1[U("hahah")].serialize());
+
+        // Construct as null value, then turn into object.
+        json::value val2;
+        VERIFY_IS_TRUE(val2.is_null());
+        val2[U("A")] = 44;
+        val2[U("hahah")] = json::value(true);
+        VERIFY_ARE_EQUAL(2u, val2.size());
+        VERIFY_ARE_EQUAL(U("44"), val2[U("A")].serialize());
+        VERIFY_ARE_EQUAL(U("true"), val2[U("hahah")].serialize());
+    }
+
+    TEST(object_construction_keep_order)
+    {
+        std::vector<std::pair<string_t, json::value>> f;
+        f.push_back(std::make_pair(U("x"), json::value(0)));
+        f.push_back(std::make_pair(U("a"), json::value(1)));
+
+        auto obj1 = json::value::object(f, /*keep_order==*/true);
+        VERIFY_ARE_EQUAL(obj1.as_object().begin()->first, U("x"));
+
+        auto obj2 = json::value::object(f, /*keep_order==*/false);
+        VERIFY_ARE_EQUAL(obj2.as_object().begin()->first, U("a"));
+    }
+
+    TEST(object_construction_from_null_keep_order)
+    {
+        struct restore
+        {
+            ~restore() { json::keep_object_element_order(false); }
+        } _;
+
+        json::keep_object_element_order(true);
+
+        auto val1 = json::value::null();
+        val1[U("B")] = 1;
+        val1[U("A")] = 1;
+        VERIFY_ARE_EQUAL(val1.as_object().begin()->first, U("B"));
+
+        json::keep_object_element_order(false);
+
+        auto val2 = json::value::null();
+        val2[U("B")] = 1;
+        val2[U("A")] = 1;
+        VERIFY_ARE_EQUAL(val2.as_object().begin()->first, U("A"));
+    }
+
+    TEST(array_construction)
+    {
+        // Constructor which takes a vector.
+        std::vector<json::value> e;
+        e.push_back(json::value(false));
+        e.push_back(json::value::string(U("hehe")));
+        json::value arr = json::value::array(e);
+        VERIFY_ARE_EQUAL(e.size(), arr.size());
+        VERIFY_ARE_EQUAL(U("false"), arr[0].serialize());
+        arr[3] = json::value(22);
+        VERIFY_ARE_NOT_EQUAL(e.size(), arr.size());
+        VERIFY_ARE_EQUAL(U("22"), arr[3].serialize());
+
+        // Test empty factory and adding.
+        auto arr2 = json::value::array();
+        arr2[1] = json::value(false);
+        arr2[0] = json::value::object();
+        arr2[0][U("A")] = json::value::string(U("HE"));
+        VERIFY_ARE_EQUAL(2u, arr2.size());
+        VERIFY_ARE_EQUAL(U("false"), arr2[1].serialize());
+        VERIFY_ARE_EQUAL(U("\"HE\""), arr2[0][U("A")].serialize());
+
+        // Construct as null value and then add elements.
+        json::value arr3;
+        VERIFY_IS_TRUE(arr3.is_null());
+        arr3[1] = json::value(false);
+        // Element [0] should already behave as an object.
+        arr3[0][U("A")] = json::value::string(U("HE"));
+        VERIFY_ARE_EQUAL(2u, arr3.size());
+        VERIFY_ARE_EQUAL(U("false"), arr3[1].serialize());
+        VERIFY_ARE_EQUAL(U("\"HE\""), arr3[0][U("A")].serialize());
+
+        // Test factory which takes a size.
+        auto arr4 = json::value::array(2);
+        VERIFY_IS_TRUE(arr4[0].is_null());
+        VERIFY_IS_TRUE(arr4[1].is_null());
+        arr4[2] = json::value(true);
+        arr4[0] = json::value(false);
+        VERIFY_ARE_EQUAL(U("false"), arr4[0].serialize());
+        VERIFY_ARE_EQUAL(U("true"), arr4[2].serialize());
+    }
+
+    TEST(array_test)
+    {
+        json::value arr = json::value::array();
+        const json::value& carr = arr;
+        arr[0] = json::value(3.14);
+        arr[1] = json::value(true);
+        arr[2] = json::value("Yes");
+        int count;
+        json::array& array = arr.as_array();
+        const json::array& carray = arr.as_array();
+
+        VERIFY_THROWS(array.at(4), json::json_exception);
+        VERIFY_THROWS(carray.at(5), json::json_exception);
+        VERIFY_THROWS(arr.at(6), json::json_exception);
+        VERIFY_THROWS(carr.at(7), json::json_exception);
+
+        // The begin and end iterators on non-const instances
+        count = 0;
+        for (auto iter = array.begin(); iter != array.end(); ++iter)
+        {
+            VERIFY_IS_TRUE((*iter) == array[count]);
+            VERIFY_IS_TRUE((*iter) == array.at(count));
+            VERIFY_IS_TRUE((*iter) == carray.at(count));
+            count++;
+        }
+        VERIFY_ARE_EQUAL(array.size(), count);
+
+        count = 0;
+        for (auto iter = array.cbegin(); iter != array.cend(); ++iter)
+        {
+            VERIFY_IS_TRUE((*iter) == array[count]);
+            VERIFY_IS_TRUE((*iter) == array.at(count));
+            count++;
+        }
+        VERIFY_ARE_EQUAL(array.size(), count);
+
+        count = 0;
+        for (auto iter = array.rbegin(); iter != array.rend(); ++iter)
+        {
+            VERIFY_IS_TRUE((*iter) == array[array.size() - 1 - count]);
+            VERIFY_IS_TRUE((*iter) == array.at(array.size() - 1 - count));
+            VERIFY_IS_TRUE((*iter) == carray.at(array.size() - 1 - count));
+            count++;
+        }
+        VERIFY_ARE_EQUAL(array.size(), count);
+
+        count = 0;
+        for (auto iter = array.crbegin(); iter != array.crend(); ++iter)
+        {
+            VERIFY_IS_TRUE((*iter) == array[array.size() - 1 - count]);
+            VERIFY_IS_TRUE((*iter) == arr[array.size() - 1 - count]);
+            VERIFY_IS_TRUE((*iter) == array.at(array.size() - 1 - count));
+            VERIFY_IS_TRUE((*iter) == arr.at(array.size() - 1 - count));
+            count++;
+        }
+        VERIFY_ARE_EQUAL(array.size(), count);
+
+        // The begin and end iterators on const instances
+        count = 0;
+        for (auto iter = carray.begin(); iter != carray.end(); ++iter)
+        {
+            VERIFY_IS_TRUE((*iter) == carray.at(count));
+            VERIFY_IS_TRUE((*iter) == carr.at(count));
+            count++;
+        }
+        VERIFY_ARE_EQUAL(array.size(), count);
+
+        count = 0;
+        for (auto iter = carray.rbegin(); iter != carray.rend(); ++iter)
+        {
+            VERIFY_IS_TRUE((*iter) == carray.at(array.size() - 1 - count));
+            VERIFY_IS_TRUE((*iter) == carr.at(array.size() - 1 - count));
+            count++;
+        }
+        VERIFY_ARE_EQUAL(array.size(), count);
+    }
+
+    TEST(object_test)
+    {
+        json::value obj = json::value::object();
+        const json::value& cobj = obj;
+        json::object& object = obj.as_object();
+        const json::object& cobject = obj.as_object();
+
+        VERIFY_IS_TRUE(object.empty());
+
+        obj[U("name")] = json::value(U("John"));
+        obj[U("surname")] = json::value(U("Smith"));
+        obj[U("height")] = json::value(5.9);
+        obj[U("vegetarian")] = json::value(true);
+        int count;
+
+        // Test at()
+        VERIFY_ARE_EQUAL(U("John"), obj.at(U("name")).as_string());
+        VERIFY_ARE_EQUAL(U("John"), cobj.at(U("name")).as_string());
+        VERIFY_ARE_EQUAL(U("Smith"), object.at(U("surname")).as_string());
+        VERIFY_ARE_EQUAL(U("Smith"), cobject.at(U("surname")).as_string());
+        VERIFY_THROWS(obj.at(U("wrong key")), json::json_exception);
+        VERIFY_THROWS(cobj.at(U("wrong key")), json::json_exception);
+
+        // Test find()
+        {
+            auto iter = object.find(U("height"));
+            VERIFY_ARE_NOT_EQUAL(object.end(), iter);
+            VERIFY_IS_TRUE(iter->second.is_number());
+            VERIFY_ARE_EQUAL(5.9, iter->second.as_number().to_double());
+            VERIFY_ARE_EQUAL(object.end(), object.find(U("wrong_key")));
+        }
+
+        // Test find() const
+        auto citer = cobject.find(U("height"));
+        VERIFY_ARE_NOT_EQUAL(cobject.end(), citer);
+        VERIFY_IS_TRUE(citer->second.is_number());
+        VERIFY_ARE_EQUAL(5.9, citer->second.as_number().to_double());
+        VERIFY_ARE_EQUAL(cobject.end(), cobject.find(U("wrong_key")));
+
+        VERIFY_IS_FALSE(object.empty());
+
+        // The begin and end iterators on non-const instances
+        count = 0;
+        for (auto iter = object.begin(); iter != object.end(); ++iter)
+        {
+            VERIFY_ARE_EQUAL(object[iter->first], iter->second);
+            count++;
+        }
+        VERIFY_ARE_EQUAL(object.size(), count);
+
+        count = 0;
+        for (auto iter = object.rbegin(); iter != object.rend(); ++iter)
+        {
+            VERIFY_ARE_EQUAL(object[iter->first], iter->second);
+            count++;
+        }
+        VERIFY_ARE_EQUAL(object.size(), count);
+
+        count = 0;
+        for (auto iter = object.cbegin(); iter != object.cend(); ++iter)
+        {
+            VERIFY_ARE_EQUAL(object[iter->first], iter->second);
+            count++;
+        }
+        VERIFY_ARE_EQUAL(object.size(), count);
+
+        count = 0;
+        for (auto iter = object.crbegin(); iter != object.crend(); ++iter)
+        {
+            VERIFY_ARE_EQUAL(object[iter->first], iter->second);
+            count++;
+        }
+        VERIFY_ARE_EQUAL(object.size(), count);
+
+        // The begin and end iterators on const instances
+        count = 0;
+        for (auto iter = cobject.begin(); iter != cobject.end(); ++iter)
+        {
+            VERIFY_ARE_EQUAL(cobject.find(iter->first)->second, iter->second);
+            count++;
+        }
+        VERIFY_ARE_EQUAL(cobject.size(), count);
+
+        count = 0;
+        for (auto iter = cobject.rbegin(); iter != cobject.rend(); ++iter)
+        {
+            VERIFY_ARE_EQUAL(cobject.find(iter->first)->second, iter->second);
+            count++;
+        }
+        VERIFY_ARE_EQUAL(cobject.size(), count);
+    }
+
+    TEST(github_asan_989)
+    {
+        ::web::json::value::parse(_XPLATSTR(R"([ { "k1" : "v" }, { "k2" : "v" }, { "k3" : "v" }, { "k4" : "v" } ])"));
+    }
+
+} // SUITE(construction_tests)
+
+} // namespace json_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/json/fuzz_tests.cpp b/Release/tests/functional/json/fuzz_tests.cpp
new file mode 100644 (file)
index 0000000..80b9a81
--- /dev/null
@@ -0,0 +1,83 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * fuzz_tests.cpp
+ *
+ * Fuzz tests for the JSON library.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "cpprest/json.h"
+#include "cpprest/containerstream.h"
+#include "cpprest/filestream.h"
+#include "unittestpp.h"
+
+using namespace web;
+
+namespace tests
+{
+namespace functional
+{
+namespace json_tests
+{
+#ifdef _WIN32
+
+SUITE(json_fuzz_tests)
+{
+    std::string get_fuzzed_file_path()
+    {
+        std::string ipfile;
+
+        if (UnitTest::GlobalSettings::Has("fuzzedinputfile"))
+        {
+            ipfile = UnitTest::GlobalSettings::Get("fuzzedinputfile");
+        }
+
+        return ipfile;
+    }
+
+    TEST(fuzz_json_parser, "Requires", "fuzzedinputfile")
+    {
+        std::wstring ipfile = utility::conversions::to_utf16string(get_fuzzed_file_path());
+        if (true == ipfile.empty())
+        {
+            VERIFY_IS_TRUE(false, "Input file is empty");
+            return;
+        }
+
+        auto fs = Concurrency::streams::file_stream<uint8_t>::open_istream(ipfile).get();
+        concurrency::streams::container_buffer<std::string> cbuf;
+        fs.read_to_end(cbuf).get();
+        fs.close().get();
+        auto json_str = cbuf.collection();
+
+        // Look for UTF-8 BOM
+        if ((uint8_t)json_str[0] != 0xEF || (uint8_t)json_str[1] != 0xBB || (uint8_t)json_str[2] != 0xBF)
+        {
+            VERIFY_IS_TRUE(false, "Input file encoding is not UTF-8. Test will not parse the file.");
+            return;
+        }
+
+        auto utf16_json_str = utility::conversions::utf8_to_utf16(json_str);
+        // UTF8 to UTF16 conversion will retain the BOM, remove it.
+        if (utf16_json_str.front() == 0xFEFF) utf16_json_str.erase(0, 1);
+
+        try
+        {
+            json::value::parse(std::move(utf16_json_str));
+            std::cout << "Input file parsed successfully.";
+        }
+        catch (const json::json_exception& ex)
+        {
+            std::cout << "json exception:" << ex.what();
+        }
+    }
+}
+
+#endif
+} // namespace json_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/json/iterator_tests.cpp b/Release/tests/functional/json/iterator_tests.cpp
new file mode 100644 (file)
index 0000000..0e72709
--- /dev/null
@@ -0,0 +1,308 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * iterator_tests.cpp
+ *
+ * Tests iterating over JSON values
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "cpprest/json.h"
+#include "unittestpp.h"
+#include <algorithm>
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace json_tests
+{
+SUITE(iterator_tests)
+{
+    void validate_array_and_object_throw(json::value value)
+    {
+        VERIFY_THROWS(value.as_array(), web::json::json_exception);
+        VERIFY_THROWS(value.as_object(), web::json::json_exception);
+    }
+
+    TEST(non_composites_member_preincrement)
+    {
+        validate_array_and_object_throw(json::value::null());
+        validate_array_and_object_throw(json::value::number(17));
+        validate_array_and_object_throw(json::value::boolean(true));
+        validate_array_and_object_throw(json::value::string(U("Hello!")));
+    }
+
+    TEST(objects_constructed)
+    {
+        json::value val1;
+        val1[U("a")] = 44;
+        val1[U("b")] = json::value(true);
+        val1[U("c")] = json::value(false);
+
+        VERIFY_ARE_EQUAL(3, val1.size());
+
+        size_t count = 0;
+        for (auto iter = std::begin(val1.as_object()); iter != std::end(val1.as_object()); ++iter)
+        {
+            auto key = iter->first;
+            auto& value = iter->second;
+            switch (count)
+            {
+                case 0:
+                    VERIFY_ARE_EQUAL(U("a"), key);
+                    VERIFY_IS_TRUE(value.is_number());
+                    break;
+                case 1:
+                    VERIFY_ARE_EQUAL(U("b"), key);
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    break;
+                case 2:
+                    VERIFY_ARE_EQUAL(U("c"), key);
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    break;
+            }
+            count++;
+        }
+        VERIFY_ARE_EQUAL(3, count);
+    }
+
+    TEST(objects_parsed)
+    {
+        json::value val1 = json::value::parse(U("{\"a\": 44, \"b\": true, \"c\": false}"));
+
+        VERIFY_ARE_EQUAL(3, val1.size());
+
+        size_t count = 0;
+        for (auto iter = std::begin(val1.as_object()); iter != std::end(val1.as_object()); ++iter)
+        {
+            auto key = iter->first;
+            auto& value = iter->second;
+            switch (count)
+            {
+                default: VERIFY_IS_TRUE(value.is_null()); break;
+                case 0:
+                    VERIFY_ARE_EQUAL(U("a"), key);
+                    VERIFY_IS_TRUE(value.is_number());
+                    VERIFY_ARE_EQUAL(44, value.as_integer());
+                    break;
+                case 1:
+                    VERIFY_ARE_EQUAL(U("b"), key);
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    VERIFY_IS_TRUE(value.as_bool());
+                    break;
+                case 2:
+                    VERIFY_ARE_EQUAL(U("c"), key);
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    VERIFY_IS_FALSE(value.as_bool());
+                    break;
+            }
+            count++;
+        }
+        VERIFY_ARE_EQUAL(3, count);
+    }
+
+    TEST(objects_reverse)
+    {
+        json::value val1 = json::value::parse(U("{\"a\": 44, \"b\": true, \"c\": false}"));
+
+        VERIFY_ARE_EQUAL(3, val1.size());
+        VERIFY_ARE_EQUAL(3, val1.as_object().size());
+
+        size_t count = 0;
+        for (auto iter = val1.as_object().rbegin(); iter != val1.as_object().rend(); ++iter)
+        {
+            auto key = iter->first;
+            auto& value = iter->second;
+            switch (count)
+            {
+                case 2:
+                    VERIFY_ARE_EQUAL(U("a"), key);
+                    VERIFY_IS_TRUE(value.is_number());
+                    VERIFY_ARE_EQUAL(44, value.as_integer());
+                    break;
+                case 1:
+                    VERIFY_ARE_EQUAL(U("b"), key);
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    VERIFY_IS_TRUE(value.as_bool());
+                    break;
+                case 0:
+                    VERIFY_ARE_EQUAL(U("c"), key);
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    VERIFY_IS_FALSE(value.as_bool());
+                    break;
+            }
+            count++;
+        }
+        VERIFY_ARE_EQUAL(3, count);
+    }
+
+    TEST(arrays_constructed)
+    {
+        json::value val1;
+        val1[0] = 44;
+        val1[2] = json::value(true);
+        val1[5] = json::value(true);
+
+        VERIFY_ARE_EQUAL(6, val1.size());
+
+        size_t count = 0;
+        for (auto value : val1.as_array())
+        {
+            switch (count)
+            {
+                case 0:
+                    VERIFY_IS_TRUE(value.is_number());
+                    VERIFY_ARE_EQUAL(44, value.as_integer());
+                    break;
+                case 2:
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    VERIFY_IS_TRUE(value.as_bool());
+                    break;
+                case 5:
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    VERIFY_IS_TRUE(value.as_bool());
+                    break;
+            }
+            count++;
+        }
+        VERIFY_ARE_EQUAL(6, count);
+    }
+
+    TEST(arrays_parsed)
+    {
+        json::value val1 = json::value::parse(U("[44, true, false]"));
+
+        VERIFY_ARE_EQUAL(3, val1.size());
+
+        size_t count = 0;
+        for (auto& value : val1.as_array())
+        {
+            switch (count)
+            {
+                case 0:
+                    VERIFY_IS_TRUE(value.is_number());
+                    VERIFY_ARE_EQUAL(44, value.as_integer());
+                    break;
+                case 1:
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    VERIFY_IS_TRUE(value.as_bool());
+                    break;
+                case 2:
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    VERIFY_IS_FALSE(value.as_bool());
+                    break;
+            }
+            count++;
+        }
+        VERIFY_ARE_EQUAL(3, count);
+    }
+
+    TEST(arrays_reversed)
+    {
+        json::value val1 = json::value::parse(U("[44, true, false]"));
+
+        VERIFY_ARE_EQUAL(3, val1.size());
+
+        size_t count = 0;
+        for (auto iter = val1.as_array().rbegin(); iter != val1.as_array().rend(); ++iter)
+        {
+            auto value = *iter;
+            switch (count)
+            {
+                case 2:
+                    VERIFY_IS_TRUE(value.is_number());
+                    VERIFY_ARE_EQUAL(44, value.as_integer());
+                    break;
+                case 1:
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    VERIFY_IS_TRUE(value.as_bool());
+                    break;
+                case 0:
+                    VERIFY_IS_TRUE(value.is_boolean());
+                    VERIFY_IS_FALSE(value.as_bool());
+                    break;
+            }
+            count++;
+        }
+        VERIFY_ARE_EQUAL(3, count);
+    }
+
+    TEST(comparison)
+    {
+        json::value val1;
+        val1[U("a")] = 44;
+        val1[U("b")] = json::value(true);
+        val1[U("c")] = json::value(false);
+
+        auto first = std::begin(val1.as_object());
+        auto f = first;
+        auto f_1 = first++;
+        auto f_2 = ++first;
+
+        VERIFY_ARE_EQUAL(f, f_1);
+        VERIFY_ARE_NOT_EQUAL(f_1, f_2);
+    }
+
+    TEST(std_algorithms)
+    {
+        {
+            // for_each
+            size_t count = 0;
+            json::value v_array = json::value::parse(U("[44, true, false]"));
+            std::for_each(std::begin(v_array.as_array()),
+                          std::end(v_array.as_array()),
+                          [&](json::array::iterator::value_type) { count++; });
+            VERIFY_ARE_EQUAL(3, count);
+        }
+        {
+            // find_if
+            json::value v_array = json::value::parse(U("[44, true, false]"));
+            auto _where = std::find_if(std::begin(v_array.as_array()),
+                                       std::end(v_array.as_array()),
+                                       [&](json::array::iterator::value_type value) { return value.is_boolean(); });
+
+            VERIFY_ARE_NOT_EQUAL(_where, std::end(v_array.as_array()));
+
+            VERIFY_ARE_EQUAL(_where->as_bool(), true);
+        }
+        {
+            // copy_if
+            json::value v_array = json::value::parse(U("[44, true, false]"));
+            std::vector<json::array::iterator::value_type> v_target(v_array.size());
+            auto _where = std::copy_if(std::begin(v_array.as_array()),
+                                       std::end(v_array.as_array()),
+                                       std::begin(v_target),
+                                       [&](json::array::iterator::value_type value) { return value.is_boolean(); });
+            VERIFY_ARE_EQUAL(2, _where - std::begin(v_target));
+            VERIFY_IS_FALSE(v_array.as_array().begin()[1].is_number());
+        }
+        {
+            // transform
+            json::value v_array = json::value::parse(U("[44, true, false]"));
+            std::vector<json::value> v_target(v_array.size());
+            std::transform(std::begin(v_array.as_array()),
+                           std::end(v_array.as_array()),
+                           std::begin(v_target),
+                           [&](json::array::iterator::value_type) -> json::value { return json::value::number(17); });
+
+            VERIFY_ARE_EQUAL(3, v_target.size());
+
+            for (auto iter = std::begin(v_target); iter != std::end(v_target); ++iter)
+            {
+                VERIFY_IS_FALSE(iter->is_null());
+            }
+        }
+    }
+}
+
+} // namespace json_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/json/json_numbers_tests.cpp b/Release/tests/functional/json/json_numbers_tests.cpp
new file mode 100644 (file)
index 0000000..5fa9026
--- /dev/null
@@ -0,0 +1,324 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * json_numbers_tests.cpp
+ *
+ * Tests parsing numbers and json::number class
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "cpprest/json.h"
+#include "unittestpp.h"
+#include <clocale>
+#include <iomanip>
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace json_tests
+{
+SUITE(json_numbers_tests)
+{
+    TEST(numbers)
+    {
+        json::value num = json::value::parse(U("-22"));
+        VERIFY_ARE_EQUAL(-22, num.as_double());
+        VERIFY_ARE_EQUAL(-22, num.as_integer());
+
+        num = json::value::parse(U("-1.45E2"));
+        VERIFY_IS_TRUE(num.is_number());
+
+        num = json::value::parse(U("-1.45E+1"));
+        VERIFY_IS_TRUE(num.is_number());
+
+        num = json::value::parse(U("-1.45E-10"));
+        VERIFY_IS_TRUE(num.is_number());
+
+        num = json::value::parse(U("1e01"));
+        VERIFY_IS_TRUE(num.is_number());
+    }
+
+    // Test both positive and negative number
+    void test_int64(int64_t number)
+    {
+        stringstream_t ss;
+        ss << number;
+        json::value num = json::value::parse(ss);
+        VERIFY_ARE_EQUAL(number, num.as_number().to_int64());
+        VERIFY_IS_TRUE(num.is_integer());
+        num = json::value::number(number);
+        VERIFY_ARE_EQUAL(number, num.as_number().to_int64());
+        VERIFY_IS_TRUE(num.is_integer());
+
+        // Check that the number is convertible to signed int64
+        VERIFY_IS_TRUE(num.as_number().is_int64());
+
+        // Check for other integral conversions
+        VERIFY_ARE_EQUAL(number >= INT_MIN && number <= INT_MAX, num.as_number().is_int32());
+        VERIFY_ARE_EQUAL(number >= 0 && number <= UINT_MAX, num.as_number().is_uint32());
+        VERIFY_ARE_EQUAL(number >= 0, num.as_number().is_uint64());
+    }
+
+    TEST(parse_int64)
+    {
+        // Negative limits
+        test_int64(int64_t(LLONG_MIN));
+        test_int64(int64_t(LLONG_MIN) + 1);
+        test_int64(int64_t(INT_MIN) - 1);
+        test_int64(int64_t(INT_MIN));
+        test_int64(int64_t(INT_MIN) + 1);
+
+        // Around zero
+        test_int64(int64_t(-1));
+        test_int64(int64_t(0));
+        test_int64(int64_t(1));
+
+        // Positive limits
+        test_int64(int64_t(INT_MAX));
+        test_int64(int64_t(INT_MAX) + 1);
+        test_int64(int64_t(UINT_MAX));
+        test_int64(int64_t(UINT_MAX) + 1);
+
+        // Outside 32-bits limits
+        test_int64(int64_t(INT_MAX) * 13 + 5); // a number out of the int32 range
+        test_int64(uint64_t(LLONG_MAX / 2));
+    }
+
+    void test_int64(uint64_t number)
+    {
+        stringstream_t ss;
+        ss << number;
+        json::value num = json::value::parse(ss);
+        VERIFY_ARE_EQUAL(number, num.as_number().to_uint64());
+        VERIFY_IS_TRUE(num.is_integer());
+        num = json::value::number(number);
+        VERIFY_ARE_EQUAL(number, num.as_number().to_uint64());
+        VERIFY_IS_TRUE(num.is_integer());
+
+        // Check that the number is convertible to unsigned int64
+        VERIFY_IS_TRUE(num.as_number().is_uint64());
+
+        // Check for other integral conversions
+        VERIFY_ARE_EQUAL(number <= INT_MAX, num.as_number().is_int32());
+        VERIFY_ARE_EQUAL(number <= UINT_MAX, num.as_number().is_uint32());
+        VERIFY_ARE_EQUAL(number <= LLONG_MAX, num.as_number().is_int64());
+    }
+
+    TEST(parse_uint64)
+    {
+        test_int64(int64_t(0));
+        test_int64(int64_t(1));
+
+        test_int64(uint64_t(LLONG_MAX) - 1);
+        test_int64(uint64_t(LLONG_MAX));
+        test_int64(uint64_t(LLONG_MAX) + 1);
+        test_int64(uint64_t(ULLONG_MAX));
+        test_int64(uint64_t(ULLONG_MAX) - 1);
+    }
+
+    const int DOUBLE_DIGITS =
+        std::numeric_limits<double>::digits10 +
+        7; // 7 = length of "1." and "e+123" which is the begining and the end of the double representation
+
+    void test_double(double number, string_t str_rep)
+    {
+        stringstream_t ss;
+        ss << str_rep;
+
+        json::value num = json::value::parse(ss);
+        VERIFY_ARE_EQUAL(number, num.as_double());
+        VERIFY_ARE_EQUAL(number, num.as_number().to_double());
+
+        // If the number is within integral types limit and not decimal, it should be stored as one of the integral
+        // types
+        VERIFY_ARE_EQUAL(number > LLONG_MIN && number < ULLONG_MAX && number == floor(number), num.is_integer());
+
+        // If it is outside the range, these methods should return false.
+        // Note that at this point there is no guarantee that the number was stored as double.
+
+        if (number < INT_MIN || number > INT_MAX || number != floor(number))
+            VERIFY_IS_FALSE(num.as_number().is_int32());
+
+        if (number < 0 || number > UINT_MAX || number != floor(number)) VERIFY_IS_FALSE(num.as_number().is_uint32());
+
+        if (number < LLONG_MIN || number > LLONG_MAX || number != floor(number))
+            VERIFY_IS_FALSE(num.as_number().is_int64());
+
+        if (number < 0 || number > ULLONG_MAX || number != floor(number)) VERIFY_IS_FALSE(num.as_number().is_uint64());
+    }
+
+    void test_double(double d)
+    {
+        ::std::basic_stringstream<string_t::value_type> ss;
+        ss << ::std::setprecision(DOUBLE_DIGITS);
+        ss << d;
+        test_double(d, ss.str());
+    }
+
+    TEST(parsing_doubles_into_longs)
+    {
+        test_double(2.0);
+        test_double(pow(2.0, 10.0));
+        test_double(pow(2.0, 20.0));
+        test_double(pow(2.0, 60.0));
+        test_double(pow(2.0, 63.0));
+    }
+
+    TEST(parsing_doubles)
+    {
+        test_double(3.14);
+        test_double(-9.81);
+
+        // Note: this should not parse to a ullong because of rounding
+        test_double(static_cast<double>(ULLONG_MAX));
+
+        test_double(0 - static_cast<double>(ULLONG_MAX));
+        test_double(static_cast<double>(ULLONG_MAX) +
+                    (2 << (64 - 52))); // the lowest number that will be represented as double due to overflowing
+                                       // unsigned int64 (52bits fraction in double-precision)
+        test_double(0 - pow(2.0, 63.0) * 1.5); // between 0-ULLONG_MAX and LLONGMIN
+    }
+
+    TEST(parsing_doubles_setlocale,
+         "Ignore:Android",
+         "Locale not supported on Android",
+         "Ignore:Linux",
+         "Fails due to double conversion issues",
+         "Ignore:Apple",
+         "Fails due to double conversion issues")
+    {
+        // JSON uses the C locale always and should therefore not be impacted by the process locale
+#ifdef _WIN32
+        std::string changedLocale("fr-FR");
+#else
+        std::string changedLocale("fr_FR.UTF-8");
+#endif
+
+        // If locale isn't installed on system just silently pass.
+        if (setlocale(LC_ALL, changedLocale.c_str()) != nullptr)
+        {
+            test_double(1.91563);
+            test_double(2.0e93);
+            setlocale(LC_ALL, "C");
+        }
+    }
+
+    TEST(parsing_very_large_doubles)
+    {
+        test_double(pow(2.0, 64.0));
+        test_double(pow(2.0, 70.0));
+        test_double(pow(2.0, 80.0));
+        test_double(pow(2.0, 120.0));
+        test_double(pow(2.0, 240.0));
+        test_double(pow(2.0, 300.0));
+    }
+
+    TEST(parsing_very_small_doubles)
+    {
+        test_double(2.34e-308);
+        test_double(1e-308);
+    }
+
+    void test_integral(int number)
+    {
+        stringstream_t ss;
+        ss << number;
+        json::value num = json::value::parse(ss);
+        VERIFY_IS_TRUE(num.as_number().is_int32());
+        VERIFY_IS_TRUE(num.as_number().is_uint32());
+        VERIFY_IS_TRUE(num.as_number().is_int64());
+        VERIFY_IS_TRUE(num.as_number().is_uint64());
+
+        VERIFY_ARE_EQUAL(number, num.as_number().to_int32());
+        VERIFY_ARE_EQUAL(number, num.as_number().to_uint32());
+        VERIFY_ARE_EQUAL(number, num.as_number().to_int64());
+        VERIFY_ARE_EQUAL(number, num.as_number().to_uint64());
+    }
+
+    TEST(parsing_integral_types)
+    {
+        test_integral(0);
+        test_integral(1);
+        test_integral(INT_MAX / 2);
+        test_integral(INT_MAX);
+    }
+
+    TEST(int_double_limits)
+    {
+        utility::stringstream_t stream(utility::stringstream_t::in | utility::stringstream_t::out);
+        utility::stringstream_t oracleStream(utility::stringstream_t::in | utility::stringstream_t::out);
+
+        // unsigned int64 max
+        oracleStream.precision(std::numeric_limits<uint64_t>::digits10 + 2);
+        oracleStream << (std::numeric_limits<uint64_t>::max)();
+        json::value iMax((std::numeric_limits<uint64_t>::max)());
+        VERIFY_ARE_EQUAL(oracleStream.str(), iMax.serialize());
+        iMax.serialize(stream);
+        VERIFY_ARE_EQUAL(oracleStream.str(), stream.str());
+
+        // signed int64 min
+        stream.str(U(""));
+        oracleStream.str(U(""));
+        oracleStream.clear();
+        oracleStream << (std::numeric_limits<int64_t>::min)();
+        json::value iMin((std::numeric_limits<int64_t>::min)());
+        VERIFY_ARE_EQUAL(oracleStream.str(), iMin.serialize());
+        iMin.serialize(stream);
+        VERIFY_ARE_EQUAL(oracleStream.str(), stream.str());
+
+        // double max
+        stream.str(U(""));
+        oracleStream.str(U(""));
+        oracleStream.precision(std::numeric_limits<double>::digits10 + 2);
+        oracleStream << (std::numeric_limits<double>::max)();
+        json::value dMax((std::numeric_limits<double>::max)());
+        VERIFY_ARE_EQUAL(oracleStream.str(), dMax.serialize());
+        dMax.serialize(stream);
+        VERIFY_ARE_EQUAL(oracleStream.str(), stream.str());
+
+        // double min
+        stream.str(U(""));
+        oracleStream.str(U(""));
+        oracleStream << (std::numeric_limits<double>::min)();
+        json::value dMin((std::numeric_limits<double>::min)());
+        VERIFY_ARE_EQUAL(oracleStream.str(), dMin.serialize());
+        dMin.serialize(stream);
+        VERIFY_ARE_EQUAL(oracleStream.str(), stream.str());
+    }
+
+    TEST(compare_numbers)
+    {
+        // Make sure these are equal
+        VERIFY_ARE_EQUAL(json::value(3.14), json::value::parse(U("3.14")));
+        VERIFY_ARE_EQUAL(json::value(uint64_t(1234)), json::value::parse(U("1234")));
+        VERIFY_ARE_EQUAL(json::value(uint32_t(10)), json::value::parse(U("10")));
+
+        // These two are to verify that explicitly stated signed int was stored as unsigned int as we store all
+        // non-negative numbers as unsigned int
+        VERIFY_ARE_EQUAL(json::value(int32_t(10)), json::value::parse(U("10")));
+        VERIFY_ARE_EQUAL(json::value(int64_t(1234)), json::value::parse(U("1234")));
+
+        // These numbers would be equal if converted to double first. That is how we compared them before we had int64
+        // support.
+        VERIFY_ARE_NOT_EQUAL(json::value(int64_t(LLONG_MIN)), json::value(int64_t(LLONG_MIN + 1)));
+        VERIFY_ARE_NOT_EQUAL(json::value(uint64_t(ULLONG_MAX)), json::value(uint64_t(ULLONG_MAX - 1)));
+
+        // Checking boundary condition - zero
+        VERIFY_ARE_EQUAL(json::value(int32_t(0)), json::value::parse(U("-0")));
+        VERIFY_ARE_EQUAL(json::value(int64_t(0)), json::value::parse(U("-0")));
+        VERIFY_ARE_EQUAL(json::value::parse(U("0")), json::value::parse(U("-0")));
+    }
+
+} // SUITE(json_numbers_tests)
+
+} // 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
new file mode 100644 (file)
index 0000000..6c2ee93
--- /dev/null
@@ -0,0 +1,185 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * negative_parsing_tests.cpp
+ *
+ * Negative tests for JSON parsing.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "cpprest/json.h"
+#include "unittestpp.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace json_tests
+{
+template<typename T>
+void verify_json_throws(T& parseString)
+{
+    std::error_code ec;
+    VERIFY_THROWS(json::value::parse(parseString), json::json_exception);
+    auto value = json::value::parse(parseString, ec);
+    VERIFY_IS_TRUE(ec.value() > 0);
+    VERIFY_IS_TRUE(value.is_null());
+}
+
+SUITE(negative_parsing_tests)
+{
+    TEST(string_t)
+    {
+        verify_json_throws(U("\"\\k\""));
+        verify_json_throws(U("\" \" \""));
+        verify_json_throws(U("\"\\u23A\""));
+        verify_json_throws(U("\"\\uXY1A\""));
+        verify_json_throws(U("\"asdf"));
+        verify_json_throws(U("\\asdf"));
+        verify_json_throws(U("\"\"\"\""));
+
+        // '\', '"', and control characters must be escaped (0x1F and below).
+        verify_json_throws(U("\"\\\""));
+        verify_json_throws(U("\""));
+        utility::string_t str(U("\""));
+        str.append(1, 0x1F);
+        str.append(U("\""));
+        verify_json_throws(str);
+    }
+
+    TEST(numbers)
+    {
+        verify_json_throws(U("-"));
+        verify_json_throws(U("-."));
+        verify_json_throws(U("-e1"));
+        verify_json_throws(U("-1e"));
+        verify_json_throws(U("+1.1"));
+        verify_json_throws(U("1.1 E"));
+        verify_json_throws(U("1.1E-"));
+        verify_json_throws(U("1.1E.1"));
+        verify_json_throws(U("1.1E1.1"));
+        verify_json_throws(U("001.1"));
+        verify_json_throws(U("-.100"));
+        verify_json_throws(U("-.001"));
+        verify_json_throws(U(".1"));
+        verify_json_throws(U("0.1.1"));
+    }
+
+    // TFS 535589
+    void parse_help(utility::string_t str)
+    {
+        utility::stringstream_t ss1;
+        ss1 << str;
+        verify_json_throws(ss1);
+    }
+
+    TEST(objects)
+    {
+        verify_json_throws(U("}"));
+        parse_help(U("{"));
+        parse_help(U("{ 1, 10 }"));
+        parse_help(U("{ : }"));
+        parse_help(U("{ \"}"));
+        verify_json_throws(U("{"));
+        verify_json_throws(U("{ 1"));
+        verify_json_throws(U("{ \"}"));
+        verify_json_throws(U("{\"2\":"));
+        verify_json_throws(U("{\"2\":}"));
+        verify_json_throws(U("{\"2\": true"));
+        verify_json_throws(U("{\"2\": true false"));
+        verify_json_throws(U("{\"2\": true :false"));
+        verify_json_throws(U("{\"2\": false,}"));
+    }
+
+    TEST(arrays)
+    {
+        verify_json_throws(U("]"));
+        verify_json_throws(U("["));
+        verify_json_throws(U("[ 1"));
+        verify_json_throws(U("[ 1,"));
+        verify_json_throws(U("[ 1,]"));
+        verify_json_throws(U("[ 1 2]"));
+        verify_json_throws(U("[ \"1\" : 2]"));
+        parse_help(U("[,]"));
+        parse_help(U("[ \"]"));
+        parse_help(U("[\"2\", false,]"));
+    }
+
+    TEST(literals_not_lower_case)
+    {
+        verify_json_throws(U("NULL"));
+        verify_json_throws(U("FAlse"));
+        verify_json_throws(U("TRue"));
+    }
+
+    TEST(incomplete_literals)
+    {
+        verify_json_throws(U("nul"));
+        verify_json_throws(U("fal"));
+        verify_json_throws(U("tru"));
+    }
+
+    // TFS#501321
+    TEST(exception_string)
+    {
+        utility::string_t json_ip_str = U("");
+        verify_json_throws(json_ip_str);
+    }
+
+    TEST(boundary_chars)
+    {
+        utility::string_t str(U("\""));
+        str.append(1, 0x1F);
+        str.append(U("\""));
+        parse_help(str);
+    }
+
+    TEST(stream_left_over_chars)
+    {
+        std::stringbuf buf;
+        buf.sputn("[false]false", 12);
+        std::istream stream(&buf);
+        verify_json_throws(stream);
+    }
+
+// Test using Windows only API.
+#ifdef _WIN32
+    TEST(wstream_left_over_chars)
+    {
+        std::wstringbuf buf;
+        buf.sputn(L"[false]false", 12);
+        std::wistream stream(&buf);
+        verify_json_throws(stream);
+    }
+#endif
+
+    void garbage_impl(wchar_t ch)
+    {
+        utility::string_t ss(U("{\"a\" : 10, \"b\":"));
+
+        std::random_device rd;
+        std::mt19937 eng(rd());
+        std::uniform_int_distribution<unsigned int> dist(0, ch);
+
+        for (int i = 0; i < 2500; i++)
+            ss.push_back(static_cast<char_t>(dist(eng)));
+
+        verify_json_throws(ss);
+    }
+
+    TEST(garbage_1) { garbage_impl(0x7F); }
+
+    TEST(garbage_2) { garbage_impl(0xFF); }
+
+    TEST(garbage_3) { garbage_impl(0xFFFF); }
+}
+
+} // namespace json_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/json/parsing_tests.cpp b/Release/tests/functional/json/parsing_tests.cpp
new file mode 100644 (file)
index 0000000..bd43ee2
--- /dev/null
@@ -0,0 +1,943 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Tests for JSON parsing.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "cpprest/json.h"
+#include "unittestpp.h"
+#include <array>
+#include <iomanip>
+
+#if defined(_WIN32) || defined(__APPLE__)
+#include <regex>
+#elif (defined(ANDROID) || defined(__ANDROID__))
+#else
+// GCC 4.8 doesn't support regex very well, fall back to Boost. Revist in GCC 4.9.
+#include <boost/regex.hpp>
+#endif
+
+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
+{
+namespace json_tests
+{
+inline bool verify_parsing_error_msg(const std::string& str)
+{
+#if defined(_WIN32) || defined(__APPLE__)
+    auto spattern = "^\\* Line \\d+, Column \\d+ Syntax error: .+";
+    static std::regex pattern(spattern);
+    return std::regex_match(str, pattern, std::regex_constants::match_flag_type::match_not_null);
+#elif (defined(ANDROID) || defined(__ANDROID__))
+    return str.find("Syntax error: ") != std::string::npos;
+#else
+    auto spattern = "^\\* Line \\d+, Column \\d+ Syntax error: .+";
+    static boost::regex pattern(spattern);
+    return boost::regex_match(str, pattern, boost::regex_constants::match_flag_type::match_not_null);
+#endif
+}
+
+#if defined(_MSC_VER)
+#pragma warning(disable : 4127) // const expression
+#endif
+#define VERIFY_PARSING_THROW(target)                                                                                   \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        try                                                                                                            \
+        {                                                                                                              \
+            target;                                                                                                    \
+            VERIFY_IS_TRUE(false);                                                                                     \
+        }                                                                                                              \
+        catch (const json::json_exception& e)                                                                          \
+        {                                                                                                              \
+            VERIFY_IS_TRUE(verify_parsing_error_msg(e.what()));                                                        \
+        }                                                                                                              \
+        catch (...)                                                                                                    \
+        {                                                                                                              \
+            VERIFY_IS_TRUE(false);                                                                                     \
+        }                                                                                                              \
+    } while (false)
+
+SUITE(parsing_tests)
+{
+    TEST(stringstream_t)
+    {
+        utility::stringstream_t ss0;
+        ss0 << U("null");
+        json::value v0 = json::value::parse(ss0);
+
+        utility::stringstream_t ss1;
+        ss1 << U("17");
+        json::value v1 = json::value::parse(ss1);
+
+        utility::stringstream_t ss2;
+        ss2 << U("3.1415");
+        json::value v2 = json::value::parse(ss2);
+
+        utility::stringstream_t ss3;
+        ss3 << U("true");
+        json::value v3 = json::value::parse(ss3);
+
+        utility::stringstream_t ss4;
+        ss4 << U("\"Hello!\"");
+        json::value v4 = json::value::parse(ss4);
+
+        utility::stringstream_t ss8;
+        ss8 << U("{ \"a\" : 10 }");
+        json::value v8 = json::value::parse(ss8);
+
+        utility::stringstream_t ss9;
+        ss9 << U("[1,2,3,true]");
+        json::value v9 = json::value::parse(ss9);
+
+        VERIFY_ARE_EQUAL(v1.type(), json::value::Number);
+        VERIFY_ARE_EQUAL(v2.type(), json::value::Number);
+        VERIFY_ARE_EQUAL(v3.type(), json::value::Boolean);
+        VERIFY_ARE_EQUAL(v4.type(), json::value::String);
+        VERIFY_ARE_EQUAL(v8.type(), json::value::Object);
+        VERIFY_ARE_EQUAL(v9.type(), json::value::Array);
+    }
+
+    TEST(whitespace_failure) { VERIFY_PARSING_THROW(json::value::parse(U("  "))); }
+
+    static const std::array<char, 4> whitespace_chars = {{0x20, 0x09, 0x0A, 0x0D}};
+
+    TEST(whitespace_array)
+    {
+        // Try all the whitespace characters before/after all the structural characters
+        // whitespace characters according to RFC4627: space, horizontal tab, line feed or new line, carriage return
+        // structural characters: [{]}:,
+
+        // [,]
+        for (auto ch : whitespace_chars)
+        {
+            utility::string_t input;
+            input.append(2, ch);
+            input.append(U("["));
+            input.append(2, ch);
+            input.append(U("1"));
+            input.append(1, ch);
+            input.append(U(","));
+            input.append(4, ch);
+            input.append(U("2"));
+            input.append(1, ch);
+            input.append(U("]"));
+            input.append(2, ch);
+            json::value val = json::value::parse(input);
+            VERIFY_IS_TRUE(val.is_array());
+            VERIFY_ARE_EQUAL(U("1"), val[0].serialize());
+            VERIFY_ARE_EQUAL(U("2"), val[1].serialize());
+        }
+    }
+
+    TEST(whitespace_object)
+    {
+        // {:}
+        for (auto ch : whitespace_chars)
+        {
+            utility::string_t input;
+            input.append(2, ch);
+            input.append(U("{"));
+            input.append(2, ch);
+            input.append(U("\"1\""));
+            input.append(1, ch);
+            input.append(U(":"));
+            input.append(4, ch);
+            input.append(U("2"));
+            input.append(1, ch);
+            input.append(U("}"));
+            input.append(2, ch);
+            json::value val = json::value::parse(input);
+            VERIFY_IS_TRUE(val.is_object());
+            VERIFY_ARE_EQUAL(U("2"), val[U("1")].serialize());
+        }
+    }
+
+    TEST(string_t)
+    {
+        json::value str = json::value::parse(U("\"\\\"\""));
+        VERIFY_ARE_EQUAL(U("\""), str.as_string());
+
+        str = json::value::parse(U("\"\""));
+        VERIFY_ARE_EQUAL(U(""), str.as_string());
+
+        str = json::value::parse(U("\"\\\"ds\""));
+        VERIFY_ARE_EQUAL(U("\"ds"), str.as_string());
+
+        str = json::value::parse(U("\"\\\"\\\"\""));
+        VERIFY_ARE_EQUAL(U("\"\""), str.as_string());
+
+        // two character escapes
+        str = json::value::parse(U("\"\\\\\""));
+        VERIFY_ARE_EQUAL(U("\\"), str.as_string());
+
+        str = json::value::parse(U("\"\\/\""));
+        VERIFY_ARE_EQUAL(U("/"), str.as_string());
+
+        str = json::value::parse(U("\"\\b\""));
+        VERIFY_ARE_EQUAL(U("\b"), str.as_string());
+
+        str = json::value::parse(U("\"\\f\""));
+        VERIFY_ARE_EQUAL(U("\f"), str.as_string());
+
+        str = json::value::parse(U("\"\\n\""));
+        VERIFY_ARE_EQUAL(U("\n"), str.as_string());
+
+        str = json::value::parse(U("\"\\r\""));
+        VERIFY_ARE_EQUAL(U("\r"), str.as_string());
+
+        str = json::value::parse(U("\"\\t\""));
+        VERIFY_ARE_EQUAL(U("\t"), str.as_string());
+    }
+
+    TEST(escaped_unicode_string)
+    {
+        auto str = json::value::parse(U("\"\\u0041\""));
+        VERIFY_ARE_EQUAL(U("A"), str.as_string());
+
+        str = json::value::parse(U("\"\\u004B\""));
+        VERIFY_ARE_EQUAL(U("K"), str.as_string());
+
+        str = json::value::parse(U("\"\\u20AC\""));
+        // Euro sign as a hexadecimal UTF-8
+        const auto euro = to_string_t("\xE2\x82\xAC");
+        VERIFY_ARE_EQUAL(euro, str.as_string());
+
+        // UTF-16 character with surrogate pair
+        str = json::value::parse(U("\"\\ud83d\\ude00\""));
+        // Grinning Face emoji as a hexadecimal UTF-8
+        const auto emoji = to_string_t("\xF0\x9F\x98\x80");
+        VERIFY_ARE_EQUAL(emoji, str.as_string());
+
+        VERIFY_PARSING_THROW(json::value::parse(U("\"\\u0klB\"")));
+    }
+
+    TEST(escaping_control_characters)
+    {
+        std::vector<int> chars;
+        for (int i = 0; i <= 0x1F; ++i)
+        {
+            chars.push_back(i);
+        }
+        chars.push_back(0x5C); // backslash '\'
+        chars.push_back(0x22); // quotation '"'
+
+        for (int i : chars)
+        {
+            utility::stringstream_t ss;
+            ss << U("\"\\u") << std::uppercase << std::setfill(U('0')) << std::setw(4) << std::hex << i << U("\"");
+            const auto& str = ss.str();
+            auto expectedStr = str;
+            if (i == 0x08)
+            {
+                expectedStr = U("\"\\b\"");
+            }
+            else if (i == 0x09)
+            {
+                expectedStr = U("\"\\t\"");
+            }
+            else if (i == 0x0A)
+            {
+                expectedStr = U("\"\\n\"");
+            }
+            else if (i == 0x0C)
+            {
+                expectedStr = U("\"\\f\"");
+            }
+            else if (i == 0x0D)
+            {
+                expectedStr = U("\"\\r\"");
+            }
+            else if (i == 0x5C)
+            {
+                expectedStr = U("\"\\\\\"");
+            }
+            else if (i == 0x22)
+            {
+                expectedStr = U("\"\\\"\"");
+            }
+
+            // Try constructing a json string value directly.
+            utility::string_t schar;
+            schar.push_back(static_cast<utility::string_t::value_type>(i));
+            const auto& sv = json::value::string(schar);
+            VERIFY_ARE_EQUAL(expectedStr, sv.serialize());
+
+            // Try parsing a string
+            const auto& v = json::value::parse(str);
+            VERIFY_IS_TRUE(v.is_string());
+            VERIFY_ARE_EQUAL(expectedStr, v.serialize());
+
+            // Try parsing a stringstream.
+            const auto& ssv = json::value::parse(ss);
+            VERIFY_ARE_EQUAL(expectedStr, ssv.serialize());
+        }
+    }
+
+    TEST(comments_string)
+    {
+        // Nothing but a comment
+        VERIFY_PARSING_THROW(json::value::parse(U(" /* There's nothing but a comment here */  ")));
+        VERIFY_PARSING_THROW(json::value::parse(U(" // There's nothing but a comment here\n")));
+
+        // Some invalid comments
+        VERIFY_PARSING_THROW(json::value::parse(U(" -22 /*/")));
+        VERIFY_PARSING_THROW(json::value::parse(U(" -22 /* /* nested */ */")));
+
+        // Correctly placed comments
+        json::value num1 = json::value::parse(U("-22 // This is a trailing comment\n"));
+        VERIFY_ARE_EQUAL(-22, num1.as_double());
+        num1 = json::value::parse(U(" -22 /* This is a trailing comment with a // nested\n comment */"));
+        VERIFY_ARE_EQUAL(-22, num1.as_double());
+        json::value num2 = json::value::parse(U("// This is a leading comment\n -22"));
+        VERIFY_ARE_EQUAL(-22, num2.as_double());
+        json::value num3 = json::value::parse(U("-22 /* This is a trailing comment */"));
+        VERIFY_ARE_EQUAL(-22, num3.as_double());
+        json::value num4 = json::value::parse(U("/* This is a leading comment */ -22"));
+        VERIFY_ARE_EQUAL(-22, num4.as_double());
+        json::value num5 = json::value::parse(U("-22 /***/"));
+        VERIFY_ARE_EQUAL(-22, num5.as_double());
+
+        json::value obj1 = json::value::parse(U("{// A comment in the middle of an empty object\n}"));
+        VERIFY_IS_TRUE(obj1.is_object());
+        VERIFY_ARE_EQUAL(0u, obj1.size());
+        json::value obj2 = json::value::parse(U("{/* A comment in the middle of an empty object */}"));
+        VERIFY_IS_TRUE(obj2.is_object());
+        VERIFY_ARE_EQUAL(0u, obj2.size());
+        json::value obj3 = json::value::parse(U("{ \"test\" : // A comment in the middle of a non-empty object\n 2}"));
+        VERIFY_IS_TRUE(obj3.is_object());
+        VERIFY_ARE_EQUAL(1u, obj3.size());
+        json::value obj4 = json::value::parse(U("{ \"test\" : /* A comment in the middle of a non-empty object */ 2}"));
+        VERIFY_IS_TRUE(obj4.is_object());
+        VERIFY_ARE_EQUAL(1u, obj4.size());
+
+        json::value arr1 = json::value::parse(U("[// A comment in the middle of an empty array\n]"));
+        VERIFY_IS_TRUE(arr1.is_array());
+        VERIFY_ARE_EQUAL(0u, arr1.size());
+        json::value arr2 = json::value::parse(U("[/* A comment in the middle of an empty array */]"));
+        VERIFY_IS_TRUE(arr2.is_array());
+        VERIFY_ARE_EQUAL(0u, arr2.size());
+        json::value arr3 = json::value::parse(U("[ 1, // A comment in the middle of a non-array\n 2]"));
+        VERIFY_IS_TRUE(arr3.is_array());
+        VERIFY_ARE_EQUAL(2u, arr3.size());
+        json::value arr4 = json::value::parse(U("[ 1, /* A comment in the middle of a non-empty array */ 2]"));
+        VERIFY_IS_TRUE(arr4.is_array());
+        VERIFY_ARE_EQUAL(2u, arr4.size());
+    }
+
+    TEST(comments_stream)
+    {
+        // Nothing but a comment
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U(" /* There's nothing but a comment here */ ");
+            VERIFY_PARSING_THROW(json::value::parse(stream));
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U(" // There's nothing but a comment here\n ");
+            VERIFY_PARSING_THROW(json::value::parse(stream));
+        }
+
+        // Some invalid comments
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U(" -22 /*/");
+            VERIFY_PARSING_THROW(json::value::parse(stream));
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U(" -22 /* /* nested */ */");
+            VERIFY_PARSING_THROW(json::value::parse(stream));
+        }
+
+        // Correctly placed comments
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("-22 // This is a trailing comment\n");
+            json::value num1 = json::value::parse(stream);
+            VERIFY_ARE_EQUAL(-22, num1.as_double());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U(" -22 /* This is a trailing comment with a // nested\n comment */");
+            json::value num1 = json::value::parse(stream);
+            VERIFY_ARE_EQUAL(-22, num1.as_double());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("// This is a leading comment\n -22");
+            json::value num2 = json::value::parse(stream);
+            VERIFY_ARE_EQUAL(-22, num2.as_double());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("-22 /* This is a trailing comment */");
+            json::value num3 = json::value::parse(stream);
+            VERIFY_ARE_EQUAL(-22, num3.as_double());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("/* This is a leading comment */ -22");
+            json::value num4 = json::value::parse(stream);
+            VERIFY_ARE_EQUAL(-22, num4.as_double());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("-22 /***/");
+            json::value num4 = json::value::parse(stream);
+            VERIFY_ARE_EQUAL(-22, num4.as_double());
+        }
+
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("{// A comment in the middle of an empty object\n}");
+            json::value obj1 = json::value::parse(stream);
+            VERIFY_IS_TRUE(obj1.is_object());
+            VERIFY_ARE_EQUAL(0u, obj1.size());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("{/* A comment in the middle of an empty object */}");
+            json::value obj2 = json::value::parse(stream);
+            VERIFY_IS_TRUE(obj2.is_object());
+            VERIFY_ARE_EQUAL(0u, obj2.size());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("{ \"test1\" : // A comment in the middle of a non-empty object\n 2}");
+            json::value obj3 = json::value::parse(stream);
+            VERIFY_IS_TRUE(obj3.is_object());
+            VERIFY_ARE_EQUAL(1u, obj3.size());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("{ \"test1\" : /* A comment in the middle of a non-empty object */ 2}");
+            json::value obj4 = json::value::parse(stream);
+            VERIFY_IS_TRUE(obj4.is_object());
+            VERIFY_ARE_EQUAL(1u, obj4.size());
+        }
+
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("[// A comment in the middle of an empty array\n]");
+            json::value arr1 = json::value::parse(stream);
+            VERIFY_IS_TRUE(arr1.is_array());
+            VERIFY_ARE_EQUAL(0u, arr1.size());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("[/* A comment in the middle of an empty array */]");
+            json::value arr2 = json::value::parse(stream);
+            VERIFY_IS_TRUE(arr2.is_array());
+            VERIFY_ARE_EQUAL(0u, arr2.size());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("[ 1, // A comment in the middle of a non-array\n 2]");
+            json::value arr3 = json::value::parse(stream);
+            VERIFY_IS_TRUE(arr3.is_array());
+            VERIFY_ARE_EQUAL(2u, arr3.size());
+        }
+        {
+            std::basic_stringstream<utility::char_t> stream;
+            stream << U("[ 1, /* A comment in the middle of a non-empty array */ 2]");
+            json::value arr4 = json::value::parse(stream);
+            VERIFY_IS_TRUE(arr4.is_array());
+            VERIFY_ARE_EQUAL(2u, arr4.size());
+        }
+    }
+
+    TEST(empty_object_array)
+    {
+        json::value obj = json::value::parse(U("{}"));
+        VERIFY_IS_TRUE(obj.is_object());
+        VERIFY_ARE_EQUAL(0u, obj.size());
+
+        json::value arr = json::value::parse(U("[]"));
+        VERIFY_IS_TRUE(arr.is_array());
+        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("\"Ī“ĪæĪŗĪ¹Ī¼Ī®\""));
+        auto s = data2.serialize();
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4566)
+#endif
+        VERIFY_ARE_EQUAL(s, U("\"Ī“ĪæĪŗĪ¹Ī¼Ī®\""));
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+    }
+
+    TEST(byte_ptr_parsing_array)
+    {
+        char s[] = "[ \"test1\",true]";
+        json::value v = json::value::parse(s);
+        std::stringstream ss;
+        ss << s;
+        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;
+        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 vv = json::value::parse(ss);
+        VERIFY_ARE_EQUAL(v, vv);
+
+        auto s2 = v.serialize();
+        VERIFY_ARE_EQUAL(s2, U("{\"test1\":true}"));
+
+        std::stringstream os;
+        vv.serialize(os);
+        VERIFY_ARE_EQUAL(s2, to_string_t(os.str()));
+    }
+
+    TEST(Japanese)
+    {
+        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 vv = json::value::parse(ss);
+        VERIFY_ARE_EQUAL(v, vv);
+
+        auto s2 = v.serialize();
+        VERIFY_ARE_EQUAL(s2, ws);
+
+        std::stringstream os;
+        vv.serialize(os);
+        VERIFY_ARE_EQUAL(s2, to_string_t(os.str()));
+    }
+
+    TEST(Russian)
+    {
+        utility::string_t ws = U("{\"results\":[{\"id\":272655310,\"name\":\"ŠŠ½Š“рŠµŠ¹ Š˜Š²Ā“Š°Š½Š¾Š²\"}]}");
+        json::value v1 = json::value::parse(ws);
+        auto s2 = v1.serialize();
+
+        VERIFY_ARE_EQUAL(s2, ws);
+
+        std::string s = to_utf8string(ws);
+
+        std::stringstream ss;
+        ss << s;
+        json::value v2 = json::value::parse(ss);
+        auto s3 = v2.serialize();
+
+        VERIFY_ARE_EQUAL(s3, ws);
+    }
+
+    utility::string_t make_deep_json_string(size_t depth)
+    {
+        utility::string_t strval;
+        for (size_t i = 0; i < depth; ++i)
+        {
+            strval += U("{ \"a\" : 10, \"b\" : ");
+        }
+        strval += U("20");
+        for (size_t i = 0; i < depth; ++i)
+        {
+            strval += U("}");
+        }
+        return strval;
+    }
+
+    TEST(deeply_nested)
+    {
+#if defined(__APPLE__)
+        size_t safeDepth = 32;
+        size_t overDepth = 33;
+#else
+        size_t safeDepth = 128;
+        size_t overDepth = 129;
+#endif
+
+        // This should parse without issues:
+        auto strGood = make_deep_json_string(safeDepth);
+        json::value::parse(strGood);
+
+        // But this one should throw:
+        auto strBad = make_deep_json_string(overDepth);
+        VERIFY_PARSING_THROW(json::value::parse(strBad));
+    }
+
+    static bool compare_pairs(const std::pair<utility::string_t, json::value>& p1,
+                              const std::pair<utility::string_t, json::value>& p2)
+    {
+        return p1.first < p2.first;
+    }
+
+    TEST(unsorted_object_parsing)
+    {
+        utility::stringstream_t ss;
+        ss << U("{\"z\":2, \"a\":1}");
+        json::value v = json::value::parse(ss);
+        auto& obj = v.as_object();
+
+        VERIFY_ARE_NOT_EQUAL(obj.find(U("a")), obj.end());
+        VERIFY_ARE_NOT_EQUAL(obj.find(U("z")), obj.end());
+        VERIFY_ARE_EQUAL(obj[U("a")], 1);
+        VERIFY_ARE_EQUAL(obj[U("z")], 2);
+        VERIFY_ARE_EQUAL(obj.size(), 2);
+
+        VERIFY_IS_TRUE(::std::is_sorted(obj.begin(), obj.end(), compare_pairs));
+    }
+
+    TEST(keep_order_while_parsing)
+    {
+        utility::stringstream_t ss;
+        ss << U("{\"k\":3, \"j\":2, \"i\":1}");
+
+        json::keep_object_element_order(true);
+        struct restore
+        {
+            ~restore() { json::keep_object_element_order(false); }
+        } _;
+
+        json::value v = json::value::parse(ss);
+        auto& obj = v.as_object();
+
+        // Make sure collection stays unsorted:
+        auto b = obj.begin();
+        VERIFY_ARE_EQUAL(b[0].first, U("k"));
+        VERIFY_ARE_EQUAL(b[1].first, U("j"));
+        VERIFY_ARE_EQUAL(b[2].first, U("i"));
+
+        // Make sure lookup still works:
+        auto val_i = obj[U("i")];
+        VERIFY_ARE_EQUAL(val_i.as_integer(), 1);
+
+        auto val_j = obj[U("j")];
+        VERIFY_ARE_EQUAL(val_j.as_integer(), 2);
+
+        // Make sure 'a' goes to the back of the collection, and
+        // can be looked up
+        obj[U("a")] = 4;
+        b = obj.begin();
+        VERIFY_ARE_EQUAL(b[3].first, U("a"));
+        VERIFY_ARE_EQUAL(obj[U("a")].as_integer(), 4);
+    }
+
+    TEST(non_default_locale, "Ignore:Android", "Locale unsupported on Android")
+    {
+        std::string originalLocale = setlocale(LC_ALL, nullptr);
+#ifdef _WIN32
+        std::string changedLocale("fr-FR");
+#else
+        std::string changedLocale("fr_FR.utf8");
+#endif
+
+        // If locale isn't installed on system just silently pass.
+        if (setlocale(LC_ALL, changedLocale.c_str()) != nullptr)
+        {
+            // string serialize
+            utility::string_t str(U("[true,false,-1.55,5,null,{\"abc\":5555}]"));
+            json::value v = json::value::parse(str);
+            VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
+            VERIFY_ARE_EQUAL(str, v.serialize());
+            VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
+
+            setlocale(LC_ALL, originalLocale.c_str());
+            setlocale(LC_NUMERIC, changedLocale.c_str());
+
+            // cpprestsdk stream serialize
+            utility::stringstream_t stream;
+            stream << v;
+            utility::string_t serializedStr;
+            stream >> serializedStr;
+            VERIFY_ARE_EQUAL(str, serializedStr);
+
+            // std stream serialize
+            std::stringstream stdStream;
+            v.serialize(stdStream);
+            std::string stdStr;
+            stdStream >> stdStr;
+            VERIFY_ARE_EQUAL(str, utility::conversions::to_string_t(stdStr));
+
+            setlocale(LC_ALL, originalLocale.c_str());
+        }
+    }
+
+    template<typename T>
+    void error_code_helper(T & jsonData)
+    {
+        std::error_code err;
+        auto parsedObject = web::json::value::parse(jsonData, err);
+        VERIFY_IS_TRUE(err.value() == 0);
+        VERIFY_IS_TRUE(!parsedObject.is_null());
+    }
+
+    TEST(parse_overload_success)
+    {
+        std::error_code err;
+        utility::string_t valueStr(U("\"JSONString\""));
+        utility::string_t arrStr(U("[true,false,-1.55,5,null,{\"abc\":5555}]"));
+        utility::string_t objStr(U("{\"k\":3, \"j\":2, \"i\":1}"));
+
+        error_code_helper(valueStr);
+        error_code_helper(arrStr);
+        error_code_helper(objStr);
+
+        utility::stringstream_t valueStringStream;
+        utility::stringstream_t arrayStringStream;
+        utility::stringstream_t objStringStream;
+
+        valueStringStream << valueStr;
+        arrayStringStream << arrStr;
+        objStringStream << objStr;
+
+        error_code_helper(valueStringStream);
+        error_code_helper(arrayStringStream);
+        error_code_helper(objStringStream);
+
+#ifdef _WIN32
+        std::wstringbuf buf;
+
+        buf.sputn(valueStr.c_str(), valueStr.size());
+        std::wistream valStream(&buf);
+        error_code_helper(valStream);
+
+        buf.sputn(arrStr.c_str(), arrStr.size());
+        std::wistream arrStream(&buf);
+        error_code_helper(arrStream);
+
+        buf.sputn(objStr.c_str(), objStr.size());
+        std::wistream objStream(&buf);
+        error_code_helper(objStream);
+#endif
+    }
+
+    TEST(parse_overload_failed)
+    {
+        std::error_code err, streamErr, iStreamErr;
+        utility::string_t str(U("JSONString"));
+        utility::string_t arrStr(U("[true, false"));
+        json::value parsedObject = json::value::parse(str, err);
+
+        VERIFY_IS_TRUE(err.value() > 0);
+        VERIFY_IS_TRUE(parsedObject.is_null());
+
+        utility::stringstream_t stream;
+        stream << str;
+
+        parsedObject = json::value::parse(arrStr, streamErr);
+        VERIFY_IS_TRUE(streamErr.value() > 0);
+        VERIFY_IS_TRUE(parsedObject.is_null());
+
+#ifdef _WIN32
+        std::wstringbuf buf;
+        buf.sputn(str.c_str(), str.size());
+        std::wistream iStream(&buf);
+        parsedObject = json::value::parse(str, iStreamErr);
+        VERIFY_IS_TRUE(iStreamErr.value() > 0);
+        VERIFY_IS_TRUE(parsedObject.is_null());
+#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
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/json/to_as_and_operators_tests.cpp b/Release/tests/functional/json/to_as_and_operators_tests.cpp
new file mode 100644 (file)
index 0000000..cbf2a92
--- /dev/null
@@ -0,0 +1,515 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Tests for to_*, as_*, and operators on JSON values.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "cpprest/json.h"
+#include "unittestpp.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace json_tests
+{
+SUITE(to_as_and_operators_tests)
+{
+    TEST(to_string)
+    {
+        utility::stringstream_t stream(utility::stringstream_t::in | utility::stringstream_t::out);
+
+        // null
+        json::value n;
+        VERIFY_ARE_EQUAL(U("null"), n.serialize());
+        n.serialize(stream);
+        VERIFY_ARE_EQUAL(U("null"), stream.str());
+
+        // bool - true
+        stream.str(U(""));
+        json::value b(true);
+        VERIFY_ARE_EQUAL(U("true"), b.serialize());
+        b.serialize(stream);
+        VERIFY_ARE_EQUAL(U("true"), stream.str());
+
+        // bool - false
+        stream.str(U(""));
+        json::value b2(false);
+        VERIFY_ARE_EQUAL(U("false"), b2.serialize());
+        b2.serialize(stream);
+        VERIFY_ARE_EQUAL(U("false"), stream.str());
+
+        // number - int
+        stream.str(U(""));
+        json::value num(44);
+        VERIFY_ARE_EQUAL(U("44"), num.serialize());
+        num.serialize(stream);
+        VERIFY_ARE_EQUAL(U("44"), stream.str());
+
+        // number - double
+        stream.str(U(""));
+        json::value dNum(11.5);
+        VERIFY_ARE_EQUAL(U("11.5"), dNum.serialize());
+        dNum.serialize(stream);
+        VERIFY_ARE_EQUAL(U("11.5"), stream.str());
+
+        // string
+        stream.str(U(""));
+        json::value string = json::value::string(U("hehehe"));
+        VERIFY_ARE_EQUAL(U("\"hehehe\""), string.serialize());
+        string.serialize(stream);
+        VERIFY_ARE_EQUAL(U("\"hehehe\""), stream.str());
+
+        // object - with values created from parsing
+        stream.str(U(""));
+        const utility::string_t strValue1(U("{ \"key\" : true }"));
+        const utility::string_t strValue2(U("{\"key\":true}"));
+        json::value obj1 = json::value::parse(strValue1);
+        VERIFY_ARE_EQUAL(strValue2, obj1.serialize());
+        json::value obj2 = json::value::parse(strValue2);
+        VERIFY_ARE_EQUAL(strValue2, obj2.serialize());
+        obj1.serialize(stream);
+        VERIFY_ARE_EQUAL(strValue2, stream.str());
+
+        // object - with values added
+        stream.str(U(""));
+        json::value obj3 = json::value::object();
+        obj3[U("key")] = json::value(true);
+        VERIFY_ARE_EQUAL(strValue2, obj3.serialize());
+        obj3.serialize(stream);
+        VERIFY_ARE_EQUAL(strValue2, stream.str());
+
+        // array
+        stream.str(U(""));
+        json::value arr = json::value::array();
+        arr[0] = json::value::string(U("Here"));
+        arr[1] = json::value(true);
+        VERIFY_ARE_EQUAL(U("[\"Here\",true]"), arr.serialize());
+        VERIFY_ARE_EQUAL(U("[\"Here\",true]"), arr.serialize());
+        arr.serialize(stream);
+        VERIFY_ARE_EQUAL(U("[\"Here\",true]"), stream.str());
+    }
+
+    TEST(empty_arrays_objects)
+    {
+        // array
+        auto arr = json::value::parse(U("[   ]"));
+        VERIFY_ARE_EQUAL(U("[]"), arr.serialize());
+
+        // object
+        auto obj = json::value::parse(U("{   }"));
+        VERIFY_ARE_EQUAL(U("{}"), obj.serialize());
+    }
+
+    void verify_escaped_chars(const utility::string_t& str1, const utility::string_t& str2)
+    {
+        json::value j1 = json::value::string(str1);
+        VERIFY_ARE_EQUAL(str2, j1.serialize());
+    }
+
+    void verify_unescaped_chars(const utility::string_t& str1, const utility::string_t& str2)
+    {
+        json::value j1 = json::value::string(str1, false);
+        VERIFY_ARE_EQUAL(str2, j1.serialize());
+    }
+
+    TEST(to_string_escaped_chars)
+    {
+        verify_escaped_chars(U(" \" "), U("\" \\\" \""));
+        verify_escaped_chars(U(" \b "), U("\" \\b \""));
+        verify_escaped_chars(U(" \f "), U("\" \\f \""));
+        verify_escaped_chars(U(" \n "), U("\" \\n \""));
+        verify_escaped_chars(U(" \r "), U("\" \\r \""));
+        verify_escaped_chars(U(" \t "), U("\" \\t \""));
+
+        json::value obj = json::value::object();
+        obj[U(" \t ")] = json::value::string(U(" \b "));
+
+        json::value arr = json::value::array();
+        arr[0] = json::value::string(U(" \f "));
+
+        VERIFY_ARE_EQUAL(U("{\" \\t \":\" \\b \"}"), obj.serialize());
+        VERIFY_ARE_EQUAL(U("[\" \\f \"]"), arr.serialize());
+
+        utility::string_t str(U("{\"hello\":\" \\\"here's looking at you kid\\\" \\r \"}"));
+        json::value obj2 = json::value::parse(str);
+
+        VERIFY_ARE_EQUAL(str, obj2.serialize());
+    }
+
+    TEST(to_string_unescaped_chars)
+    {
+        verify_unescaped_chars(U(" \" "), U("\" \" \""));
+        verify_unescaped_chars(U(" \b "), U("\" \b \""));
+        verify_unescaped_chars(U(" \f "), U("\" \f \""));
+        verify_unescaped_chars(U(" \n "), U("\" \n \""));
+        verify_unescaped_chars(U(" \r "), U("\" \r \""));
+        verify_unescaped_chars(U(" \t "), U("\" \t \""));
+
+        json::value obj = json::value::object();
+        obj[U(" \t ")] = json::value::string(U(" \b "), false);
+
+        json::value arr = json::value::array();
+        arr[0] = json::value::string(U(" \f "), false);
+
+        VERIFY_ARE_EQUAL(U("{\" \\t \":\" \b \"}"), obj.serialize());
+        VERIFY_ARE_EQUAL(U("[\" \f \"]"), arr.serialize());
+    }
+
+    TEST(as_string)
+    {
+        json::value b(false);
+        VERIFY_THROWS(b.as_string(), json::json_exception);
+        VERIFY_THROWS(b.as_string(), json::json_exception);
+
+        utility::string_t data(U("HERE IS A STRING"));
+        utility::string_t wdata(data.begin(), data.end());
+        json::value str = json::value::string(data);
+        VERIFY_ARE_EQUAL(data, str.as_string());
+        VERIFY_ARE_EQUAL(wdata, str.as_string());
+    }
+
+    TEST(as_copy_constructor)
+    {
+        auto arr = json::value::array();
+        arr[0] = json::value::number(44);
+        arr[1] = json::value::string(U("abc"));
+        json::array arrCopy = arr.as_array();
+        VERIFY_ARE_EQUAL(2, arrCopy.size());
+        VERIFY_ARE_EQUAL(2, arr.size());
+        VERIFY_ARE_EQUAL(44, arrCopy[0].as_integer());
+        VERIFY_ARE_EQUAL(U("abc"), arrCopy[1].as_string());
+        VERIFY_ARE_EQUAL(44, arr[0].as_integer());
+        VERIFY_ARE_EQUAL(U("abc"), arr[1].as_string());
+
+        auto obj = json::value::object();
+        obj[U("abc")] = json::value::number(123);
+        json::object objCopy = obj.as_object();
+        VERIFY_ARE_EQUAL(1, objCopy.size());
+        VERIFY_ARE_EQUAL(1, obj.size());
+        VERIFY_ARE_EQUAL(123, objCopy[U("abc")].as_integer());
+        VERIFY_ARE_EQUAL(123, obj[U("abc")].as_integer());
+
+        auto num = json::value::number(44);
+        json::number numCopy = num.as_number();
+        VERIFY_ARE_EQUAL(44, num.as_integer());
+        VERIFY_ARE_EQUAL(44, numCopy.to_int32());
+    }
+
+    TEST(as_bool_as_double_as_string)
+    {
+        utility::stringstream_t ss1;
+        ss1 << U("17");
+        json::value v1 = json::value::parse(ss1);
+
+        utility::stringstream_t ss2;
+        ss2 << U("3.1415");
+        json::value v2 = json::value::parse(ss2);
+
+        utility::stringstream_t ss3;
+        ss3 << U("true");
+        json::value v3 = json::value::parse(ss3);
+
+        utility::stringstream_t ss4;
+        ss4 << U("\"Hello!\"");
+        json::value v4 = json::value::parse(ss4);
+
+        utility::stringstream_t ss8;
+        ss8 << U("{ \"a\" : 10, \"b\" : 4711.17, \"c\" : false }");
+        json::value v8 = json::value::parse(ss8);
+
+        utility::stringstream_t ss9;
+        ss9 << U("[1,2,3,true]");
+        json::value v9 = json::value::parse(ss9);
+
+        VERIFY_ARE_EQUAL(v1.as_double(), 17);
+        VERIFY_ARE_EQUAL(v2.as_double(), 3.1415);
+        VERIFY_IS_TRUE(v3.as_bool());
+        VERIFY_ARE_EQUAL(v4.as_string(), U("Hello!"));
+        VERIFY_ARE_EQUAL(v4.as_string(), U("Hello!"));
+
+        VERIFY_ARE_EQUAL(v8[U("a")].as_double(), 10);
+        VERIFY_ARE_EQUAL(v8[U("b")].as_double(), 4711.17);
+        VERIFY_ARE_EQUAL(v8[U("a")].as_integer(), 10);
+        VERIFY_IS_FALSE(v8[U("c")].as_bool());
+
+        VERIFY_ARE_EQUAL(v9[0].as_double(), 1);
+        VERIFY_ARE_EQUAL(v9[1].as_double(), 2);
+        VERIFY_ARE_EQUAL(v9[2].as_double(), 3);
+        VERIFY_IS_TRUE(v9[3].as_bool());
+    }
+
+    TEST(to_stream_operator)
+    {
+        utility::string_t str(U("\"JSON STRING\""));
+        json::value value = json::value::parse(str);
+        utility::stringstream_t stream;
+        stream << value;
+        VERIFY_ARE_EQUAL(str, stream.str());
+    }
+
+    TEST(from_stream_operator)
+    {
+        utility::string_t str(U("\"JSON STRING!\""));
+        utility::stringstream_t stream;
+        stream << str;
+        json::value value;
+        stream >> value;
+        VERIFY_IS_TRUE(value.is_string());
+        VERIFY_ARE_EQUAL(str, value.serialize());
+    }
+
+    TEST(negative_is_tests)
+    {
+        json::value b(true);
+        json::value str(U("string"));
+        json::value d(22.5);
+        json::value n;
+        json::value a = json::value::array(2);
+        json::value o = json::value::object();
+
+        VERIFY_IS_FALSE(b.is_number());
+        VERIFY_IS_FALSE(str.is_boolean());
+        VERIFY_IS_FALSE(d.is_string());
+        VERIFY_IS_FALSE(a.is_object());
+        VERIFY_IS_FALSE(o.is_array());
+        VERIFY_IS_FALSE(n.is_string());
+        VERIFY_IS_FALSE(str.is_null());
+    }
+
+    TEST(negative_index_operator_boolean)
+    {
+        json::value v = json::value::boolean(true);
+
+        VERIFY_THROWS(v[0], json::json_exception);
+        VERIFY_THROWS(v[U("H")], json::json_exception);
+        VERIFY_THROWS(v[U("A")], json::json_exception);
+    }
+
+    TEST(negative_get_field_object)
+    {
+        json::value v;
+
+        v[U("a")] = json::value::number(1);
+        VERIFY_IS_TRUE(v.is_object());
+        VERIFY_ARE_EQUAL(v[U("a")].as_integer(), 1);
+        VERIFY_IS_TRUE(v[U("b")].is_null());
+        VERIFY_THROWS(v[0], json::json_exception);
+    }
+
+    TEST(negative_get_element_array)
+    {
+        json::value v;
+
+        v[0] = json::value::number(1);
+        VERIFY_ARE_EQUAL(v[0].as_integer(), 1);
+        VERIFY_IS_TRUE(v[1].is_null());
+        VERIFY_THROWS(v[U("a")], json::json_exception);
+    }
+
+    TEST(has_field_object)
+    {
+        json::value v1;
+
+        v1[U("a")] = json::value::number(1);
+        v1[U("b")] = json::value::boolean(true);
+        v1[U("c")] = json::value::string(U("a string"));
+        v1[U("d")] = json::value::array({});
+        json::value sub_field;
+        sub_field[U("x")] = json::value::number(1);
+        v1[U("e")] = sub_field;
+
+        VERIFY_IS_TRUE(v1.has_field(U("a")));
+        VERIFY_IS_TRUE(v1.has_field(U("b")));
+        VERIFY_IS_TRUE(v1.has_field(U("c")));
+        VERIFY_IS_TRUE(v1.has_field(U("d")));
+        VERIFY_IS_TRUE(v1.has_field(U("e")));
+        VERIFY_IS_FALSE(v1.has_field(U("f")));
+
+        VERIFY_IS_TRUE(v1.has_number_field(U("a")));
+        VERIFY_IS_TRUE(v1.has_integer_field(U("a")));
+        VERIFY_IS_FALSE(v1.has_double_field(U("a")));
+        VERIFY_IS_FALSE(v1.has_boolean_field(U("a")));
+        VERIFY_IS_FALSE(v1.has_string_field(U("a")));
+        VERIFY_IS_FALSE(v1.has_array_field(U("a")));
+        VERIFY_IS_FALSE(v1.has_object_field(U("a")));
+
+        VERIFY_IS_TRUE(v1.has_boolean_field(U("b")));
+        VERIFY_IS_FALSE(v1.has_number_field(U("b")));
+        VERIFY_IS_FALSE(v1.has_integer_field(U("b")));
+        VERIFY_IS_FALSE(v1.has_double_field(U("b")));
+        VERIFY_IS_FALSE(v1.has_string_field(U("b")));
+        VERIFY_IS_FALSE(v1.has_array_field(U("b")));
+        VERIFY_IS_FALSE(v1.has_object_field(U("b")));
+
+        VERIFY_IS_TRUE(v1.has_string_field(U("c")));
+        VERIFY_IS_FALSE(v1.has_boolean_field(U("c")));
+        VERIFY_IS_FALSE(v1.has_number_field(U("c")));
+        VERIFY_IS_FALSE(v1.has_integer_field(U("c")));
+        VERIFY_IS_FALSE(v1.has_double_field(U("c")));
+        VERIFY_IS_FALSE(v1.has_array_field(U("c")));
+        VERIFY_IS_FALSE(v1.has_object_field(U("c")));
+
+        VERIFY_IS_TRUE(v1.has_array_field(U("d")));
+        VERIFY_IS_FALSE(v1.has_string_field(U("d")));
+        VERIFY_IS_FALSE(v1.has_boolean_field(U("d")));
+        VERIFY_IS_FALSE(v1.has_number_field(U("d")));
+        VERIFY_IS_FALSE(v1.has_integer_field(U("d")));
+        VERIFY_IS_FALSE(v1.has_double_field(U("d")));
+        VERIFY_IS_FALSE(v1.has_object_field(U("d")));
+
+        VERIFY_IS_TRUE(v1.has_object_field(U("e")));
+        VERIFY_IS_FALSE(v1.has_array_field(U("e")));
+        VERIFY_IS_FALSE(v1.has_string_field(U("e")));
+        VERIFY_IS_FALSE(v1.has_boolean_field(U("e")));
+        VERIFY_IS_FALSE(v1.has_number_field(U("e")));
+        VERIFY_IS_FALSE(v1.has_integer_field(U("e")));
+        VERIFY_IS_FALSE(v1.has_double_field(U("e")));
+
+        json::value v2;
+
+        v2[0] = json::value::number(1);
+        VERIFY_IS_FALSE(v2.has_field(U("0")));
+        VERIFY_IS_FALSE(v2.has_field(U("b")));
+    }
+
+    TEST(negative_as_tests)
+    {
+        json::value b(true);
+        VERIFY_THROWS(b.as_double(), json::json_exception);
+        VERIFY_THROWS(b.as_integer(), json::json_exception);
+        VERIFY_THROWS(b.as_string(), json::json_exception);
+
+        json::value str = json::value::string(U("string"));
+        VERIFY_THROWS(str.as_double(), json::json_exception);
+        VERIFY_THROWS(str.as_bool(), json::json_exception);
+        VERIFY_THROWS(str.as_integer(), json::json_exception);
+
+        json::value d(2.0f);
+        VERIFY_THROWS(d.as_string(), json::json_exception);
+        VERIFY_THROWS(d.as_bool(), json::json_exception);
+
+        json::value i(11);
+        VERIFY_THROWS(i.as_bool(), json::json_exception);
+        VERIFY_THROWS(i.as_string(), json::json_exception);
+    }
+
+    TEST(erase_array_index)
+    {
+        json::value a = json::value::array(4);
+        a[0] = json::value(1);
+        a[1] = json::value(2);
+        a[2] = json::value(3);
+        a[3] = json::value(4);
+
+        a.erase(1);
+        VERIFY_ARE_EQUAL(3, a.size());
+        VERIFY_ARE_EQUAL(1, a[0].as_integer());
+        VERIFY_ARE_EQUAL(3, a[1].as_integer());
+        VERIFY_ARE_EQUAL(4, a[2].as_integer());
+        a.as_array().erase(2);
+        VERIFY_ARE_EQUAL(2, a.size());
+        VERIFY_ARE_EQUAL(1, a[0].as_integer());
+        VERIFY_ARE_EQUAL(3, a[1].as_integer());
+    }
+
+    TEST(erase_array_iter)
+    {
+        json::value a = json::value::array(3);
+        a[0] = json::value(1);
+        a[1] = json::value(2);
+        a[2] = json::value(3);
+
+        auto iter = a.as_array().begin() + 1;
+        auto afterLoc = a.as_array().erase(iter);
+        VERIFY_ARE_EQUAL(3, afterLoc->as_integer());
+        VERIFY_ARE_EQUAL(2, a.size());
+        VERIFY_ARE_EQUAL(1, a[0].as_integer());
+        VERIFY_ARE_EQUAL(3, a[1].as_integer());
+
+        iter = a.as_array().begin() + 1;
+        afterLoc = a.as_array().erase(iter);
+        VERIFY_ARE_EQUAL(a.as_array().end(), afterLoc);
+        VERIFY_ARE_EQUAL(1, a.size());
+        VERIFY_ARE_EQUAL(1, a[0].as_integer());
+    }
+
+    TEST(erase_object_key)
+    {
+        auto o = json::value::object();
+        o[U("a")] = json::value(1);
+        o[U("b")] = json::value(2);
+        o[U("c")] = json::value(3);
+        o[U("d")] = json::value(4);
+
+        o.erase(U("a"));
+        VERIFY_ARE_EQUAL(3, o.size());
+        VERIFY_ARE_EQUAL(2, o[U("b")].as_integer());
+        VERIFY_ARE_EQUAL(3, o[U("c")].as_integer());
+        VERIFY_ARE_EQUAL(4, o[U("d")].as_integer());
+
+        o.as_object().erase(U("d"));
+        VERIFY_ARE_EQUAL(2, o.size());
+        VERIFY_ARE_EQUAL(2, o[U("b")].as_integer());
+        VERIFY_ARE_EQUAL(3, o[U("c")].as_integer());
+    }
+
+    TEST(erase_object_iter)
+    {
+        auto o = json::value::object();
+        o[U("a")] = json::value(1);
+        o[U("b")] = json::value(2);
+        o[U("c")] = json::value(3);
+        o[U("d")] = json::value(4);
+
+        auto iter = o.as_object().begin() + 1;
+        auto afterLoc = o.as_object().erase(iter);
+        VERIFY_ARE_EQUAL(3, o.size());
+        VERIFY_ARE_EQUAL(3, afterLoc->second.as_integer());
+        VERIFY_ARE_EQUAL(1, o[U("a")].as_integer());
+        VERIFY_ARE_EQUAL(3, o[U("c")].as_integer());
+        VERIFY_ARE_EQUAL(4, o[U("d")].as_integer());
+
+        iter = o.as_object().begin() + 2;
+        afterLoc = o.as_object().erase(iter);
+        VERIFY_ARE_EQUAL(2, o.size());
+        VERIFY_ARE_EQUAL(o.as_object().end(), afterLoc);
+        VERIFY_ARE_EQUAL(1, o[U("a")].as_integer());
+        VERIFY_ARE_EQUAL(3, o[U("c")].as_integer());
+    }
+
+    TEST(floating_number_serialize)
+    {
+        // This number will have the longest serializaton possible (lenght of the string):
+        // Sign, exponent, decimal comma, longest mantisa and exponent make so.
+        auto value = json::value(-3.123456789012345678901234567890E-123);
+
+        // #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx)
+        const auto len = std::numeric_limits<double>::digits10 + 9;
+
+        // Check narrow string implementation
+        std::stringstream ss;
+        value.serialize(ss);
+        VERIFY_ARE_EQUAL(len, ss.str().length());
+
+#ifdef _WIN32
+        // Check wide string implementation
+        std::basic_stringstream<wchar_t> wss;
+        value.serialize(wss);
+        VERIFY_ARE_EQUAL(len, wss.str().length());
+#endif
+    }
+
+} // SUITE(to_as_and_operators_tests)
+
+} // namespace json_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/misc/atl_headers/Resource.h b/Release/tests/functional/misc/atl_headers/Resource.h
new file mode 100644 (file)
index 0000000..4ab6fff
--- /dev/null
@@ -0,0 +1,17 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by header_test.rc
+//
+
+#define IDS_APP_TITLE 103
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Release/tests/functional/misc/atl_headers/header_test.rc b/Release/tests/functional/misc/atl_headers/header_test.rc
new file mode 100644 (file)
index 0000000..424d489
Binary files /dev/null and b/Release/tests/functional/misc/atl_headers/header_test.rc differ
diff --git a/Release/tests/functional/misc/atl_headers/header_test1.cpp b/Release/tests/functional/misc/atl_headers/header_test1.cpp
new file mode 100644 (file)
index 0000000..95dd223
--- /dev/null
@@ -0,0 +1,62 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests to include headers
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+// Include ATL headers before casablanca headers
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+#endif
+
+// These MFC headers are not code analysis clean.
+#pragma warning(push)
+#pragma warning(disable : 6387)
+#include <afx.h>
+#include <afxext.h> // MFC extensions
+#include <afxwin.h> // MFC core and standard components
+#ifndef _AFX_NO_OLE_SUPPORT
+#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
+#endif
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows Common Controls
+#endif              // _AFX_NO_AFXCMN_SUPPORT
+#pragma warning(pop)
+
+#include <iostream>
+// Windows Header Files:
+#include <windows.h>
+
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
+
+#include "cpprest/http_client.h"
+#include "unittestpp.h"
+#include <atlbase.h>
+#include <atlstr.h>
+
+namespace tests
+{
+namespace functional
+{
+namespace misc
+{
+namespace atl_headers
+{
+SUITE(header_test1)
+{
+    TEST(HeaderTest) { web::http::client::http_client client(U("http://www.cnn.com")); }
+
+} // SUITE(header_test1)
+
+} // namespace atl_headers
+} // namespace misc
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/misc/atl_headers/header_test2.cpp b/Release/tests/functional/misc/atl_headers/header_test2.cpp
new file mode 100644 (file)
index 0000000..73e3571
--- /dev/null
@@ -0,0 +1,63 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests to include headers
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#include "cpprest/http_client.h"
+
+// Include ATL headers after casablanca headers
+#define WIN32_LEAN_AND_MEAN                // Exclude rarely-used stuff from Windows headers
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+#endif
+
+// These MFC headers are not code analysis clean.
+#pragma warning(push)
+#pragma warning(disable : 6387)
+#include <afx.h>
+#include <afxext.h> // MFC extensions
+#include <afxwin.h> // MFC core and standard components
+#ifndef _AFX_NO_OLE_SUPPORT
+#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
+#endif
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows Common Controls
+#endif              // _AFX_NO_AFXCMN_SUPPORT
+#pragma warning(pop)
+
+#include <iostream>
+// Windows Header Files:
+#include <windows.h>
+
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
+
+#include "unittestpp.h"
+#include <atlbase.h>
+#include <atlstr.h>
+
+namespace tests
+{
+namespace functional
+{
+namespace misc
+{
+namespace atl_headers
+{
+SUITE(header_test2)
+{
+    TEST(HeaderTest) { web::http::client::http_client client(U("http://www.cnn.com")); }
+
+} // SUITE(header_test2)
+
+} // namespace atl_headers
+} // namespace misc
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/pplx/CMakeLists.txt b/Release/tests/functional/pplx/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f55ea07
--- /dev/null
@@ -0,0 +1 @@
+add_subdirectory(pplx_test)
diff --git a/Release/tests/functional/pplx/pplx_test/CMakeLists.txt b/Release/tests/functional/pplx/pplx_test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7007a58
--- /dev/null
@@ -0,0 +1,9 @@
+set(SOURCES
+  pplx_op_test.cpp
+  pplx_task_options.cpp
+  pplxtask_tests.cpp
+)
+
+add_casablanca_test(pplx_test SOURCES)
+
+configure_pch(pplx_test stdafx.h stdafx.cpp)
diff --git a/Release/tests/functional/pplx/pplx_test/pplx_op_test.cpp b/Release/tests/functional/pplx/pplx_test/pplx_op_test.cpp
new file mode 100644 (file)
index 0000000..82b10a7
--- /dev/null
@@ -0,0 +1,361 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests for PPLX operations.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+pplx::details::atomic_long s_flag;
+
+#if defined(_MSC_VER)
+
+class pplx_dflt_scheduler : public pplx::scheduler_interface
+{
+    struct _Scheduler_Param
+    {
+        pplx::TaskProc_t m_proc;
+        void* m_param;
+
+        _Scheduler_Param(pplx::TaskProc_t proc, void* param) : m_proc(proc), m_param(param) {}
+    };
+
+    static void CALLBACK DefaultWorkCallbackTest(PTP_CALLBACK_INSTANCE, PVOID pContext, PTP_WORK)
+    {
+        auto schedulerParam = std::unique_ptr<_Scheduler_Param>(static_cast<_Scheduler_Param*>(pContext));
+
+        schedulerParam->m_proc(schedulerParam->m_param);
+    }
+
+    virtual void schedule(pplx::TaskProc_t proc, void* param)
+    {
+        pplx::details::atomic_increment(s_flag);
+        auto schedulerParam = std::unique_ptr<_Scheduler_Param>(new _Scheduler_Param(proc, param));
+        auto work = CreateThreadpoolWork(DefaultWorkCallbackTest, schedulerParam.get(), NULL);
+
+        if (work == nullptr)
+        {
+            throw utility::details::create_system_error(GetLastError());
+        }
+
+        SubmitThreadpoolWork(work);
+        CloseThreadpoolWork(work);
+        schedulerParam.release();
+    }
+};
+
+#else
+class pplx_dflt_scheduler : public pplx::scheduler_interface
+{
+    std::unique_ptr<crossplat::threadpool> m_pool;
+
+    virtual void schedule(pplx::TaskProc_t proc, void* param)
+    {
+        pplx::details::atomic_increment(s_flag);
+        m_pool->service().post([=]() -> void { proc(param); });
+    }
+
+public:
+    pplx_dflt_scheduler() : m_pool(crossplat::threadpool::construct(4)) {}
+};
+#endif
+
+namespace tests
+{
+namespace functional
+{
+namespace pplx_tests
+{
+SUITE(pplx_op_tests)
+{
+    TEST(task_from_value)
+    {
+        auto val = pplx::task_from_result<int>(17);
+
+        VERIFY_ARE_EQUAL(val.get(), 17);
+    }
+
+    TEST(task_from_value_with_continuation)
+    {
+        auto val = pplx::task_from_result<int>(17);
+
+        int v = 0;
+
+        auto t = val.then([&](int x) { v = x; });
+        t.wait();
+
+        VERIFY_ARE_EQUAL(v, 17);
+    }
+
+    TEST(create_task)
+    {
+        auto val = pplx::create_task([]() { return 17; });
+
+        VERIFY_ARE_EQUAL(val.get(), 17);
+    }
+
+    TEST(create_task_with_continuation)
+    {
+        auto val = pplx::create_task([]() { return 17; });
+
+        int v = 0;
+
+        auto t = val.then([&](int x) { v = x; });
+        t.wait();
+
+        VERIFY_ARE_EQUAL(v, 17);
+    }
+
+    TEST(task_from_event)
+    {
+        pplx::task_completion_event<int> tce;
+        auto val = pplx::create_task(tce);
+        tce.set(17);
+
+        VERIFY_ARE_EQUAL(val.get(), 17);
+    }
+
+    TEST(task_from_event_with_continuation)
+    {
+        pplx::task_completion_event<int> tce;
+        auto val = pplx::create_task(tce);
+
+        int v = 0;
+
+        auto t = val.then([&](int x) { v = x; });
+
+        tce.set(17);
+        t.wait();
+
+        VERIFY_ARE_EQUAL(v, 17);
+    }
+
+    TEST(task_from_event_is_done)
+    {
+        pplx::task_completion_event<long> tce;
+        auto val = pplx::create_task(tce);
+
+        pplx::details::atomic_long v(0);
+
+        auto t = val.then([&](long x) { pplx::details::atomic_exchange(v, x); });
+
+        // The task should not have started yet.
+        bool isDone = t.is_done();
+        VERIFY_ARE_EQUAL(isDone, false);
+
+        // Start the task
+        tce.set(17);
+
+        // Wait for the lambda to finish running
+        while (!t.is_done())
+        {
+            // Yield.
+        }
+
+        // Verify that the lambda did run
+        VERIFY_ARE_EQUAL(v, 17);
+
+        // Wait for the task.
+        t.wait();
+
+        VERIFY_ARE_EQUAL(v, 17);
+    }
+
+    TEST(task_from_event_with_exception)
+    {
+        pplx::task_completion_event<long> tce;
+        auto val = pplx::create_task(tce);
+
+        pplx::details::atomic_long v(0);
+
+        auto t = val.then([&](long x) { pplx::details::atomic_exchange(v, x); });
+
+        // Start the task
+        tce.set_exception(pplx::invalid_operation());
+
+        // Wait for the lambda to finish running
+        while (!t.is_done())
+        {
+            // Yield.
+        }
+
+        // Verify that the lambda did run
+        VERIFY_ARE_EQUAL(v, 0);
+
+        // Wait for the task.
+        try
+        {
+            t.wait();
+        }
+        catch (pplx::invalid_operation)
+        {
+        }
+        catch (std::exception_ptr)
+        {
+            v = 1;
+        }
+
+        VERIFY_ARE_EQUAL(v, 0);
+    }
+
+    TEST(schedule_task_hold_then_release)
+    {
+        pplx::details::atomic_long flag(0);
+
+        pplx::task<void> t1([&flag]() {
+            while (flag == 0)
+                ;
+        });
+
+        pplx::details::atomic_exchange(flag, 1L);
+        t1.wait();
+    }
+
+    // TFS # 521911
+    TEST(schedule_two_tasks)
+    {
+        pplx_dflt_scheduler sched;
+        pplx::details::atomic_exchange(s_flag, 0L);
+
+        auto nowork = []() {};
+
+        auto defaultTask = pplx::create_task(nowork);
+        defaultTask.wait();
+        VERIFY_ARE_EQUAL(s_flag, 0);
+
+        pplx::task_completion_event<void> tce;
+        auto t = pplx::create_task(tce, sched);
+
+        // 2 continuations to be scheduled on the scheduler.
+        // Note that task "t" is not scheduled.
+        auto t1 = t.then(nowork).then(nowork);
+
+        tce.set();
+        t1.wait();
+
+        VERIFY_ARE_EQUAL(s_flag, 2);
+    }
+
+    TEST(task_throws_exception)
+    {
+        pplx::extensibility::event_t ev;
+        bool caught = false;
+
+        // Ensure that exceptions thrown from user lambda
+        // are indeed propagated and do not escape out of
+        // the task.
+        auto t1 = pplx::create_task([&ev]() {
+            ev.set();
+            throw std::logic_error("Should not escape");
+        });
+
+        auto t2 = t1.then([]() { VERIFY_IS_TRUE(false); });
+
+        // Ensure that we do not inline the work on this thread
+        ev.wait();
+
+        try
+        {
+            t2.wait();
+        }
+        catch (std::exception&)
+        {
+            caught = true;
+        }
+
+        VERIFY_IS_TRUE(caught);
+    }
+
+    pplx::task<int> make_unwrapped_task()
+    {
+        pplx::task<int> t1([]() { return 10; });
+
+        return pplx::task<int>([t1]() { return t1; });
+    }
+
+    TEST(unwrap_task)
+    {
+        pplx::task<int> t = make_unwrapped_task();
+        int n = t.get();
+        VERIFY_ARE_EQUAL(n, 10);
+    }
+
+    TEST(task_from_event_with_tb_continuation)
+    {
+        volatile long flag = 0;
+
+        pplx::task_completion_event<int> tce;
+        auto val = pplx::create_task(tce).then([=, &flag](pplx::task<int> op) -> short {
+            flag = 1;
+            return (short)op.get();
+        });
+
+        tce.set(17);
+
+        VERIFY_ARE_EQUAL(val.get(), 17);
+        VERIFY_ARE_EQUAL(flag, 1);
+    }
+
+    class fcc_370010
+    {
+    public:
+        fcc_370010(pplx::task_completion_event<bool> op) : m_op(op) {}
+
+        virtual void on_closed(bool result)
+        {
+            m_op.set(result);
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdelete-non-virtual-dtor"
+#endif
+            delete this;
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+        }
+
+    private:
+        pplx::task_completion_event<bool> m_op;
+    };
+
+    TEST(BugRepro370010)
+    {
+        auto result_tce = pplx::task_completion_event<bool>();
+
+        auto f = new fcc_370010(result_tce);
+
+        pplx::task<void> dummy([f]() { f->on_closed(true); });
+
+        auto result = pplx::task<bool>(result_tce);
+
+        VERIFY_IS_TRUE(result.get());
+    }
+
+    TEST(event_set_reset_timeout, "Ignore", "785846")
+    {
+        pplx::extensibility::event_t ev;
+
+        ev.set();
+
+        // Wait should succeed as the event was set above
+        VERIFY_IS_TRUE(ev.wait(0) == 0);
+
+        // wait should succeed as this is manual reset
+        VERIFY_IS_TRUE(ev.wait(0) == 0);
+
+        ev.reset();
+
+        // wait should fail as the event is reset (not set)
+        VERIFY_IS_TRUE(ev.wait(0) == pplx::extensibility::event_t::timeout_infinite);
+    }
+
+} // SUITE(pplx_op_tests)
+
+} // namespace pplx_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/pplx/pplx_test/pplx_task_options.cpp b/Release/tests/functional/pplx/pplx_test/pplx_task_options.cpp
new file mode 100644 (file)
index 0000000..983e9cc
--- /dev/null
@@ -0,0 +1,447 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests for PPLX task options.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
+// Dev12 doesn't have an in-box ambient scheduler, since all tasks execute on ConcRT.
+// Therefore, we need to provide one. A scheduler that directly executes a functor given to it is
+// a simple and valid implementation of a PPL scheduler
+class direct_executor : public pplx::scheduler_interface
+{
+public:
+    virtual void schedule(concurrency::TaskProc_t proc, _In_ void* param) { proc(param); }
+};
+
+static std::shared_ptr<pplx::scheduler_interface> g_executor;
+std::shared_ptr<pplx::scheduler_interface> __cdecl get_scheduler()
+{
+    if (!g_executor)
+    {
+        g_executor = std::make_shared<direct_executor>();
+    }
+
+    return g_executor;
+}
+#else
+std::shared_ptr<pplx::scheduler_interface> __cdecl get_scheduler() { return pplx::get_ambient_scheduler(); }
+#endif
+
+class TaskOptionsTestScheduler : public pplx::scheduler_interface
+{
+public:
+    TaskOptionsTestScheduler() : m_numTasks(0), m_scheduler(get_scheduler()) {}
+
+    virtual void schedule(pplx::TaskProc_t proc, void* param)
+    {
+        pplx::details::atomic_increment(m_numTasks);
+        m_scheduler->schedule(proc, param);
+    }
+
+    long get_num_tasks() { return m_numTasks; }
+
+private:
+    pplx::details::atomic_long m_numTasks;
+    pplx::scheduler_ptr m_scheduler;
+
+    TaskOptionsTestScheduler(const TaskOptionsTestScheduler&);
+    TaskOptionsTestScheduler& operator=(const TaskOptionsTestScheduler&);
+};
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4512)
+#endif
+class CheckLifetimeScheduler : public pplx::scheduler_interface
+{
+public:
+    CheckLifetimeScheduler(pplx::extensibility::event_t& ev) : m_event(ev), m_numTasks(0) {}
+
+    ~CheckLifetimeScheduler() { m_event.set(); }
+
+    virtual void schedule(pplx::TaskProc_t proc, void* param)
+    {
+        pplx::details::atomic_increment(m_numTasks);
+        get_scheduler()->schedule(proc, param);
+    }
+
+    long get_num_tasks() { return m_numTasks; }
+
+    pplx::extensibility::event_t& m_event;
+    pplx::details::atomic_long m_numTasks;
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+namespace tests
+{
+namespace functional
+{
+namespace PPLX
+{
+SUITE(pplx_task_options_tests)
+{
+    TEST(voidtask_schedoption_test)
+    {
+        TaskOptionsTestScheduler sched;
+        long n = 0;
+
+        auto t1 = pplx::create_task([&n]() { n++; }, sched); // run on sched
+        t1.wait();
+
+        VERIFY_ARE_EQUAL(sched.get_num_tasks(), n);
+    }
+
+    TEST(task_schedoption_test)
+    {
+        TaskOptionsTestScheduler sched;
+        long n = 0;
+
+        auto t1 = pplx::create_task(
+            [&n]() -> int {
+                n++;
+                return 1;
+            },
+            sched); // run on sched
+        t1.wait();
+
+        VERIFY_ARE_EQUAL(sched.get_num_tasks(), n);
+    }
+
+    TEST(then_nooptions_test)
+    {
+        TaskOptionsTestScheduler sched;
+        long n = 0;
+
+        auto t1 = pplx::create_task([&n]() { n++; }, sched);
+        t1.then([&n]() { n++; }) // inherit sched
+            .then([&n]() { n++; })
+            .wait();
+
+        VERIFY_ARE_EQUAL(sched.get_num_tasks(), n);
+    }
+
+    TEST(then_multiple_schedulers_test1)
+    {
+        TaskOptionsTestScheduler sched1;
+        TaskOptionsTestScheduler sched2;
+
+        auto emptyFunc = []() {};
+
+        auto t1 = pplx::create_task(emptyFunc, sched1); // sched1
+        t1.then(emptyFunc, sched2).wait();              // sched2
+
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), 1);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 1);
+    }
+
+    TEST(then_multiple_schedulers_test2)
+    {
+        TaskOptionsTestScheduler sched1;
+        TaskOptionsTestScheduler sched2;
+
+        auto emptyFunc = []() {};
+
+        auto t1 = pplx::create_task(emptyFunc, sched1);
+        t1.then(emptyFunc, sched2)
+            .then(emptyFunc) // inherit sched2
+            .wait();
+
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), 1);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 2);
+    }
+
+    TEST(opand_nooptions_test)
+    {
+        TaskOptionsTestScheduler sched;
+
+        auto t1 = pplx::create_task([]() {}, sched);
+        auto t2 = pplx::create_task([]() {}, sched);
+
+        auto t3 = t1 && t2;             // Does not run on the scheduler - it should run inline
+        t3.then([]() {}, sched).wait(); // run on sched
+
+        VERIFY_ARE_EQUAL(sched.get_num_tasks(), 3);
+    }
+
+    TEST(whenall_nooptions_test)
+    {
+        TaskOptionsTestScheduler sched1;
+        TaskOptionsTestScheduler sched2;
+
+        std::vector<pplx::task<void>> taskVect;
+        const int n = 10;
+        for (int i = 0; i < n; i++)
+        {
+            taskVect.push_back(pplx::create_task([]() {}, sched1));
+        }
+
+        auto t3 =
+            pplx::when_all(begin(taskVect), end(taskVect)); // Does not run on the scheduler - it should run inline
+        t3.then([]() {}, sched2).wait();                    // run on sched2
+
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), n);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 1);
+    }
+
+    TEST(whenall_options_test1)
+    {
+        TaskOptionsTestScheduler sched1;
+        TaskOptionsTestScheduler sched2;
+
+        std::vector<pplx::task<void>> taskVect;
+        const int n = 10;
+        for (int i = 0; i < n; i++)
+        {
+            taskVect.push_back(pplx::create_task([]() {}, sched1));
+        }
+
+        auto t3 = pplx::when_all(
+            begin(taskVect), end(taskVect), sched2); // Does not run on the scheduler - it should run inline
+        t3.then([]() {}).wait();                     // run on sched2 (inherits from the when_all task
+
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), n);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 1);
+    }
+
+    TEST(whenall_options_test2)
+    {
+        // Same as the above test but use task<int> to instatinate those templates
+        TaskOptionsTestScheduler sched1;
+        TaskOptionsTestScheduler sched2;
+
+        std::vector<pplx::task<int>> taskVect;
+        const int n = 10;
+        for (int i = 0; i < n; i++)
+        {
+            taskVect.push_back(pplx::create_task([i]() -> int { return i; }, sched1));
+        }
+
+        auto t3 = pplx::when_all(
+            begin(taskVect), end(taskVect), sched2); // Does not run on the scheduler - it should run inline
+        t3.then([](std::vector<int>) {}).wait();     // run on sched2 (inherits from the when_all task
+
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), n);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 1);
+    }
+
+    TEST(whenall_options_test3)
+    {
+        // Same as the above test but use multiple when_all
+        TaskOptionsTestScheduler sched1;
+        TaskOptionsTestScheduler sched2;
+
+        std::vector<pplx::task<int>> taskVect;
+        const int n = 10;
+        for (int i = 0; i < n; i++)
+        {
+            taskVect.push_back(pplx::create_task([i]() -> int { return i; }, sched1));
+        }
+
+        auto t2 = pplx::create_task([]() -> int { return 0; }, sched1);
+
+        auto t3 = pplx::when_all(
+            begin(taskVect), end(taskVect), sched2); // Does not run on the scheduler - it should run inline
+
+        auto t4 = t2 && t3;
+        t4.then([](std::vector<int>) {})
+            .wait(); // run on default scheduler as the operator && breaks the chain of inheritance
+
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), n + 1);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 0);
+    }
+
+    TEST(opor_nooptions_test)
+    {
+        TaskOptionsTestScheduler sched;
+
+        auto t1 = pplx::create_task([]() {}, sched);
+        auto t2 = pplx::create_task([]() {}, sched);
+
+        auto t3 = t1 || t2; // Runs inline
+        t3.then([]() {}, sched).wait();
+
+        VERIFY_ARE_EQUAL(sched.get_num_tasks(), 3);
+    }
+
+    TEST(whenany_nooptions_test)
+    {
+        TaskOptionsTestScheduler sched1;
+        TaskOptionsTestScheduler sched2;
+
+        std::vector<pplx::task<void>> taskVect;
+        const int n = 10;
+        for (int i = 0; i < n; i++)
+        {
+            taskVect.push_back(pplx::create_task([]() {}, sched1));
+        }
+
+        auto t3 =
+            pplx::when_any(begin(taskVect), end(taskVect)); // Does not run on the scheduler - it should run inline
+        t3.then([](size_t) {}, sched2).wait();              // run on sched2
+
+        // Do a whenall to wait for all the tasks
+        pplx::when_all(begin(taskVect), end(taskVect)).wait();
+
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), n);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 1);
+    }
+
+    TEST(whenany_options_test1)
+    {
+        TaskOptionsTestScheduler sched1;
+        TaskOptionsTestScheduler sched2;
+
+        std::vector<pplx::task<void>> taskVect;
+        const int n = 10;
+        for (int i = 0; i < n; i++)
+        {
+            taskVect.push_back(pplx::create_task([]() {}, sched1));
+        }
+
+        auto t3 = pplx::when_any(
+            begin(taskVect), end(taskVect), sched2); // Does not run on the scheduler - it should run inline
+        t3.then([](size_t) {}).wait();               // run on sched2 (inherits from the when_all task
+
+        // Do a whenall to wait for all the tasks
+        pplx::when_all(begin(taskVect), end(taskVect)).wait();
+
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), n);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 1);
+    }
+
+    TEST(whenany_options_test2)
+    {
+        // Same as whenany_options_test1 except that we instantiate a different set of template functions
+        TaskOptionsTestScheduler sched1;
+        TaskOptionsTestScheduler sched2;
+
+        std::vector<pplx::task<int>> taskVect;
+        const int n = 10;
+        for (int i = 0; i < n; i++)
+        {
+            taskVect.push_back(pplx::create_task([]() -> int { return 0; }, sched1));
+        }
+
+        auto t3 = pplx::when_any(
+            begin(taskVect), end(taskVect), sched2);   // Does not run on the scheduler - it should run inline
+        t3.then([](std::pair<int, size_t>) {}).wait(); // run on sched2 (inherits from the when_all task
+
+        // Do a whenall to wait for all the tasks
+        pplx::when_all(begin(taskVect), end(taskVect)).wait();
+
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), n);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 1);
+    }
+
+    TEST(tce_nooptions_test)
+    {
+        TaskOptionsTestScheduler sched;
+        TaskOptionsTestScheduler sched1;
+        TaskOptionsTestScheduler sched2;
+
+        pplx::task_completion_event<void> tce;
+        auto t1 = pplx::create_task(tce, sched1);
+        auto t2 = pplx::create_task(tce, sched2);
+
+        tce.set();
+        t1.wait();
+        t2.wait();
+
+        // There is nothing to execute
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), 0);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 0);
+
+        auto emptyFunc = []() {};
+
+        auto t3 = t1.then(emptyFunc);
+        auto t4 = t2.then(emptyFunc);
+
+        t3.wait();
+        t4.wait();
+
+        VERIFY_ARE_EQUAL(sched1.get_num_tasks(), 1);
+        VERIFY_ARE_EQUAL(sched2.get_num_tasks(), 1);
+    }
+
+    TEST(fromresult_options_test)
+    {
+        TaskOptionsTestScheduler sched;
+
+        int value = 10;
+        auto t1 = pplx::task_from_result(value);
+        t1.wait();
+        VERIFY_ARE_EQUAL(sched.get_num_tasks(), 0);
+
+        t1.then([](int i) -> int { return i; }, sched).wait();
+        VERIFY_ARE_EQUAL(sched.get_num_tasks(), 1);
+    }
+
+    TEST(scheduler_lifetime)
+    {
+        pplx::extensibility::event_t ev;
+        {
+            auto sched = std::make_shared<CheckLifetimeScheduler>(ev);
+
+            pplx::create_task([]() {}, sched) // runs on sched (1)
+                .then([]() {})                // runs on sched (2)
+                .wait();
+
+            VERIFY_ARE_EQUAL(sched->get_num_tasks(), 2);
+        }
+
+        ev.wait();
+    }
+
+    TEST(scheduler_lifetime_mixed)
+    {
+        pplx::extensibility::event_t ev;
+        auto t = pplx::create_task([]() {}); // use default scheduler
+        {
+            auto sched = std::make_shared<CheckLifetimeScheduler>(ev);
+
+            t.then([]() {}, sched) // (1)
+                .then([]() {})     // (2)
+                .wait();
+
+            VERIFY_ARE_EQUAL(sched->get_num_tasks(), 2);
+        }
+
+        ev.wait();
+    }
+
+    TEST(scheduler_lifetime_nested)
+    {
+        pplx::extensibility::event_t ev;
+        auto t = pplx::create_task([]() {}); // use default scheduler
+        {
+            auto sched = std::make_shared<CheckLifetimeScheduler>(ev);
+
+            t.then([]() {}, sched) // custom scheduler (1)
+                .then(
+                    [sched]() {
+                        // We are on the default scheduler
+                        pplx::create_task([]() {}, sched); // run on custom scheduler (2)
+                    },
+                    t.scheduler())
+                .wait();
+
+            VERIFY_ARE_EQUAL(sched->get_num_tasks(), 2);
+        }
+
+        ev.wait();
+    }
+
+} // SUITE(pplx_task_options_tests)
+} // namespace PPLX
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/pplx/pplx_test/pplxtask_tests.cpp b/Release/tests/functional/pplx/pplx_test/pplxtask_tests.cpp
new file mode 100644 (file)
index 0000000..7c61982
--- /dev/null
@@ -0,0 +1,1898 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests for PPLX operations
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+using namespace ::pplx;
+using namespace ::tests::common::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace PPLX
+{
+static void IsTrue(bool condition, const wchar_t*, ...) { VERIFY_IS_TRUE(condition); }
+
+static void IsFalse(bool condition, ...) { VERIFY_IS_TRUE(condition == false); }
+
+static void LogFailure(const wchar_t* msg, ...)
+{
+    wprintf(L"%s", msg);
+    VERIFY_IS_TRUE(false);
+}
+
+namespace helpers
+{
+static int FibSerial(int n)
+{
+    if (n < 2) return n;
+
+    return FibSerial(n - 1) + FibSerial(n - 2);
+}
+
+static void DoRandomParallelWork()
+{
+    int param = (rand() % 8) + 20;
+    // Calculate fib in serial
+    volatile int val = FibSerial(param);
+    val;
+}
+
+template<typename _EX, typename _T>
+bool VerifyException(task<_T>& task)
+{
+    bool gotException = true;
+    bool wrongException = false;
+
+    try
+    {
+        task.get();
+        gotException = false;
+    }
+    catch (const _EX&)
+    {
+    }
+    catch (...)
+    {
+        wrongException = true;
+    }
+
+    return (gotException && !wrongException);
+}
+
+template<typename _T>
+bool VerifyNoException(task<_T>& task)
+{
+    try
+    {
+        task.get();
+    }
+    catch (...)
+    {
+        return false;
+    }
+    return true;
+}
+
+template<typename _T>
+bool VerifyCanceled(task<_T>& task)
+{
+    try
+    {
+        task.get();
+    }
+    catch (task_canceled&)
+    {
+        return true;
+    }
+    catch (...)
+    {
+        return false;
+    }
+    return false;
+}
+
+template<typename _T>
+void ObserveException(task<_T>& task)
+{
+    try
+    {
+        task.get();
+    }
+    catch (...)
+    {
+    }
+}
+
+template<typename Iter>
+void ObserveAllExceptions(Iter begin, Iter end)
+{
+    typedef typename std::iterator_traits<Iter>::value_type::result_type TaskType;
+    for (auto it = begin; it != end; ++it)
+    {
+        ObserveException(*it);
+    }
+}
+} // namespace helpers
+
+SUITE(pplxtask_tests)
+{
+    TEST(TestCancellationTokenRegression)
+    {
+        for (int i = 0; i < 500; i++)
+        {
+            task_completion_event<void> tce;
+            task<void> starter(tce);
+
+            cancellation_token_source ct;
+
+            task<int> t1 = starter.then([]() -> int { return 47; }, ct.get_token());
+
+            task<int> t2([]() -> int { return 82; });
+
+            task<int> t3([]() -> int { return 147; });
+
+            auto t4 = (t1 && t2 && t3).then([=](std::vector<int> vec) -> int { return vec[0] + vec[1] + vec[3]; });
+
+            ct.cancel();
+
+            tce.set();
+            // this should not hang
+            task_status t4Status = t4.wait();
+            IsTrue(t4Status == canceled,
+                   L"operator && did not properly cancel. Expected: %d, Actual: %d",
+                   canceled,
+                   t4Status);
+        }
+    }
+    TEST(TestTasks_basic)
+    {
+        {
+            task<int> t1([]() -> int { return 47; });
+
+            auto t2 = t1.then([=](int i) -> float {
+                IsTrue(i == 47,
+                       L"Continuation did not recieve the correct value from ancestor. Expected: 47, Actual: %d",
+                       i);
+                return (float)i / 2;
+            });
+
+            float t2Result = t2.get();
+            IsTrue(t2Result == 23.5,
+                   L"Continuation task did not produce the correct result. Expected: 23.5, Actual: %f",
+                   t2Result);
+
+            task_status t2Status = t2.wait();
+            IsTrue(t2Status == completed,
+                   L"Continuation task was not in completed state. Expected: %d, Actual: %d",
+                   completed,
+                   t2Status);
+
+            task<int> t3([]() -> int { return 0; });
+
+            IsTrue(t1 == t1, L"task operator== resulted false on equivalent tasks");
+            IsFalse(t1 != t1, L"task operator!= resulted true on equivalent tasks");
+            IsFalse(t1 == t3, L"task operator== resulted true on different tasks");
+            IsTrue(t1 != t3, L"task operator!= resulted false on different tasks");
+
+            t3.wait();
+        }
+    }
+
+    TEST(TestTasks_default_construction)
+    {
+        // Test that default constructed task<T> properly throw exceptions
+        {
+            task<int> t1;
+
+            try
+            {
+                t1.wait();
+                LogFailure(L"t1.wait() should have thrown an exception");
+            }
+            catch (invalid_operation)
+            {
+            }
+
+            try
+            {
+                t1.get();
+                LogFailure(L"t1.get() should have thrown an exception");
+            }
+            catch (invalid_operation)
+            {
+            }
+
+            try
+            {
+                t1.then([](int i) { return i; });
+
+                LogFailure(L"t1.then() should have thrown an exception");
+            }
+            catch (invalid_operation)
+            {
+            }
+        }
+    }
+
+    TEST(TestTasks_void_tasks)
+    {
+        // Test void tasks
+        {
+            int value = 0;
+            task<void> t1([&value]() { value = 147; });
+
+            auto t2 = t1.then([&]() {
+                IsTrue(value == 147,
+                       L"void continuation did not recieve the correct value from ancestor. Expected: 147, Actual: %d",
+                       value);
+                value++;
+            });
+
+            IsTrue(t2.wait() == completed, L"void task was not in completed state.");
+
+            IsTrue(value == 148, L"void tasks did not properly execute. Expected: 148, Actual: %d", value);
+
+            task<void> t3([]() {});
+
+            IsTrue(t1 == t1, L"task operator== resulted false on equivalent tasks");
+            IsFalse(t1 != t1, L"task operator!= resulted true on equivalent tasks");
+            IsFalse(t1 == t3, L"task operator== resulted true on different tasks");
+            IsTrue(t1 != t3, L"task operator!= resulted false on different tasks");
+        }
+    }
+
+    TEST(TestTasks_void_tasks_default_construction)
+    {
+        // Test that default constructed task<void> properly throw exceptions
+        {
+            task<void> t1;
+
+            try
+            {
+                t1.wait();
+                LogFailure(L"t1.wait() should have thrown an exception");
+            }
+            catch (invalid_operation)
+            {
+            }
+
+            try
+            {
+                t1.get();
+                LogFailure(L"t1.get() should have thrown an exception");
+            }
+            catch (invalid_operation)
+            {
+            }
+
+            try
+            {
+                t1.then([]() {});
+                LogFailure(L"t1.contiue_with() should have thrown an exception");
+            }
+            catch (invalid_operation)
+            {
+            }
+        }
+    }
+
+    TEST(TestTasks_movable_then)
+    {
+#ifndef _MSC_VER
+        // create movable only type
+        struct A
+        {
+            A() = default;
+            A(A&&) = default;
+            A& operator=(A&&) = default;
+
+            // explicitly delete copy functions
+            A(const A&) = delete;
+            A& operator=(const A&) = delete;
+
+            char operator()(int) { return 'c'; }
+        } a;
+
+        task<int> task = create_task([] { return 2; });
+        auto f = task.then(std::move(a));
+
+        IsTrue(f.get() == 'c', L".then should be able to work with movable functors");
+#endif // _MSC_VER
+    }
+
+    TEST(TestTasks_constant_this)
+    {
+#ifdef _MSC_VER
+#if _MSC_VER < 1700
+        // Dev10 compiler gives an error => .then(func) where func = int!
+#else
+        {
+            // Test constant 'this' pointer in member functions then(), wait() and get(),
+            // so that they can be used in Lambda.
+            task<int> t1([]() -> int { return 0; });
+
+            auto func = [t1]() -> int {
+                t1.then([](int last) -> int { return last; });
+                t1.wait();
+                return t1.get();
+            };
+
+            IsTrue(func() == 0, L"Tasks should be able to used inside a Lambda.");
+        }
+#endif // _MSC_VER < 1700
+#endif // _MSC_VER
+    }
+
+    TEST(TestTasks_fire_and_forget)
+    {
+        // Test Fire-and-forget behavior
+        extensibility::event_t evt;
+        bool flag = false;
+        {
+            task<int> t1([&flag, &evt]() -> int {
+                flag = true;
+                evt.set();
+                return 0;
+            });
+        }
+
+        evt.wait();
+        IsTrue(flag == true, L"Fire-and-forget task did not properly execute.");
+    }
+    TEST(TestTasks_create_task)
+    {
+        // test create task
+        task<int> t1 = create_task([]() -> int { return 4; });
+        IsTrue(t1.get() == 4, L"create_task for simple task did not properly execute.");
+        IsTrue(create_task(t1).get() == 4, L"create_task from a task task did not properly execute.");
+        task<void> t2 = create_task([]() {});
+        task<int> t3 = create_task([]() -> task<int> { return create_task([]() -> int { return 4; }); });
+        IsTrue(t3.get() == 4, L"create_task for task unwrapping did not properly execute.");
+    }
+
+    TEST(TestTaskCompletionEvents_basic)
+    {
+        task_completion_event<int> tce;
+        task<int> completion(tce);
+        auto completion2 = create_task(tce);
+
+        task<void> setEvent([=]() { tce.set(50); });
+
+        int result = completion.get();
+        IsTrue(result == 50, L"Task Completion Event did not get the right result. Expected: 50, Actual: %d", result);
+        IsTrue(completion2.get() == 50,
+               L"create_task didn't construct correct task for task_completion_event, Expected: 50, Actual: %d",
+               result);
+    }
+
+    TEST(TestTaskCompletionEvents_basic2)
+    {
+        task_completion_event<void> tce;
+        task<void> completion(tce);
+        auto completion2 = create_task(tce);
+
+        task<void> setEvent([=]() { tce.set(); });
+
+        // this should not hang, because of the set of tce
+        completion.wait();
+        completion2.wait();
+    }
+
+    TEST(TestTaskCompletionEvents_set_exception_basic)
+    {
+        task_completion_event<void> tce;
+        task<void> t(tce);
+        tce.set_exception(42);
+
+        t.then([=](task<void> p) {
+             try
+             {
+                 p.get();
+                 IsTrue(false, L"Exception not propagated to task t when calling set_exception.");
+             }
+             catch (int n)
+             {
+                 IsTrue(n == 42, L"%ws:%u:bad exception value", __FILE__, __LINE__);
+             }
+         })
+            .wait();
+    }
+
+    TEST(TestTaskCompletionEvents_set_exception_multiple)
+    {
+        task_completion_event<void> tce;
+        task<void> t(tce);
+        tce.set_exception(42);
+
+        t.then([=](task<void> p) {
+             try
+             {
+                 p.get();
+                 IsTrue(false, L"Exception not propagated to task t's first continuation when calling set_exception.");
+             }
+             catch (int n)
+             {
+                 IsTrue(n == 42, L"%ws:%u:bad exception value", __FILE__, __LINE__);
+             }
+         })
+            .wait();
+
+        t.then([=](task<void> p) {
+             try
+             {
+                 p.get();
+                 IsTrue(false, L"Exception not propagated to task t's second continuation when calling set_exception.");
+             }
+             catch (int n)
+             {
+                 IsTrue(n == 42, L"%ws:%u:bad exception value", __FILE__, __LINE__);
+             }
+         })
+            .wait();
+    }
+
+    TEST(TestTaskCompletionEvents_set_exception_struct)
+    {
+#if defined(_MSC_VER) && _MSC_VER < 1700
+        // The Dev10 compiler hits an ICE with this code
+#else
+        struct s
+        {
+        };
+
+        task_completion_event<void> tce;
+        task<void> t(tce);
+        tce.set_exception(s());
+        t.then([=](task<void> p) {
+             try
+             {
+                 p.get();
+                 IsTrue(false, L"Exception not caught.");
+             }
+             catch (s)
+             {
+                 // Do nothing
+             }
+             catch (...)
+             {
+                 IsTrue(false, L"%ws:%u:not the right exception", __FILE__, __LINE__);
+             }
+         })
+            .wait();
+#endif // _MSC_VER < 1700
+    }
+
+    TEST(TestTaskCompletionEvents_multiple_tasks)
+    {
+        task_completion_event<void> tce;
+        task<void> t1(tce);
+        task<void> t2(tce);
+        tce.set_exception(1);
+
+        t1.then([=](task<void> p) {
+            try
+            {
+                p.get();
+                IsTrue(false, L"An exception was not thrown when calling t1.get().  An exception was expected.");
+            }
+            catch (int ex)
+            {
+                IsTrue(ex == 1, L"%ws:%u:wrong exception value", __FILE__, __LINE__);
+            }
+            catch (...)
+            {
+                IsTrue(false, L"%ws:%u:not the right exception", __FILE__, __LINE__);
+            }
+        });
+
+        t2.then([=](task<void> p) {
+            try
+            {
+                p.get();
+                IsTrue(false, L"An exception was not thrown when calling t2.get().  An exception was expected.");
+            }
+            catch (int ex)
+            {
+                IsTrue(ex == 1, L"%ws:%u:wrong exception value", __FILE__, __LINE__);
+            }
+            catch (...)
+            {
+                IsTrue(false, L"%ws:%u:not the right exception", __FILE__, __LINE__);
+            }
+        });
+    }
+
+    TEST(TestTaskCompletionEvents_set_exception_after_set)
+    {
+        task_completion_event<int> tce;
+        task<int> t(tce);
+        tce.set(1);
+        auto result = tce.set_exception(std::current_exception());
+        IsFalse(result, L"set_exception must return false, but did not");
+        t.then([=](task<int> p) {
+             try
+             {
+                 int n = p.get();
+                 IsTrue(n == 1, L"Value not properly propagated to continuation");
+             }
+             catch (...)
+             {
+                 IsTrue(false, L"An exception was unexpectedly thrown in the continuation task");
+             }
+         })
+            .wait();
+    }
+
+    TEST(TestTaskCompletionEvents_set_exception_after_set2)
+    {
+        task_completion_event<int> tce;
+        task<int> t(tce);
+        tce.set_exception(1);
+        auto result = tce.set_exception(2);
+        IsFalse(result, L"set_exception must return false, but did not");
+        t.then([=](task<int> p) {
+             try
+             {
+                 p.get();
+                 IsTrue(false, L"%ws:%u:expected exception not thrown", __FILE__, __LINE__);
+             }
+             catch (int n)
+             {
+                 IsTrue(n == 1, L"%ws:%u:unexpected exception payload", __FILE__, __LINE__);
+             }
+         })
+            .wait();
+    }
+
+    TEST(TestTaskCompletionEvents_set_after_set_exception)
+    {
+        task_completion_event<int> tce;
+        task<int> t(tce);
+        tce.set_exception(42);
+        tce.set(1); // should be no-op
+        t.then([=](task<int> p) {
+             try
+             {
+                 p.get();
+                 IsTrue(false, L"Exception should have been thrown here.");
+             }
+             catch (int e)
+             {
+                 IsTrue(e == 42, L"%ws:%u:not the right exception value", __FILE__, __LINE__);
+             }
+             catch (...)
+             {
+                 IsTrue(false, L"%ws:%u:not the right exception", __FILE__, __LINE__);
+             }
+         })
+            .wait();
+    }
+
+    TEST(TestTaskOperators_and_or)
+    {
+        task<int> t1([]() -> int { return 47; });
+
+        task<int> t2([]() -> int { return 82; });
+
+        auto t3 = (t1 && t2).then([=](std::vector<int> vec) -> int {
+            IsTrue(vec.size() == 2,
+                   L"operator&& did not produce a correct vector size. Expected: 2, Actual: %d",
+                   vec.size());
+            IsTrue(vec[0] == 47, L"operator&& did not produce a correct vector[0]. Expected: 47, Actual: %d", vec[0]);
+            IsTrue(vec[1] == 82, L"operator&& did not produce a correct vector[1]. Expected: 82, Actual: %d", vec[1]);
+            return vec[0] + vec[1];
+        });
+
+        int t3Result = t3.get();
+        IsTrue(t3Result == 129,
+               L"operator&& task did not produce the correct result. Expected: 129, Actual: %d",
+               t3Result);
+    }
+
+    TEST(TestTaskOperators_and_or2)
+    {
+        task<int> t1([]() -> int { return 47; });
+
+        task<int> t2([]() -> int { return 82; });
+
+        task<int> t3([]() -> int { return 147; });
+
+        task<int> t4([]() -> int { return 192; });
+
+        auto t5 = (t1 && t2 && t3 && t4).then([=](std::vector<int> vec) -> int {
+            IsTrue(vec.size() == 4,
+                   L"operator&& did not produce a correct vector size. Expected: 4, Actual: %d",
+                   vec.size());
+            IsTrue(vec[0] == 47, L"operator&& did not produce a correct vector[0]. Expected: 47, Actual: %d", vec[0]);
+            IsTrue(vec[1] == 82, L"operator&& did not produce a correct vector[1]. Expected: 82, Actual: %d", vec[1]);
+            IsTrue(vec[2] == 147, L"operator&& did not produce a correct vector[2]. Expected: 147, Actual: %d", vec[2]);
+            IsTrue(vec[3] == 192, L"operator&& did not produce a correct vector[3]. Expected: 192, Actual: %d", vec[3]);
+            int count = 0;
+            for (unsigned i = 0; i < vec.size(); i++)
+                count += vec[i];
+            return count;
+        });
+
+        int t5Result = t5.get();
+        IsTrue(t5Result == 468,
+               L"operator&& task did not produce the correct result. Expected: 468, Actual: %d",
+               t5Result);
+    }
+
+    TEST(TestTaskOperators_and_or3)
+    {
+        task<int> t1([]() -> int { return 47; });
+
+        task<int> t2([]() -> int { return 82; });
+
+        task<int> t3([]() -> int { return 147; });
+
+        task<int> t4([]() -> int { return 192; });
+
+        auto t5 = ((t1 && t2) && (t3 && t4)).then([=](std::vector<int> vec) -> int {
+            IsTrue(vec.size() == 4,
+                   L"operator&& did not produce a correct vector size. Expected: 4, Actual: %d",
+                   vec.size());
+            IsTrue(vec[0] == 47, L"operator&& did not produce a correct vector[0]. Expected: 47, Actual: %d", vec[0]);
+            IsTrue(vec[1] == 82, L"operator&& did not produce a correct vector[1]. Expected: 82, Actual: %d", vec[1]);
+            IsTrue(vec[2] == 147, L"operator&& did not produce a correct vector[2]. Expected: 147, Actual: %d", vec[2]);
+            IsTrue(vec[3] == 192, L"operator&& did not produce a correct vector[3]. Expected: 192, Actual: %d", vec[3]);
+            int count = 0;
+            for (unsigned i = 0; i < vec.size(); i++)
+                count += vec[i];
+            return count;
+        });
+
+        int t5Result = t5.get();
+        IsTrue(t5Result == 468,
+               L"operator&& task did not produce the correct result. Expected: 468, Actual: %d",
+               t5Result);
+    }
+
+    TEST(TestTaskOperators_and_or4)
+    {
+        extensibility::event_t evt;
+
+        task<int> t1([&evt]() -> int {
+            evt.wait();
+            return 47;
+        });
+
+        task<int> t2([]() -> int { return 82; });
+
+        auto t3 = (t1 || t2).then([=](int p) -> int {
+            IsTrue(p == 82, L"operator|| did not get the right result. Expected: 82, Actual: %d", p);
+            return p;
+        });
+
+        t3.wait();
+
+        evt.set();
+        t1.wait();
+    }
+
+    TEST(TestTaskOperators_and_or5)
+    {
+        extensibility::event_t evt;
+
+        task<int> t1([&evt]() -> int {
+            evt.wait();
+            return 47;
+        });
+
+        task<int> t2([&evt]() -> int {
+            evt.wait();
+            return 82;
+        });
+
+        task<int> t3([]() -> int { return 147; });
+
+        task<int> t4([&evt]() -> int {
+            evt.wait();
+            return 192;
+        });
+
+        auto t5 = (t1 || t2 || t3 || t4).then([=](int result) -> int {
+            IsTrue(result == 147, L"operator|| did not produce a correct result. Expected: 147, Actual: %d", result);
+            return result;
+        });
+
+        t5.wait();
+
+        evt.set();
+        t1.wait();
+        t2.wait();
+        t4.wait();
+    }
+
+    TEST(TestTaskOperators_and_or_sequence)
+    {
+        // testing ( t1 && t2 ) || t3, operator&& finishes first
+        extensibility::event_t evt;
+
+        task<int> t1([]() -> int { return 47; });
+
+        task<int> t2([]() -> int { return 82; });
+
+        task<int> t3([&evt]() -> int {
+            evt.wait();
+            return 147;
+        });
+
+        auto t4 = ((t1 && t2) || t3).then([=](std::vector<int> vec) -> int {
+            IsTrue(vec.size() == 2,
+                   L"(t1 && t2) || t3 did not produce a correct vector size. Expected: 2, Actual: %d",
+                   vec.size());
+            IsTrue(vec[0] == 47,
+                   L"(t1 && t2) || t3 did not produce a correct vector[0]. Expected: 47, Actual: %d",
+                   vec[0]);
+            IsTrue(vec[1] == 82,
+                   L"(t1 && t2) || t3 did not produce a correct vector[1]. Expected: 82, Actual: %d",
+                   vec[1]);
+            return vec[0] + vec[1];
+        });
+
+        int t4Result = t4.get();
+        IsTrue(t4.get() == 129,
+               L"(t1 && t2) || t3 task did not produce the correct result. Expected: 129, Actual: %d",
+               t4Result);
+
+        evt.set();
+        t3.wait();
+    }
+
+    TEST(TestTaskOperators_and_or_sequence2)
+    {
+        // testing ( t1 && t2 ) || t3, operator|| finishes first
+        extensibility::event_t evt;
+
+        task<int> t1([&evt]() -> int {
+            evt.wait();
+            return 47;
+        });
+
+        task<int> t2([&evt]() -> int {
+            evt.wait();
+            return 82;
+        });
+
+        task<int> t3([]() -> int { return 147; });
+
+        auto t4 = ((t1 && t2) || t3).then([=](std::vector<int> vec) -> int {
+            IsTrue(vec.size() == 1,
+                   L"(t1 && t2) || t3 did not produce a correct vector size. Expected: 1, Actual: %d",
+                   vec.size());
+            IsTrue(vec[0] == 147,
+                   L"(t1 && t2) || t3 did not produce a correct vector[0]. Expected: 147, Actual: %d",
+                   vec[0]);
+            return vec[0];
+        });
+
+        int t4Result = t4.get();
+        IsTrue(t4.get() == 147,
+               L"(t1 && t2) || t3 task did not produce the correct result. Expected: 147, Actual: %d",
+               t4Result);
+
+        evt.set();
+        t1.wait();
+        t2.wait();
+    }
+
+    TEST(TestTaskOperators_and_or_sequence3)
+    {
+        // testing t1 && (t2 || t3)
+        extensibility::event_t evt;
+
+        task<int> t1([]() -> int { return 47; });
+
+        task<int> t2([&evt]() -> int {
+            evt.wait();
+            return 82;
+        });
+
+        task<int> t3([]() -> int { return 147; });
+
+        auto t4 = (t1 && (t2 || t3)).then([=](std::vector<int> vec) -> int {
+            IsTrue(vec.size() == 2,
+                   L"t1 && (t2 || t3) did not produce a correct vector size. Expected: 2, Actual: %d",
+                   vec.size());
+            IsTrue(vec[0] == 47,
+                   L"t1 && (t2 || t3) did not produce a correct vector[0]. Expected: 47, Actual: %d",
+                   vec[0]);
+            IsTrue(vec[1] == 147,
+                   L"t1 && (t2 || t3) did not produce a correct vector[1]. Expected: 147, Actual: %d",
+                   vec[1]);
+            return vec[0] + vec[1];
+        });
+
+        int t4Result = t4.get();
+        IsTrue(t4.get() == 194,
+               L"t1 && (t2 || t3) task did not produce the correct result. Expected: 194 Actual: %d",
+               t4Result);
+
+        evt.set();
+        t2.wait();
+    }
+
+    TEST(TestTaskOperators_cancellation)
+    {
+        task_completion_event<void> tce;
+        task<void> starter(tce);
+
+        cancellation_token_source ct;
+
+        task<int> t1 = starter.then([]() -> int { return 47; }, ct.get_token());
+
+        task<int> t2([]() -> int { return 82; });
+
+        task<int> t3([]() -> int { return 147; });
+
+        auto t4 = (t1 && t2 && t3).then([=](std::vector<int> vec) -> int { return vec[0] + vec[1] + vec[3]; });
+
+        ct.cancel();
+
+        tce.set();
+        // this should not hang
+        task_status t4Status = t4.wait();
+        IsTrue(
+            t4Status == canceled, L"operator && did not properly cancel. Expected: %d, Actual: %d", canceled, t4Status);
+    }
+
+    TEST(TestTaskOperators_cancellation_and)
+    {
+        task_completion_event<void> tce;
+        task<void> starter(tce);
+
+        cancellation_token_source ct;
+
+        task<void> t1 = starter.then([]() -> void {}, ct.get_token());
+
+        task<void> t2([]() -> void {});
+
+        task<void> t3([]() -> void {});
+
+        auto t4 = (t1 && t2 && t3).then([=]() {});
+
+        ct.cancel();
+
+        tce.set();
+        // this should not hang
+        task_status t4Status = t4.wait();
+        IsTrue(
+            t4Status == canceled, L"operator && did not properly cancel. Expected: %d, Actual: %d", canceled, t4Status);
+    }
+
+    TEST(TestTaskOperators_cancellation_or)
+    {
+        task_completion_event<void> tce;
+        task<void> starter(tce);
+
+        cancellation_token_source ct1;
+        cancellation_token_source ct2;
+        cancellation_token_source ct3;
+
+        task<int> t1 = starter.then([]() -> int { return 47; }, ct1.get_token());
+
+        task<int> t2 = starter.then([]() -> int { return 82; }, ct2.get_token());
+
+        task<int> t3 = starter.then([]() -> int { return 147; }, ct3.get_token());
+
+        auto t4 = (t1 || t2 || t3).then([=](int result) -> int { return result; });
+
+        ct1.cancel();
+        ct2.cancel();
+        ct3.cancel();
+
+        tce.set();
+        // this should not hang
+        task_status t4Status = t4.wait();
+        IsTrue(
+            t4Status == canceled, L"operator || did not properly cancel. Expected: %d, Actual: %d", canceled, t4Status);
+    }
+    TEST(TestTaskOperators_cancellation_or2)
+    {
+        task_completion_event<void> tce;
+        task<void> starter(tce);
+
+        cancellation_token_source ct1;
+        cancellation_token_source ct2;
+        cancellation_token_source ct3;
+
+        task<void> t1 = starter.then([]() -> void {}, ct1.get_token());
+
+        task<void> t2 = starter.then([]() -> void {}, ct2.get_token());
+
+        task<void> t3 = starter.then([]() -> void {}, ct3.get_token());
+
+        auto t4 = (t1 || t2 || t3).then([=]() {});
+
+        ct1.cancel();
+        ct2.cancel();
+        ct3.cancel();
+
+        tce.set();
+        // this should not hang
+        task_status t4Status = t4.wait();
+        IsTrue(
+            t4Status == canceled, L"operator || did not properly cancel. Expected: %d, Actual: %d", canceled, t4Status);
+    }
+
+    TEST(TestTaskOperators_cancellation_complex)
+    {
+        extensibility::event_t evt1, evt2;
+        pplx::details::atomic_long n(0);
+
+        cancellation_token_source ct;
+
+        task<void> t1(
+            [&n, &evt1, &evt2]() {
+                pplx::details::atomic_add(n, 1L); // this should execute
+                evt2.set();
+                evt1.wait();
+            },
+            ct.get_token());
+
+        task<void> t2 = t1.then([&n]() {
+            pplx::details::atomic_add(n, 10L); // this should NOT execute
+        });
+
+        task<void> t3 = t1.then([&n]() {
+            pplx::details::atomic_add(n, 100L); // this should NOT execute
+        });
+
+        task<void> t4 = t1.then([&n](task<void> taskResult) {
+            pplx::details::atomic_add(n, 1000L); // this should execute
+        });
+
+        task<void> t5 = t1.then([&n](task<void> taskResult) {
+            try
+            {
+                taskResult.get();
+                pplx::details::atomic_add(n, 10000L); // this should execute
+            }
+            catch (task_canceled&)
+            {
+                pplx::details::atomic_add(n, 100000L); // this should NOT execute
+            }
+        });
+
+        evt2.wait();
+        ct.cancel();
+        evt1.set();
+
+        IsTrue((t2 && t3).wait() == canceled, L"(t1 && t2) was not canceled");
+        IsTrue((t2 || t3 || t4 || t5).wait() == completed, L"(t2 || t3 || t4 || t5) did not complete");
+        IsTrue((t4 && t5).wait() == completed, L"(t4 && t5) did not complete");
+
+        try
+        {
+            t1.get();
+        }
+        catch (task_canceled&)
+        {
+            LogFailure(L"get() on canceled task t1 should not throw a task_canceled exception.");
+        }
+
+        try
+        {
+            t2.get();
+            LogFailure(L"get() on canceled task t2 should throw a task_canceled exception.");
+        }
+        catch (task_canceled&)
+        {
+        }
+
+        try
+        {
+            t3.get();
+            LogFailure(L"get() on canceled task t3 should throw a task_canceled exception.");
+        }
+        catch (task_canceled&)
+        {
+        }
+
+        try
+        {
+            t4.get();
+            t5.get();
+        }
+        catch (...)
+        {
+            LogFailure(L"get() on completed tasks threw an exception.");
+        }
+        IsTrue(
+            n == 11001L,
+            L"The right result was not obtained from the sequence of tasks that executed. Expected: 11001, Actual: %d",
+            static_cast<long>(n));
+    }
+
+    TEST(TestTaskOperators_cancellation_exception)
+    {
+        extensibility::event_t evt1, evt2;
+        pplx::details::atomic_long n(0);
+
+        cancellation_token_source ct;
+
+        task<void> t1(
+            [&n, &evt1, &evt2]() {
+                evt2.set();
+                evt1.wait();
+            },
+            ct.get_token());
+
+        task<void> t2([&n]() { throw 42; });
+
+        for (int i = 0; i < 5; ++i)
+        {
+            try
+            {
+                t2.get();
+                LogFailure(L"Exception was not received from t2.get()");
+            }
+            catch (int x)
+            {
+                IsTrue(x == 42, L"Incorrect integer was thrown from t2.get(). Expected: 42, Actual: %d", x);
+            }
+            catch (task_canceled&)
+            {
+                LogFailure(L"task_canceled was thrown from t2.get() when an integer was expected");
+            }
+        }
+
+        for (int i = 0; i < 5; ++i)
+        {
+            try
+            {
+                t2.wait();
+                LogFailure(L"Exception was not received from t2.wait()");
+            }
+            catch (int x)
+            {
+                IsTrue(x == 42, L"Incorrect integer was thrown from t2.wait(). Expected: 42, Actual: %d", x);
+            }
+            catch (task_canceled&)
+            {
+                LogFailure(L"task_canceled was thrown from t2.wait() when an integer was expected");
+            }
+        }
+
+        task<void> t3 = t1.then([&n]() {
+            pplx::details::atomic_add(n, 1L); // this should NOT execute,
+        });
+
+        task<void> t4 = t1.then([&n](task<void> taskResult) {
+            pplx::details::atomic_add(n, 10L); // this should execute
+        });
+
+        task<void> t5 = t2.then([&n]() {
+            pplx::details::atomic_add(n, 100L); // this should NOT execute
+        });
+
+        task<void> t6 = t2.then([&n](task<void> taskResult) {
+            pplx::details::atomic_add(n, 1000L);  // this should execute
+            taskResult.get();                     // should throw 42
+            pplx::details::atomic_add(n, 10000L); // this should NOT execute
+        });
+
+        task<void> t7 = t2.then([&n, this](task<void> taskResult) {
+            try
+            {
+                taskResult.get();
+                pplx::details::atomic_add(n, 100000L); // this should NOT execute
+            }
+            catch (int x)
+            {
+                IsTrue(
+                    x == 42,
+                    L"Incorrect integer exception was received in t7 from taskresult.get(). Expected: 42, Actual: %d",
+                    x);
+                pplx::details::atomic_add(n, 1000000L); // this should execute
+            }
+            catch (task_canceled)
+            {
+                LogFailure(L"task_canceled was thrown by taskResult.get() in t7");
+            }
+            catch (...)
+            {
+                LogFailure(L"A random exception was thrown by taskResult.get() in t7");
+            }
+
+            throw 96;
+        });
+
+        task<void> t8 = (t6 || t7).then([&n, this](task<void> taskResult) {
+            try
+            {
+                taskResult.get();
+                pplx::details::atomic_add(n, 1000000L); // this should NOT execute
+            }
+            catch (int x)
+            {
+                IsTrue((x == 42 || x == 96),
+                       L"Incorrect integer exception was received in t7 from taskresult.get(). Expected: 42 or 96, "
+                       L"Actual: %d",
+                       x);
+                pplx::details::atomic_add(n, 100000000L); // this should execute
+            }
+            catch (task_canceled)
+            {
+                LogFailure(L"(t6 || t7) was canceled without an exception");
+            }
+            catch (...)
+            {
+                LogFailure(L"(t6 || t7) was canceled with an unexpected exception");
+            }
+        });
+
+        // Cancel t1 now that t2 is guaranteed canceled with an exception
+        evt2.wait();
+        ct.cancel();
+        evt1.set();
+
+        try
+        {
+            task_status status = (t1 && t2).wait();
+            IsTrue((status == canceled),
+                   L"(t1 && t2).wait() did not return canceled. Expected: %d, Actual %d",
+                   canceled,
+                   status);
+        }
+        catch (int x)
+        {
+            IsTrue(x == 42,
+                   L"Incorrect integer exception was received from (t1 && t2).wait(). Expected: 42, Actual: %d",
+                   x);
+        }
+
+        try
+        {
+            task_status status = t3.wait();
+            IsTrue((status == canceled),
+                   L"t3.wait() did not returned canceled. Expected: %d, Actual %d",
+                   canceled,
+                   status);
+        }
+        catch (task_canceled&)
+        {
+            LogFailure(L"t3.wait() threw task_canceled instead of returning canceled");
+        }
+        catch (...)
+        {
+            LogFailure(L"t3.wait() threw an unexpected exception");
+        }
+
+        try
+        {
+            task_status status = t4.wait();
+            IsTrue((status == completed),
+                   L"t4.wait() did not returned completed. Expected: %d, Actual %d",
+                   completed,
+                   status);
+        }
+        catch (...)
+        {
+            LogFailure(L"t4.wait() threw an unexpected exception");
+        }
+
+        try
+        {
+            t5.wait();
+            LogFailure(L"t5.wait() did not throw an exception");
+        }
+        catch (int x)
+        {
+            IsTrue(x == 42, L"Incorrect integer exception was received from t5.wait(). Expected: 42, Actual: %d", x);
+        }
+
+        // Observe the exceptions from t5, t6 and t7
+        helpers::ObserveException(t5);
+        helpers::ObserveException(t6);
+        helpers::ObserveException(t7);
+
+        try
+        {
+            (t1 || t6).get();
+            LogFailure(L"(t1 || t6).get() should throw an exception.");
+        }
+        catch (task_canceled&)
+        {
+            LogFailure(L"(t1 || t6).get() threw task_canceled when an int was expected.");
+        }
+        catch (int x)
+        {
+            IsTrue(
+                (x == 42 || x == 96),
+                L"Incorrect integer exception was received from (t1 || t6 || t7).get(). Expected: 42 or 96, Actual: %d",
+                x);
+        }
+
+        t8.wait();
+
+        IsTrue(n == 101001010L,
+               L"The right result was not obtained from the sequence of tasks that executed. Expected 101001010, "
+               L"actual %d",
+               101001010,
+               static_cast<long>(n));
+    }
+
+    TEST(TestTaskOperators_when_all_cancellation)
+    {
+        // A task that participates in a 'when all' operation is canceled and then throws an exception. Verify that
+        // value and task based continuations of the when all task see the exception.
+        extensibility::event_t evt1, evt2;
+
+        cancellation_token_source ct;
+
+        task<void> t1(
+            [&evt1, &evt2]() {
+                evt2.set();
+                evt1.wait();
+                os_utilities::sleep(100);
+                throw 42;
+            },
+            ct.get_token());
+
+        task<void> t2([]() { helpers::DoRandomParallelWork(); });
+
+        task<void> t3([]() { helpers::DoRandomParallelWork(); });
+
+        task<void> whenAllTask = t1 && t2 && t3;
+
+        task<void> t4 = whenAllTask.then([this](task<void> t) {
+            IsFalse(helpers::VerifyCanceled(t), L"%ws:%u:t should be canceled by token", __FILE__, __LINE__);
+            IsTrue(helpers::VerifyException<int>(t), L"%ws:%u:exception from t is unexpected", __FILE__, __LINE__);
+        });
+
+        task<void> t5 =
+            whenAllTask.then([this]() { LogFailure(L"%ws:%u:t5 was unexpectedly executed", __FILE__, __LINE__); });
+
+        evt2.wait();
+        ct.cancel();
+        evt1.set();
+
+        IsFalse(helpers::VerifyCanceled(t5), L"%ws:%u:t5 should be canceled", __FILE__, __LINE__);
+    }
+
+    TEST(TestTaskOperators_when_all_cancellation_sequence)
+    {
+        // A task that participates in a 'when all' operation throws an exception, but a continuation of the when all
+        // task is canceled before this point. Ensure that continuation does not get the exception but others do.
+        extensibility::event_t evt1, evt2;
+
+        cancellation_token_source ct;
+
+        task<void> t1([&evt1, &evt2]() {
+            evt2.set();
+            evt1.wait();
+            os_utilities::sleep(100);
+            throw 42;
+        });
+
+        task<void> t2([]() { helpers::DoRandomParallelWork(); });
+
+        task<void> t3([]() { helpers::DoRandomParallelWork(); });
+
+        task<void> whenAllTask = t1 && t2 && t3;
+
+        task<void> t4 = whenAllTask.then([this](task<void> t) {
+            IsFalse(helpers::VerifyCanceled(t), L"%ws:%u:t was unexpectedly canceled", __FILE__, __LINE__);
+            IsTrue(helpers::VerifyException<int>(t),
+                   L"%ws:%u:Did not receive the correct exception from t",
+                   __FILE__,
+                   __LINE__);
+        });
+
+        task<void> t5 =
+            whenAllTask.then([this]() { LogFailure(L"%ws:%u:t5 was unexpectedly executed", __FILE__, __LINE__); });
+
+        task<void> t6 = whenAllTask.then(
+            [this](task<void> t) {
+                IsTrue(helpers::VerifyCanceled(t), L"%ws:%u:t was not canceled as expected", __FILE__, __LINE__);
+            },
+            ct.get_token());
+
+        evt2.wait();
+        ct.cancel();
+        evt1.set();
+
+        IsTrue(helpers::VerifyException<int>(t5),
+               L"%ws:%u:Did not receive the correct exception from t5",
+               __FILE__,
+               __LINE__);
+    }
+
+    TEST(TestTaskOperators_and_cancellation_multiple_tokens)
+    //
+    // operator&& with differing tokens:
+    //
+    {
+        cancellation_token_source ct1;
+        cancellation_token_source ct2;
+        cancellation_token_source ct3;
+        cancellation_token_source ct4;
+
+        task<int> t1([]() -> int { return 42; }, ct1.get_token());
+
+        task<int> t2([]() -> int { return 77; }, ct2.get_token());
+
+        task<int> t3([]() -> int { return 92; }, ct3.get_token());
+
+        task<int> t4([]() -> int { return 147; }, ct4.get_token());
+
+        auto t5 = t1 && t2 && t3 && t4;
+
+        extensibility::event_t ev1, ev2;
+
+        auto t6 = t5.then([&ev1, &ev2](std::vector<int> iVec) -> int {
+            ev2.set();
+            ev1.wait();
+            return iVec[0] + iVec[1] + iVec[2] + iVec[3];
+        });
+
+        auto t7 = t6.then([](int val) -> int { return val; });
+
+        ev2.wait();
+        ct3.cancel();
+        ev1.set();
+        t6.wait();
+        t7.wait();
+
+        bool caughtCanceled = false;
+
+        try
+        {
+            t7.get();
+        }
+        catch (task_canceled&)
+        {
+            caughtCanceled = true;
+        }
+
+        IsTrue(caughtCanceled, L"Cancellation token was not joined/inherited on operator&&");
+    }
+
+    struct TestException1
+    {
+    };
+
+    struct TestException2
+    {
+    };
+
+    // CodePlex 292
+    static int ThrowFunc() { throw 42; }
+
+    TEST(TestContinuationsWithTask1)
+    {
+        int n2 = 0;
+
+        task<int> t([&]() -> int { return 10; });
+
+        t.then([&](task<int> ti) { n2 = ti.get(); }).wait();
+
+        VERIFY_IS_TRUE(n2 == 10);
+    }
+
+    TEST(TestContinuationsWithTask2)
+    {
+        int n = 0;
+
+        task<void> tt1([]() {});
+        auto tt2 = tt1.then([&]() -> task<void> {
+            task<void> tt3([&]() { n = 1; });
+            return tt3;
+        });
+
+        tt2.get();
+        VERIFY_IS_TRUE(n == 1);
+
+        task<void> tt4 = tt2.then([&]() -> task<void> {
+            task<void> tt5([&]() { n = 2; });
+            return tt5;
+        });
+        tt4.get();
+        VERIFY_IS_TRUE(n == 2);
+    }
+
+    TEST(TestContinuationsWithTask3)
+    {
+        bool gotException = true;
+        int n2 = 0;
+        task<int> t(ThrowFunc);
+        t.then([&](task<int> ti) {
+             try
+             {
+                 ti.get();
+                 gotException = false;
+             }
+             catch (int)
+             {
+                 n2 = 20;
+             }
+         })
+            .wait();
+
+        VERIFY_IS_TRUE(gotException);
+        VERIFY_IS_TRUE(n2 == 20);
+    }
+
+    TEST(TestContinuationsWithTask4)
+    {
+        int n2 = 0;
+
+        task<int> t([&]() -> int { return 10; });
+
+        t.then([&](int n) -> task<int> {
+             task<int> t2([n]() -> int { return n + 10; });
+             return t2;
+         })
+            .then([&](int n) { n2 = n; })
+            .wait();
+
+        VERIFY_IS_TRUE(n2 == 20);
+    }
+
+    TEST(TestContinuationsWithTask5)
+    {
+        int n2 = 0;
+
+        task<int> t([&]() -> int { return 10; });
+
+        t.then([&](task<int> tn) -> task<int> {
+             int n = tn.get();
+             task<int> t2([n]() -> int { return n + 10; });
+             return t2;
+         })
+            .then([&](task<int> n) { n2 = n.get(); })
+            .wait();
+
+        VERIFY_IS_TRUE(n2 == 20);
+    }
+
+    TEST(TestContinuationsWithTask6)
+    {
+        pplx::details::atomic_long hit(0);
+        auto* hitptr = &hit;
+        task<int> t([]() { return 10; });
+
+        auto ot = t.then([hitptr](int n) -> task<int> {
+            auto hitptr1 = hitptr;
+            task<int> it([n, hitptr1]() -> int {
+                os_utilities::sleep(100);
+                pplx::details::atomic_exchange(*hitptr1, 1L);
+                return n * 2;
+            });
+
+            return it;
+        });
+
+        int value = ot.get();
+        VERIFY_IS_TRUE(value == 20 && hit != 0);
+    }
+
+    TEST(TestContinuationsWithTask7)
+    {
+        volatile long hit = 0;
+        volatile long* hitptr = &hit;
+
+        task<int> t([]() { return 10; });
+
+        auto ot = t.then([hitptr](int n) -> task<int> {
+            task<int> it([n, hitptr]() -> int { throw TestException1(); });
+
+            return it;
+        });
+
+        VERIFY_IS_TRUE(helpers::VerifyException<TestException1>(ot));
+    }
+
+    TEST(TestContinuationsWithTask8)
+    {
+        volatile long hit = 0;
+        volatile long* hitptr = &hit;
+
+        task<int> t([]() { return 10; });
+
+        auto ot = t.then([hitptr](int n) -> task<int> {
+            volatile long* hitptr1 = hitptr;
+            task<int> it([n, hitptr1]() -> int {
+                os_utilities::sleep(100);
+                os_utilities::interlocked_exchange(hitptr1, 1);
+
+                // This test is needed to disable an optimizer dead-code check that
+                // winds up generating errors in VS 2010.
+                if (n == 10) throw TestException2();
+
+                return n * 3;
+            });
+
+            return it;
+        });
+
+        VERIFY_IS_TRUE(helpers::VerifyException<TestException2>(ot),
+                       "(7) Inner task exception not propagated out of outer .get()");
+        VERIFY_IS_TRUE(hit != 0, "(7) Expected inner task hit marker to be set!");
+    }
+
+    TEST(TestContinuationsWithTask9)
+    {
+        volatile long hit = 0;
+        volatile long* hitptr = &hit;
+        extensibility::event_t e;
+        task<int> it;
+
+        task<int> t([]() { return 10; });
+
+        auto ot = t.then([hitptr, &it, &e](int n) -> task<int> {
+            volatile long* hitptr1 = hitptr;
+            it = task<int>([hitptr1, n]() -> int {
+                os_utilities::interlocked_exchange(hitptr1, 1);
+                // This test is needed to disable an optimizer dead-code check that
+                // winds up generating errors in VS 2010.
+                if (n == 10) throw TestException1();
+                return n * 5;
+            });
+
+            e.set();
+            os_utilities::sleep(100);
+            // This test is needed to disable an optimizer dead-code check that
+            // winds up generating errors in VS 2010.
+            if (n == 10) throw TestException2();
+            return it;
+        });
+
+        e.wait();
+
+        VERIFY_IS_TRUE(helpers::VerifyException<TestException2>(ot),
+                       "(8) Outer task exception not propagated when inner task also throws");
+        VERIFY_IS_TRUE(helpers::VerifyException<TestException1>(it),
+                       "(8) Inner task exception not explicitly propgated on pass out / get");
+        VERIFY_IS_TRUE(hit != 0, "(8) Inner hit marker expected!");
+    }
+
+    TEST(TestContinuationsWithTask10)
+    {
+        volatile long hit = 0;
+
+        task<int> t([]() { return 10; });
+
+        auto ot = t.then([&](int n) -> task<int> {
+            task<int> it([&, n]() -> int {
+                os_utilities::sleep(100);
+                // This test is needed to disable an optimizer dead-code check that
+                // winds up generating errors in VS 2010.
+                if (n == 10) throw TestException1();
+                return n * 6;
+            });
+            return it;
+        });
+
+        auto otc = ot.then([&](task<int> itp) {
+            os_utilities::interlocked_exchange(&hit, 1);
+            VERIFY_IS_TRUE(helpers::VerifyException<TestException1>(itp),
+                           "(9) Outer task exception handling continuation did not get plumbed inner exception");
+        });
+
+        VERIFY_IS_TRUE(helpers::VerifyException<TestException1>(ot),
+                       "(9) Inner task exception not propagated correctly");
+        helpers::ObserveException(otc);
+        VERIFY_IS_TRUE(hit != 0, "(9) Outer task exception handling continuation did not run!");
+    }
+
+    TEST(TestUnwrappingCtors)
+    {
+        int res;
+        {
+            // take task<int> in the ctor
+
+            task<int> ti([]() -> int { return 1; });
+
+            // Must unwrap:
+            task<int> t1(ti);
+            res = t1.get();
+            VERIFY_IS_TRUE(res == 1, "unexpected value in TestUnwrappingCtors, location 1");
+        }
+
+        {
+            // take lambda returning task<int> in the ctor
+
+            // Must NOT unwrap:
+            task<task<int>> t1([]() -> task<int> {
+                task<int> ti([]() -> int { return 1; });
+                return ti;
+            });
+            res = t1.get().get();
+            VERIFY_IS_TRUE(res == 1, "unexpected value in TestUnwrappingCtors, location 2");
+
+            // Must unwrap:
+            task<int> t2([]() -> task<int> {
+                task<int> ti([]() -> int { return 2; });
+                return ti;
+            });
+            res = t2.get();
+            VERIFY_IS_TRUE(res == 2, "unexpected value in TestUnwrappingCtors, location 3");
+
+            res = t2.then([](int n) { return n + 1; }).get();
+            VERIFY_IS_TRUE(res == 3, "unexpected value in TestUnwrappingCtors, location 4");
+        }
+
+        {
+            int executed = 0;
+            // take task<void> in the ctor
+            task<void> ti([&]() { executed = 1; });
+
+            // Must unwrap:
+            task<void> t1(ti);
+            t1.wait();
+            VERIFY_IS_TRUE(executed == 1, "unexpected value in TestUnwrappingCtors, location 5");
+        }
+
+        {
+            // take lambda returning task<void> in the ctor
+
+            int executed = 0;
+            int* executedPtr = &executed;
+
+            // Must NOT unwrap:
+            task<task<void>> t1([executedPtr]() -> task<void> {
+                auto executedPtr1 = executedPtr;
+                task<void> ti([executedPtr1]() { *executedPtr1 = 1; });
+                return ti;
+            });
+            t1.get().get();
+            VERIFY_IS_TRUE(executed == 1, "unexpected value in TestUnwrappingCtors, location 6");
+
+            task<void> t2([]() {});
+            // Must unwrap:
+            task<void> t3 = t2.then([executedPtr]() -> task<void> {
+                auto executedPtr1 = executedPtr;
+                task<void> ti([executedPtr1]() { *executedPtr1 = 2; });
+                return ti;
+            });
+
+            t3.wait();
+            VERIFY_IS_TRUE(executed == 2, "unexpected value in TestUnwrappingCtors, location 7");
+
+            // Must unwrap:
+            task<void> t4([executedPtr]() -> task<void> {
+                auto executedPtr1 = executedPtr;
+                task<void> ti([executedPtr1]() { *executedPtr1 = 3; });
+                return ti;
+            });
+            t4.wait();
+            VERIFY_IS_TRUE(executed == 3, "unexpected value in TestUnwrappingCtors, location 8");
+
+            t4.then([&]() { executed++; }).wait();
+            VERIFY_IS_TRUE(executed == 4, "unexpected value in TestUnwrappingCtors, location 9");
+        }
+
+        {
+            res = create_task([]() -> task<int> { return create_task([]() -> int { return 1; }); }).get();
+            VERIFY_IS_TRUE(res == 1, "unexpected value in TestUnwrappingCtors, create_task, location 1");
+
+            create_task([]() -> task<void> { return create_task([]() {}); }).wait();
+        }
+
+        {
+            // BUG TFS: 344954
+            cancellation_token_source cts, cts2;
+            cts.cancel(); // Commenting this line out makes the program work!
+            // Create a task that is always cancelled
+            auto falseTask = create_task([]() {}, cts.get_token());
+            cancellation_token ct2 = cts2.get_token();
+            create_task(
+                [falseTask]() {
+                    // Task unwrapping!
+                    // This should not crash
+                    return falseTask;
+                },
+                ct2)
+                .then([this, falseTask, ct2](task<void> t) -> task<void> {
+                    VERIFY_IS_TRUE(t.wait() == canceled,
+                                   "unexpected value in TestUnwrappingCtors, cancellation token, location 1");
+                    VERIFY_IS_TRUE(!ct2.is_canceled(),
+                                   "unexpected value in TestUnwrappingCtors, cancellation token, location 2");
+                    // again, unwrapping in continuation
+                    // this should not crash
+                    return falseTask;
+                })
+                .then([this] {
+                    VERIFY_IS_TRUE(false, "unexpected path in TestUnwrappingCtors, cancellation token, location 3");
+                });
+        }
+    }
+
+    TEST(TestNestedTasks)
+    {
+        {
+            task<int> rootTask([]() -> int { return 234; });
+
+            task<task<int>> resultTask = rootTask.then([](int value) -> task<task<int>> {
+                return task<task<int>>([=]() -> task<int> {
+                    auto val1 = value;
+                    return task<int>([=]() -> int { return val1 + 22; });
+                });
+            });
+
+            int n = resultTask.get().get();
+            VERIFY_IS_TRUE(n == 256, "TestNestedTasks_1");
+        }
+
+        {
+            // Same for void task
+            int flag = 1;
+            int* flagptr = &flag;
+            task<void> rootTask([&]() { flag++; });
+
+            task<task<void>> resultTask = rootTask.then([flagptr]() -> task<task<void>> {
+                auto flag1 = flagptr;
+                return task<task<void>>([flag1]() -> task<void> {
+                    auto flag2 = flag1;
+                    return task<void>([flag2]() { ++(flag2[0]); });
+                });
+            });
+
+            resultTask.get().wait();
+            VERIFY_IS_TRUE(flag == 3, "TestNestedTasks_2");
+        }
+
+        {
+            task<int> rootTask([]() -> int { return 234; });
+
+            task<task<task<int>>> resultTask = rootTask.then([](int value) -> task<task<task<int>>> {
+                return task<task<task<int>>>([=]() -> task<task<int>> {
+                    auto v1 = value;
+                    return task<task<int>>([=]() -> task<int> {
+                        auto v2 = v1;
+                        return task<int>([=]() -> int { return v2 + 22; });
+                    });
+                });
+            });
+
+            int n = resultTask.get().get().get();
+            VERIFY_IS_TRUE(n == 256, "TestNestedTasks_3");
+        }
+
+        {
+            task<void> nestedTask;
+            task<void> unwrap([&]() -> task<void> {
+                nestedTask = task<void>([]() { cancel_current_task(); });
+                return nestedTask;
+            });
+            task_status st = unwrap.wait();
+            VERIFY_IS_TRUE(st == canceled, "TestNestedTasks_4");
+            st = nestedTask.wait();
+            VERIFY_IS_TRUE(st == canceled, "TestNestedTasks_5 ");
+        }
+    }
+
+    template<typename Function>
+    task<void> async_for(int start, int step, int end, Function func)
+    {
+        if (start < end)
+        {
+            return func(start).then([=]() -> task<void> { return async_for(start + step, step, end, func); });
+        }
+        else
+        {
+            return task<void>([] {});
+        }
+    }
+
+    TEST(TestInlineChunker)
+    {
+        const int numiter = 1000;
+        volatile int sum = 0;
+        async_for(0,
+                  1,
+                  numiter,
+                  [&](int) -> task<void> {
+                      sum++;
+                      return create_task([]() {});
+                  })
+            .wait();
+
+        VERIFY_IS_TRUE(sum == numiter, "TestInlineChunker: async_for did not return correct result.");
+    }
+
+#if defined(_WIN32) && (_MSC_VER >= 1700) && (_MSC_VER < 1800)
+
+    TEST(PPL_Conversions_basic)
+    {
+        pplx::task<int> t1([] { return 1; });
+        concurrency::task<int> t2 = pplx::pplx_task_to_concurrency_task(t1);
+        int n = t2.get();
+        VERIFY_ARE_EQUAL(n, 1);
+
+        pplx::task<int> t3 = pplx::concurrency_task_to_pplx_task(t2);
+        int n2 = t3.get();
+        VERIFY_ARE_EQUAL(n2, 1);
+    }
+
+    TEST(PPL_Conversions_Nested)
+    {
+        pplx::task<int> t1([] { return 12; });
+        pplx::task<int> t2 = pplx::concurrency_task_to_pplx_task(pplx::pplx_task_to_concurrency_task(
+            pplx::concurrency_task_to_pplx_task(pplx::pplx_task_to_concurrency_task(t1))));
+        int n = t2.get();
+        VERIFY_ARE_EQUAL(n, 12);
+    }
+
+    TEST(PPL_Conversions_Exceptions)
+    {
+        pplx::task<int> t1(ThrowFunc);
+        concurrency::task<int> t2 = pplx::pplx_task_to_concurrency_task(t1);
+        try
+        {
+            t2.get();
+            VERIFY_IS_TRUE(false);
+        }
+        catch (int m)
+        {
+            VERIFY_ARE_EQUAL(m, 42);
+        }
+
+        pplx::task<int> t3 = pplx::concurrency_task_to_pplx_task(t2);
+        try
+        {
+            t3.get();
+            VERIFY_IS_TRUE(false);
+        }
+        catch (int m)
+        {
+            VERIFY_ARE_EQUAL(m, 42);
+        }
+    }
+
+    TEST(PPL_Conversions_Basic_void)
+    {
+        pplx::task<void> t1([] {});
+        concurrency::task<void> t2 = pplx::pplx_task_to_concurrency_task(t1);
+        t2.get();
+
+        pplx::task<void> t3 = pplx::concurrency_task_to_pplx_task(t2);
+        t3.get();
+    }
+
+    TEST(PPL_Conversions_Exceptions_void)
+    {
+        pplx::task<void> t1([]() { throw 3; });
+        concurrency::task<void> t2 = pplx::pplx_task_to_concurrency_task(t1);
+        try
+        {
+            t2.get();
+            VERIFY_IS_TRUE(false);
+        }
+        catch (int m)
+        {
+            VERIFY_ARE_EQUAL(m, 3);
+        }
+
+        pplx::task<void> t3 = pplx::concurrency_task_to_pplx_task(t2);
+        try
+        {
+            t3.get();
+            VERIFY_IS_TRUE(false);
+        }
+        catch (int m)
+        {
+            VERIFY_ARE_EQUAL(m, 3);
+        }
+    }
+
+#endif
+
+} // SUITE(pplxtask_tests)
+
+} // namespace PPLX
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/pplx/pplx_test/stdafx.cpp b/Release/tests/functional/pplx/pplx_test/stdafx.cpp
new file mode 100644 (file)
index 0000000..155f888
--- /dev/null
@@ -0,0 +1,14 @@
+/***
+ * 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 pplx_test_generate_lib = 0;
+#endif
diff --git a/Release/tests/functional/pplx/pplx_test/stdafx.h b/Release/tests/functional/pplx/pplx_test/stdafx.h
new file mode 100644 (file)
index 0000000..790829e
--- /dev/null
@@ -0,0 +1,33 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Pre-compiled headers
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#ifdef _WIN32
+#include <Windows.h>
+#endif
+
+#include "pplx/pplxtasks.h"
+#include <fstream>
+#include <memory>
+#include <stdio.h>
+#include <time.h>
+#include <vector>
+
+#if defined(_WIN32)
+#include "pplx/pplxconv.h"
+#else
+#include "pplx/threadpool.h"
+#endif
+
+#include "cpprest/asyncrt_utils.h"
+#include "os_utilities.h"
+#include "unittestpp.h"
diff --git a/Release/tests/functional/streams/CMakeLists.txt b/Release/tests/functional/streams/CMakeLists.txt
new file mode 100644 (file)
index 0000000..92077b0
--- /dev/null
@@ -0,0 +1,27 @@
+set(SOURCES
+  fstreambuf_tests.cpp
+  istream_tests.cpp
+  memstream_tests.cpp
+  ostream_tests.cpp
+  stdstream_tests.cpp
+)
+if(WINDOWS_STORE OR WINDOWS_PHONE)
+  list(APPEND SOURCES winrt_interop_tests.cpp)
+else()
+  list(APPEND SOURCES fuzz_tests.cpp)
+  if(WIN32)
+    list(APPEND SOURCES CppSparseFile.cpp)
+  endif()
+endif()
+
+add_casablanca_test(streams_test SOURCES)
+if(NOT WIN32 OR CPPREST_WEBSOCKETS_IMPL STREQUAL "wspp")
+  cpprest_find_boost()
+  if(NOT TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT")
+    target_link_libraries(streams_test PRIVATE cpprestsdk_boost_internal)
+  else()
+    target_include_directories(streams_test PRIVATE $<TARGET_PROPERTY:cpprestsdk_boost_internal,INTERFACE_INCLUDE_DIRECTORIES>)
+  endif()
+endif()
+
+configure_pch(streams_test stdafx.h stdafx.cpp)
diff --git a/Release/tests/functional/streams/CppSparseFile.cpp b/Release/tests/functional/streams/CppSparseFile.cpp
new file mode 100644 (file)
index 0000000..a4afc6f
--- /dev/null
@@ -0,0 +1,227 @@
+/****************************** Module Header ******************************\
+* Module Name:  CppSparseFile.cpp
+* Project:      CppSparseFile
+* URL: http://code.msdn.microsoft.com/windowsapps/CppSparseFile-7f28156b
+* Copyright (c) Microsoft Corporation.
+*
+* CppSparseFile demonstrates the common operations on sparse files. A sparse
+* file is a type of computer file that attempts to use file system space more
+* efficiently when blocks allocated to the file are mostly empty. This is
+* achieved by writing brief information (metadata) representing the empty
+* blocks to disk instead of the actual "empty" space which makes up the
+* block, using less disk space. You can find in this example the creation of
+* sparse file, the detection of sparse attribute, the retrieval of sparse
+* file size, and the query of sparse file layout.
+*
+* This source is subject to the Microsoft Public License.
+* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
+* All other rights reserved.
+*
+* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
+* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
+\***************************************************************************/
+
+#pragma region Includes
+#include "stdafx.h"
+
+#include "CppSparseFile.h"
+#pragma endregion
+
+/*!
+ * VolumeSupportsSparseFiles determines if the volume supports sparse streams.
+ *
+ * \param lpRootPathName
+ * Volume root path e.g. C:\
+ */
+BOOL VolumeSupportsSparseFiles(LPCTSTR lpRootPathName)
+{
+    DWORD dwVolFlags;
+    GetVolumeInformation(lpRootPathName, NULL, MAX_PATH, NULL, NULL, &dwVolFlags, NULL, MAX_PATH);
+
+    return (dwVolFlags & FILE_SUPPORTS_SPARSE_FILES) ? TRUE : FALSE;
+}
+
+/*!
+ * IsSparseFile determines if a file is sparse.
+ *
+ * \param lpFileName
+ * File name
+ */
+BOOL IsSparseFile(LPCTSTR lpFileName)
+{
+    // Open the file for read
+    HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE) return FALSE;
+
+    // Get file information
+    BY_HANDLE_FILE_INFORMATION bhfi;
+    GetFileInformationByHandle(hFile, &bhfi);
+    CloseHandle(hFile);
+
+    return (bhfi.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) ? TRUE : FALSE;
+}
+
+/*!
+ * Get sparse file sizes.
+ *
+ * \param lpFileName
+ * File name
+ *
+ * \see
+ * http://msdn.microsoft.com/en-us/library/aa365276.aspx
+ */
+BOOL GetSparseFileSize(LPCTSTR lpFileName)
+{
+    // Retrieves the size of the specified file, in bytes. The size includes
+    // both allocated ranges and sparse ranges.
+    HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE) return FALSE;
+    LARGE_INTEGER liSparseFileSize;
+    GetFileSizeEx(hFile, &liSparseFileSize);
+
+    // Retrieves the file's actual size on disk, in bytes. The size does not
+    // include the sparse ranges.
+    LARGE_INTEGER liSparseFileCompressedSize;
+    liSparseFileCompressedSize.LowPart =
+        GetCompressedFileSize(lpFileName, (LPDWORD)&liSparseFileCompressedSize.HighPart);
+
+    // Print the result
+    wprintf(L"\nFile total size: %I64uKB\nActual size on disk: %I64uKB\n",
+            liSparseFileSize.QuadPart / 1024,
+            liSparseFileCompressedSize.QuadPart / 1024);
+
+    CloseHandle(hFile);
+    return TRUE;
+}
+
+/*!
+ * Create a sparse file.
+ *
+ * \param lpFileName
+ * The name of the sparse file
+ */
+HANDLE CreateSparseFile(LPCTSTR lpFileName)
+{
+    // Create a normal file
+    HANDLE hSparseFile = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if (hSparseFile == INVALID_HANDLE_VALUE) return hSparseFile;
+
+    // Use the DeviceIoControl function with the FSCTL_SET_SPARSE control
+    // code to mark the file as sparse. If you don't mark the file as sparse,
+    // the FSCTL_SET_ZERO_DATA control code will actually write zero bytes to
+    // the file instead of marking the region as sparse zero area.
+    DWORD dwTemp;
+    DeviceIoControl(hSparseFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp, NULL);
+
+    return hSparseFile;
+}
+
+/*!
+ * Converting a file region to A sparse zero area.
+ *
+ * \param hSparseFile
+ * Handle of the sparse file
+ *
+ * \param start
+ * Start address of the sparse zero area
+ *
+ * \param size
+ * Size of the sparse zero block. The minimum sparse size is 64KB.
+ *
+ * \remarks
+ * Note that SetSparseRange does not perform actual file I/O, and unlike the
+ * WriteFile function, it does not move the current file I/O pointer or sets
+ * the end-of-file pointer. That is, if you want to place a sparse zero block
+ * in the end of the file, you must move the file pointer accordingly using
+ * the FileStream.Seek function, otherwise DeviceIoControl will have no effect
+ */
+void SetSparseRange(HANDLE hSparseFile, LONGLONG start, LONGLONG size)
+{
+    // Specify the starting and the ending address (not the size) of the
+    // sparse zero block
+    FILE_ZERO_DATA_INFORMATION fzdi;
+    fzdi.FileOffset.QuadPart = start;
+    fzdi.BeyondFinalZero.QuadPart = start + size;
+
+    // Mark the range as sparse zero block
+    DWORD dwTemp;
+    DeviceIoControl(hSparseFile, FSCTL_SET_ZERO_DATA, &fzdi, sizeof(fzdi), NULL, 0, &dwTemp, NULL);
+}
+
+/*!
+ * Query the sparse file layout.
+ *
+ * \param lpFileName
+ * File name
+ */
+BOOL GetSparseRanges(LPCTSTR lpFileName)
+{
+    // Open the file for read
+    HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE) return FALSE;
+
+    LARGE_INTEGER liFileSize;
+    GetFileSizeEx(hFile, &liFileSize);
+
+    // Range to be examined (the whole file)
+    FILE_ALLOCATED_RANGE_BUFFER queryRange;
+    queryRange.FileOffset.QuadPart = 0;
+    queryRange.Length = liFileSize;
+
+    // Allocated areas info
+    FILE_ALLOCATED_RANGE_BUFFER allocRanges[1024];
+
+    DWORD nbytes;
+    BOOL fFinished;
+    _putws(L"\nAllocated ranges in the file:");
+    do
+    {
+        fFinished = DeviceIoControl(hFile,
+                                    FSCTL_QUERY_ALLOCATED_RANGES,
+                                    &queryRange,
+                                    sizeof(queryRange),
+                                    allocRanges,
+                                    sizeof(allocRanges),
+                                    &nbytes,
+                                    NULL);
+
+        if (!fFinished)
+        {
+            DWORD dwError = GetLastError();
+
+            // ERROR_MORE_DATA is the only error that is normal
+            if (dwError != ERROR_MORE_DATA)
+            {
+                wprintf(L"DeviceIoControl failed w/err 0x%08lx\n", dwError);
+                CloseHandle(hFile);
+                return FALSE;
+            }
+        }
+
+        // Calculate the number of records returned
+        DWORD dwAllocRangeCount = nbytes / sizeof(FILE_ALLOCATED_RANGE_BUFFER);
+
+        // Print each allocated range
+        for (DWORD i = 0; i < dwAllocRangeCount; i++)
+        {
+            wprintf(L"allocated range: [%I64u] [%I64u]\n",
+                    allocRanges[i].FileOffset.QuadPart,
+                    allocRanges[i].Length.QuadPart);
+        }
+
+        // Set starting address and size for the next query
+        if (!fFinished && dwAllocRangeCount > 0)
+        {
+            queryRange.FileOffset.QuadPart = allocRanges[dwAllocRangeCount - 1].FileOffset.QuadPart +
+                                             allocRanges[dwAllocRangeCount - 1].Length.QuadPart;
+
+            queryRange.Length.QuadPart = liFileSize.QuadPart - queryRange.FileOffset.QuadPart;
+        }
+
+    } while (!fFinished);
+
+    CloseHandle(hFile);
+    return TRUE;
+}
diff --git a/Release/tests/functional/streams/CppSparseFile.h b/Release/tests/functional/streams/CppSparseFile.h
new file mode 100644 (file)
index 0000000..306b44b
--- /dev/null
@@ -0,0 +1,82 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * CppSparseFile.h : defines various apis for creation and access of sparse files under windows
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma region Includes
+#include <assert.h>
+#include <stdio.h>
+#include <tchar.h>
+
+#include <windows.h>
+#pragma endregion
+
+/*!
+ * VolumeSupportsSparseFiles determines if the volume supports sparse streams.
+ *
+ * \param lpRootPathName
+ * Volume root path e.g. C:\
+ */
+BOOL VolumeSupportsSparseFiles(LPCTSTR lpRootPathName);
+
+/*!
+ * IsSparseFile determines if a file is sparse.
+ *
+ * \param lpFileName
+ * File name
+ */
+BOOL IsSparseFile(LPCTSTR lpFileName);
+
+/*!
+ * Get sparse file sizes.
+ *
+ * \param lpFileName
+ * File name
+ *
+ * \see
+ * http://msdn.microsoft.com/en-us/library/aa365276.aspx
+ */
+BOOL GetSparseFileSize(LPCTSTR lpFileName);
+
+/*!
+ * Create a sparse file.
+ *
+ * \param lpFileName
+ * The name of the sparse file
+ */
+HANDLE CreateSparseFile(LPCTSTR lpFileName);
+
+/*!
+ * Converting a file region to A sparse zero area.
+ *
+ * \param hSparseFile
+ * Handle of the sparse file
+ *
+ * \param start
+ * Start address of the sparse zero area
+ *
+ * \param size
+ * Size of the sparse zero block. The minimum sparse size is 64KB.
+ *
+ * \remarks
+ * Note that SetSparseRange does not perform actual file I/O, and unlike the
+ * WriteFile function, it does not move the current file I/O pointer or sets
+ * the end-of-file pointer. That is, if you want to place a sparse zero block
+ * in the end of the file, you must move the file pointer accordingly using
+ * the FileStream.Seek function, otherwise DeviceIoControl will have no effect
+ */
+void SetSparseRange(HANDLE hSparseFile, LONGLONG start, LONGLONG size);
+
+/*!
+ * Query the sparse file layout.
+ *
+ * \param lpFileName
+ * File name
+ */
+BOOL GetSparseRanges(LPCTSTR lpFileName);
diff --git a/Release/tests/functional/streams/fstreambuf_tests.cpp b/Release/tests/functional/streams/fstreambuf_tests.cpp
new file mode 100644 (file)
index 0000000..190eb66
--- /dev/null
@@ -0,0 +1,1065 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests for async file stream buffer operations.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#ifdef _WIN32
+#include "CppSparseFile.h"
+#endif
+
+#if defined(__cplusplus_winrt)
+using namespace Windows::Storage;
+#endif
+
+#ifdef _WIN32
+#define DEFAULT_PROT (int)std::ios_base::_Openprot
+#else
+#define DEFAULT_PROT 0
+#define _SH_DENYRW 0x20
+#endif
+
+namespace tests
+{
+namespace functional
+{
+namespace streams
+{
+using namespace utility;
+using namespace ::pplx;
+
+// Used to prepare data for read tests
+
+utility::string_t get_full_name(const utility::string_t& name);
+
+void fill_file(const utility::string_t& name, size_t repetitions = 1);
+#ifdef _WIN32
+void fill_file_w(const utility::string_t& name, size_t repetitions = 1);
+#endif
+
+//
+// The following two functions will help mask the differences between non-WinRT environments and
+// WinRT: on the latter, a file path is typically not used to open files. Rather, a UI element is used
+// to get a 'StorageFile' reference and you go from there. However, to test the library properly,
+// we need to get a StorageFile reference somehow, and one way to do that is to create all the files
+// used in testing in the Documents folder.
+//
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4100) // Because of '_Prot' in WinRT builds.
+#endif
+template<typename _CharType>
+pplx::task<concurrency::streams::streambuf<_CharType>> OPEN(const utility::string_t& name,
+                                                            std::ios::ios_base::openmode mode,
+                                                            int _Prot = DEFAULT_PROT)
+{
+#if !defined(__cplusplus_winrt)
+    return concurrency::streams::file_buffer<_CharType>::open(name, mode, _Prot);
+#else
+    try
+    {
+        if ((mode & std::ios::out))
+        {
+            auto file =
+                pplx::create_task(KnownFolders::DocumentsLibrary->CreateFileAsync(
+                                      ref new Platform::String(name.c_str()), CreationCollisionOption::ReplaceExisting))
+                    .get();
+
+            return concurrency::streams::file_buffer<_CharType>::open(file, mode);
+        }
+        else
+        {
+            auto file =
+                pplx::create_task(KnownFolders::DocumentsLibrary->GetFileAsync(ref new Platform::String(name.c_str())))
+                    .get();
+
+            return concurrency::streams::file_buffer<_CharType>::open(file, mode);
+        }
+    }
+    catch (Platform::Exception ^ exc)
+    {
+        // The create_system_error API expects a WIN32 error code NOT an HRESULT.
+        if (exc->HResult == 0x80070002)
+        {
+            throw utility::details::create_system_error(ERROR_FILE_NOT_FOUND);
+        }
+        else
+        {
+            // Some other unexpected error code was encountered, fail immediately.
+            // Throw statement is still included after because compiler warns about not
+            // all paths returning a value.
+            VERIFY_IS_TRUE(false);
+            throw utility::details::create_system_error(exc->HResult);
+        }
+    }
+#endif
+}
+
+template<typename _CharType>
+pplx::task<concurrency::streams::streambuf<_CharType>> OPEN_W(const utility::string_t& name, int _Prot = DEFAULT_PROT)
+{
+    return OPEN<_CharType>(name, std::ios_base::out | std::ios_base::trunc, _Prot);
+}
+
+template<typename _CharType>
+pplx::task<concurrency::streams::streambuf<_CharType>> OPEN_R(const utility::string_t& name, int _Prot = DEFAULT_PROT)
+{
+    return OPEN<_CharType>(name, std::ios_base::in, _Prot);
+}
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+SUITE(file_buffer_tests)
+{
+    TEST(OpenCloseTest1)
+    {
+        // Test using single-byte strings
+        auto open = OPEN_W<char>(U("OpenCloseTest1.txt"));
+
+        auto stream = open.get();
+
+        VERIFY_IS_TRUE(open.is_done());
+        VERIFY_IS_TRUE(stream.is_open());
+
+        auto close = stream.close();
+        close.get();
+
+        VERIFY_IS_TRUE(close.is_done());
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+    TEST(OpenForReadDoesntCreateFile1)
+    {
+        utility::string_t fname = U("OpenForReadDoesntCreateFile1.txt");
+
+        VERIFY_THROWS_SYSTEM_ERROR(OPEN_R<char>(fname).get(), std::errc::no_such_file_or_directory);
+
+        std::ifstream is;
+        VERIFY_IS_NULL(is.rdbuf()->open(fname.c_str(), std::ios::in));
+    }
+
+    TEST(OpenForReadDoesntCreateFile2)
+    {
+        utility::string_t fname = U("OpenForReadDoesntCreateFile2.txt");
+
+        VERIFY_THROWS_SYSTEM_ERROR(OPEN<char>(fname, std::ios_base::in | std::ios_base::binary).get(),
+                                   std::errc::no_such_file_or_directory);
+
+        std::ifstream is;
+        VERIFY_IS_NULL(is.rdbuf()->open(fname.c_str(), std::ios::in | std::ios_base::binary));
+    }
+
+    TEST(WriteSingleCharTest1)
+    {
+        auto open = OPEN_W<char>(U("WriteSingleCharTest1.txt"));
+        auto stream = open.get();
+
+        VERIFY_IS_TRUE(open.is_done());
+        VERIFY_IS_TRUE(stream.is_open());
+
+        bool elements_equal = true;
+        for (uint8_t ch = 'a'; ch <= 'z'; ch++)
+        {
+            elements_equal = elements_equal && (ch == stream.putc(ch).get());
+        }
+
+        VERIFY_IS_TRUE(elements_equal);
+
+        auto close = stream.close();
+        close.get();
+
+        VERIFY_IS_TRUE(close.is_done());
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+#ifdef _WIN32
+    TEST(WriteSingleCharTest1w)
+    {
+        auto open = OPEN_W<wchar_t>(U("WriteSingleCharTest1w.txt"));
+        auto stream = open.get();
+
+        VERIFY_IS_TRUE(open.is_done());
+        VERIFY_IS_TRUE(stream.is_open());
+
+        bool elements_equal = true;
+        for (wchar_t ch = L'a'; ch <= L'z'; ch++)
+        {
+            elements_equal = elements_equal && (ch == stream.putc(ch).get());
+        }
+
+        VERIFY_IS_TRUE(elements_equal);
+
+        auto close = stream.close();
+        close.get();
+
+        VERIFY_IS_TRUE(close.is_done());
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+#endif
+
+    TEST(WriteBufferTest1)
+    {
+        auto open = OPEN_W<char>(U("WriteBufferTest1.txt"));
+        auto stream = open.get();
+
+        VERIFY_IS_TRUE(open.is_done());
+        VERIFY_IS_TRUE(stream.is_open());
+
+        std::vector<char> vect;
+
+        for (uint8_t ch = 'a'; ch <= 'z'; ch++)
+        {
+            vect.push_back(ch);
+        }
+
+        VERIFY_ARE_EQUAL(stream.putn_nocopy(&vect[0], vect.size()).get(), vect.size());
+
+        auto close = stream.close();
+        close.get();
+
+        VERIFY_IS_TRUE(close.is_done());
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+#ifdef _WIN32
+    TEST(WriteBufferTest1w)
+    {
+        auto open = OPEN_W<wchar_t>(U("WriteBufferTest1w.txt"));
+        auto stream = open.get();
+
+        VERIFY_IS_TRUE(open.is_done());
+        VERIFY_IS_TRUE(stream.is_open());
+
+        std::vector<wchar_t> vect;
+
+        for (wchar_t ch = L'a'; ch <= L'z'; ch++)
+        {
+            vect.push_back(ch);
+        }
+
+        VERIFY_ARE_EQUAL(stream.putn_nocopy(&vect[0], vect.size()).get(), vect.size());
+
+        auto close = stream.close();
+        close.get();
+
+        VERIFY_IS_TRUE(close.is_done());
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+#endif
+
+    TEST(WriteBufferAndSyncTest1)
+    {
+        auto open = OPEN_W<char>(U("WriteBufferAndSyncTest1.txt"));
+        auto stream = open.get();
+
+        VERIFY_IS_TRUE(open.is_done());
+        VERIFY_IS_TRUE(stream.is_open());
+
+        std::vector<char> vect;
+
+        for (uint8_t ch = 'a'; ch <= 'z'; ch++)
+        {
+            vect.push_back(ch);
+        }
+
+        auto write = stream.putn_nocopy(&vect[0], vect.size());
+
+        stream.sync().get();
+
+        VERIFY_ARE_EQUAL(write.get(), vect.size());
+        VERIFY_IS_TRUE(write.is_done());
+
+        auto close = stream.close();
+        close.get();
+
+        VERIFY_IS_TRUE(close.is_done());
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+    TEST(ReadSingleChar_bumpc1)
+    {
+        utility::string_t fname = U("ReadSingleChar_bumpc1.txt");
+        fill_file(fname);
+
+        auto stream = OPEN_R<char>(fname).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        uint8_t buf[10];
+        memset(buf, 0, sizeof(buf));
+
+        for (int i = 0; i < sizeof(buf); i++)
+        {
+            buf[i] = (uint8_t)stream.bumpc().get();
+            VERIFY_ARE_EQUAL(buf[i], 'a' + i);
+        }
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+    TEST(SequentialReadWrite)
+    {
+        utility::string_t fname = U("SequentialReadWrite.txt");
+
+        auto ostreamBuf = OPEN_W<char>(fname).get();
+
+        VERIFY_IS_TRUE(ostreamBuf.is_open());
+
+        for (int i = 0; i < 1000; i++)
+        {
+            ostreamBuf.putc(i % 26 + 'a');
+            ostreamBuf.putn_nocopy("ABCDEFGHIJ", 10);
+        }
+        ostreamBuf.close().wait();
+        VERIFY_IS_FALSE(ostreamBuf.is_open());
+
+        auto istreamBuf = OPEN_R<char>(fname).get();
+        std::vector<pplx::task<void>> t;
+
+        for (int k = 0; k < 2; k++)
+        {
+            for (int i = 0; i < 1000; i++)
+            {
+                t.push_back(istreamBuf.getc().then([i, this](char c) { VERIFY_ARE_EQUAL(i % 26 + 'a', c); }));
+                t.push_back(istreamBuf.bumpc().then([i, this](char c) { VERIFY_ARE_EQUAL(i % 26 + 'a', c); }));
+                char* buffer = new char[11];
+                t.push_back(istreamBuf.getn(buffer, 10).then([=](size_t n) {
+                    VERIFY_ARE_EQUAL(10u, n);
+                    VERIFY_ARE_EQUAL(std::string("ABCDEFGHIJ"), std::string(buffer, 10));
+                    delete[] buffer;
+                }));
+            }
+            istreamBuf.seekpos(0, std::ios::in);
+        }
+        istreamBuf.close().wait();
+        VERIFY_IS_FALSE(istreamBuf.is_open());
+        for (size_t i = 0; i < t.size(); i++)
+            t[i].wait();
+    }
+
+#ifdef _WIN32
+    TEST(ReadSingleChar_bumpcw)
+    {
+        utility::string_t fname = U("ReadSingleChar_bumpcw.txt");
+        fill_file_w(fname);
+
+        auto stream = OPEN_R<wchar_t>(fname).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        wchar_t buf[10];
+        memset(buf, 0, sizeof(buf));
+
+        for (int i = 0; i < 10; i++)
+        {
+            buf[i] = stream.bumpc().get();
+            VERIFY_ARE_EQUAL(buf[i], L'a' + i);
+        }
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+#endif
+
+    TEST(ReadSingleChar_bumpc2)
+    {
+        // Test that seeking works.
+        utility::string_t fname = U("ReadSingleChar_bumpc2.txt");
+        fill_file(fname);
+
+        auto stream = OPEN_R<char>(fname).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        stream.seekpos(3, std::ios_base::in);
+
+        uint8_t buf[10];
+        memset(buf, 0, sizeof(buf));
+
+        for (int i = 0; i < sizeof(buf); i++)
+        {
+            buf[i] = (uint8_t)stream.bumpc().get();
+            VERIFY_ARE_EQUAL(buf[i], 'd' + i);
+        }
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+    TEST(filestream_length)
+    {
+        utility::string_t fname = U("ReadSingleChar_bumpc3.txt");
+        fill_file(fname);
+
+        auto stream = OPEN_R<char>(fname, _SH_DENYRW).get();
+        stream.set_buffer_size(512);
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        test_stream_length(stream.create_istream(), 26);
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+    TEST(ReadSingleChar_bumpc3)
+    {
+        // Test that seeking works.
+        utility::string_t fname = U("ReadSingleChar_bumpc3.txt");
+        fill_file(fname);
+
+        auto stream = OPEN_R<char>(fname, _SH_DENYRW).get();
+        stream.set_buffer_size(512);
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        stream.seekpos(2, std::ios_base::in);
+
+        // Read a character asynchronously to get the buffer primed.
+        stream.bumpc().get();
+
+        auto ras = concurrency::streams::char_traits<char>::requires_async();
+
+        for (int i = 3; i < 26; i++)
+        {
+            auto c = (uint8_t)stream.sbumpc();
+            if (c != ras)
+            {
+                VERIFY_ARE_EQUAL(c, 'a' + i);
+            }
+        }
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+    TEST(ReadSingleChar_nextc)
+    {
+        utility::string_t fname = U("ReadSingleChar_nextc.txt");
+        fill_file(fname);
+
+        auto stream = OPEN_R<char>(fname).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        uint8_t buf[10];
+        memset(buf, 0, sizeof(buf));
+
+        for (int i = 0; i < sizeof(buf); i++)
+        {
+            buf[i] = (uint8_t)stream.nextc().get();
+            VERIFY_ARE_EQUAL(buf[i], 'b' + i);
+        }
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+#ifdef _WIN32
+    TEST(ReadSingleChar_nextcw)
+    {
+        utility::string_t fname = U("ReadSingleChar_nextcw.txt");
+        fill_file_w(fname);
+
+        auto stream = OPEN_R<wchar_t>(fname).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        wchar_t buf[10];
+        memset(buf, 0, sizeof(buf));
+
+        for (int i = 0; i < 10; i++)
+        {
+            buf[i] = stream.nextc().get();
+            VERIFY_ARE_EQUAL(buf[i], L'b' + i);
+        }
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+#endif
+
+    TEST(ReadSingleChar_ungetc)
+    {
+        // Test that seeking works.
+        utility::string_t fname = U("ReadSingleChar_ungetc.txt");
+        fill_file(fname);
+
+        auto stream = OPEN_R<char>(fname).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        stream.seekpos(13, std::ios_base::in);
+
+        uint8_t buf[10];
+        memset(buf, 0, sizeof(buf));
+
+        for (int i = 0; i < sizeof(buf); i++)
+        {
+            buf[i] = (uint8_t)stream.ungetc().get();
+            VERIFY_ARE_EQUAL(buf[i], 'm' - i);
+        }
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+    TEST(ReadSingleChar_getc1)
+    {
+        utility::string_t fname = U("ReadSingleChar_getc1.txt");
+        fill_file(fname);
+
+        auto stream = OPEN_R<char>(fname, _SH_DENYRW).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        uint8_t ch0 = (uint8_t)stream.getc().get();
+        uint8_t ch1 = (uint8_t)stream.getc().get();
+
+        VERIFY_ARE_EQUAL(ch0, ch1);
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+    TEST(ReadSingleChar_getc2)
+    {
+        utility::string_t fname = U("ReadSingleChar_getc2.txt");
+        fill_file(fname);
+
+        auto stream = OPEN_R<char>(fname, _SH_DENYRW).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        stream.seekpos(13, std::ios_base::in);
+
+        uint8_t ch0 = (uint8_t)stream.getc().get();
+        uint8_t ch1 = (uint8_t)stream.sgetc();
+
+        VERIFY_ARE_EQUAL(ch0, ch1);
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+#ifdef _WIN32
+    TEST(ReadSingleChar_getc1w)
+    {
+        utility::string_t fname = U("ReadSingleChar_getc1w.txt");
+        fill_file_w(fname);
+
+        auto stream = OPEN_R<wchar_t>(fname, _SH_DENYRW).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        wchar_t ch0 = stream.getc().get();
+        wchar_t ch1 = stream.getc().get();
+
+        VERIFY_ARE_EQUAL(ch0, ch1);
+        VERIFY_ARE_EQUAL(ch0, L'a');
+
+        stream.seekpos(15, std::ios_base::in);
+
+        ch0 = stream.getc().get();
+        ch1 = stream.getc().get();
+
+        VERIFY_ARE_EQUAL(ch0, ch1);
+        VERIFY_ARE_EQUAL(ch0, L'p');
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+    TEST(ReadSingleChar_getc2w)
+    {
+        utility::string_t fname = U("ReadSingleChar_getc2w.txt");
+        fill_file_w(fname);
+
+        auto stream = OPEN_R<wchar_t>(fname, _SH_DENYRW).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        stream.seekpos(13, std::ios_base::in);
+
+        wchar_t ch0 = stream.getc().get();
+        wchar_t ch1 = stream.getc().get();
+
+        VERIFY_ARE_EQUAL(ch0, ch1);
+        VERIFY_ARE_EQUAL(ch0, L'n');
+
+        stream.seekpos(5, std::ios_base::in);
+
+        ch0 = stream.getc().get();
+        ch1 = stream.getc().get();
+
+        VERIFY_ARE_EQUAL(ch0, ch1);
+        VERIFY_ARE_EQUAL(ch0, L'f');
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+#endif
+
+    TEST(ReadBuffer1)
+    {
+        // Test that seeking works.
+        utility::string_t fname = U("ReadBuffer1.txt");
+        fill_file(fname);
+
+        // In order to get the implementation to buffer reads, we have to open the file
+        // with protection against sharing.
+        auto stream = OPEN_R<char>(fname, _SH_DENYRW).get();
+        stream.set_buffer_size(512);
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        char buf[10];
+        memset(buf, 0, sizeof(buf));
+
+        auto read = stream.getn(buf, sizeof(buf)).then([=](pplx::task<size_t> op) -> size_t { return op.get(); });
+
+        VERIFY_ARE_EQUAL(sizeof(buf), read.get());
+
+        bool elements_equal = true;
+
+        for (int i = 0; i < sizeof(buf); i++)
+        {
+            elements_equal = elements_equal && (buf[i] == 'a' + i);
+        }
+
+        VERIFY_IS_TRUE(elements_equal);
+
+        stream.seekpos(3, std::ios_base::in);
+
+        memset(buf, 0, sizeof(buf));
+
+        read = stream.getn(buf, sizeof(buf)).then([=](pplx::task<size_t> op) -> size_t { return op.get(); });
+
+        VERIFY_ARE_EQUAL(sizeof(buf), read.get());
+
+        elements_equal = true;
+
+        for (int i = 0; i < sizeof(buf); i++)
+        {
+            elements_equal = elements_equal && (buf[i] == 'd' + i);
+        }
+
+        VERIFY_IS_TRUE(elements_equal);
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+#ifdef _WIN32
+    TEST(ReadBuffer1w)
+    {
+        // Test that seeking works.
+        utility::string_t fname = U("ReadBuffer1w.txt");
+        fill_file_w(fname);
+
+        // In order to get the implementation to buffer reads, we have to open the file
+        // with protection against sharing.
+        auto stream = OPEN_R<wchar_t>(fname, _SH_DENYRW).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        wchar_t buf[10];
+        memset(buf, 0, sizeof(buf));
+
+        auto read = stream.getn(buf, 10).then([=](pplx::task<size_t> op) -> size_t { return op.get(); });
+
+        VERIFY_ARE_EQUAL(10u, read.get());
+
+        bool elements_equal = true;
+
+        for (int i = 0; i < 10; i++)
+        {
+            elements_equal = elements_equal && (buf[i] == L'a' + i);
+        }
+
+        VERIFY_IS_TRUE(elements_equal);
+
+        stream.seekpos(3, std::ios_base::in);
+
+        memset(buf, 0, sizeof(buf));
+
+        read = stream.getn(buf, 10).then([=](pplx::task<size_t> op) -> size_t { return op.get(); });
+
+        VERIFY_ARE_EQUAL(10u, read.get());
+
+        elements_equal = true;
+
+        for (int i = 0; i < 10; i++)
+        {
+            elements_equal = elements_equal && (buf[i] == L'd' + i);
+        }
+
+        VERIFY_IS_TRUE(elements_equal);
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+#endif
+
+    TEST(ReadBuffer2)
+    {
+        // Test that seeking works when the file is larger than the internal buffer size.
+        utility::string_t fname = U("ReadBuffer2.txt");
+        fill_file(fname, 30);
+
+        // In order to get the implementation to buffer reads, we have to open the file
+        // with protection against sharing.
+        auto stream = OPEN_R<char>(fname, _SH_DENYRW).get();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        char buf[10];
+        memset(buf, 0, sizeof(buf));
+
+        auto read = stream.getn(buf, sizeof(buf)).then([=](pplx::task<size_t> op) -> size_t { return op.get(); });
+
+        VERIFY_ARE_EQUAL(sizeof(buf), read.get());
+
+        bool elements_equal = true;
+
+        for (int i = 0; i < sizeof(buf); i++)
+        {
+            elements_equal = elements_equal && (buf[i] == 'a' + i);
+        }
+
+        VERIFY_IS_TRUE(elements_equal);
+
+        // Test that we can seek to a position near the end of the initial buffer,
+        // read a chunk spanning the end of the buffer, and get a correct outcome.
+
+        stream.seekpos(505, std::ios_base::in);
+
+        memset(buf, 0, sizeof(buf));
+
+        read = stream.getn(buf, sizeof(buf)).then([=](pplx::task<size_t> op) -> size_t { return op.get(); });
+
+        VERIFY_ARE_EQUAL(sizeof(buf), read.get());
+
+        elements_equal = true;
+
+        for (int i = 0; i < sizeof(buf); i++)
+        {
+            elements_equal = elements_equal && (buf[i] == 'l' + i);
+        }
+
+        VERIFY_IS_TRUE(elements_equal);
+
+        stream.close().get();
+
+        VERIFY_IS_FALSE(stream.is_open());
+    }
+
+    TEST(SeekEnd1)
+    {
+        utility::string_t fname = U("SeekEnd1.txt");
+        fill_file(fname, 30);
+
+        // In order to get the implementation to buffer reads, we have to open the file
+        // with protection against sharing.
+        auto stream = OPEN_R<char>(fname).get();
+
+        auto pos = stream.seekoff(0, std::ios_base::end, std::ios_base::in);
+
+        VERIFY_ARE_EQUAL(30 * 26, pos);
+    }
+
+    TEST(IsEOFTest)
+    {
+        utility::string_t fname = U("IsEOFTest.txt");
+        fill_file(fname, 30);
+
+        auto stream = OPEN_R<char>(fname).get();
+        VERIFY_IS_FALSE(stream.is_eof());
+        stream.getc().wait();
+        VERIFY_IS_FALSE(stream.is_eof());
+        stream.seekoff(0, std::ios_base::end, std::ios_base::in);
+        VERIFY_IS_FALSE(stream.is_eof());
+        stream.getc().wait();
+        VERIFY_IS_TRUE(stream.is_eof());
+        stream.seekoff(0, std::ios_base::beg, std::ios_base::in);
+        VERIFY_IS_TRUE(stream.is_eof());
+        stream.getc().wait();
+        VERIFY_IS_FALSE(stream.is_eof());
+    }
+
+    TEST(CloseWithException)
+    {
+        struct MyException
+        {
+        };
+        auto streambuf = OPEN_W<char>(U("CloseExceptionTest.txt")).get();
+        streambuf.close(std::ios::out, std::make_exception_ptr(MyException())).wait();
+        VERIFY_THROWS(streambuf.putn_nocopy("this is good", 10).get(), MyException);
+        VERIFY_THROWS(streambuf.putc('c').get(), MyException);
+
+        streambuf = OPEN_R<char>(U("CloseExceptionTest.txt")).get();
+        streambuf.close(std::ios::in, std::make_exception_ptr(MyException())).wait();
+        char buf[100];
+        VERIFY_THROWS(streambuf.getn(buf, 100).get(), MyException);
+        VERIFY_THROWS(streambuf.getc().get(), MyException);
+    }
+
+    TEST(inout_regression_test)
+    {
+        std::string data = "abcdefghijklmn";
+        concurrency::streams::streambuf<char> file_buf =
+            OPEN<char>(U("inout_regression_test.txt"), std::ios_base::in | std::ios_base::out).get();
+        file_buf.putn_nocopy(&data[0], data.size()).get();
+
+        file_buf.bumpc().get(); // reads 'a'
+
+        char readdata[256];
+        memset(&readdata[0], '\0', 256);
+
+        file_buf.seekoff(0, std::ios::beg, std::ios::in);
+        auto data_read = file_buf.getn(&readdata[0], 3).get(); // reads 'bcd'. File contains the org string though!!!
+
+        memset(&readdata[0], '\0', 256);
+
+        file_buf.seekoff(0, std::ios::beg, std::ios::in);
+        data_read = file_buf.getn(&readdata[0], 3).get(); // reads 'efg'. File contains org string 'abcdef..'.
+
+        file_buf.close().wait();
+    }
+
+    TEST(seek_read_regression_test)
+    {
+        utility::string_t fname = U("seek_read_regression_test.txt");
+        fill_file(fname, 100);
+
+        char readdata[256];
+
+        auto istream = OPEN_R<char>(fname, _SH_DENYRW).get().create_istream();
+        istream.streambuf().set_buffer_size(128);
+
+        {
+            istream.seek(50, std::ios_base::beg);
+            concurrency::streams::rawptr_buffer<char> block(readdata, sizeof(readdata));
+            istream.read(block, 50).get();
+        }
+
+        {
+            istream.seek(256, std::ios_base::beg);
+            concurrency::streams::rawptr_buffer<char> block(readdata, sizeof(readdata));
+            istream.read(block, 256).get();
+        }
+
+        istream.close().get();
+    }
+
+    TEST(file_size)
+    {
+        utility::string_t fname = U("file_size.txt");
+        fill_file(fname, 100);
+        auto istream = OPEN_R<char>(fname).get();
+        VERIFY_IS_TRUE(istream.has_size());
+        VERIFY_ARE_EQUAL(istream.size(), 2600);
+    }
+
+#ifdef _WIN32
+    TEST(file_size_w)
+    {
+        utility::string_t fname = U("file_size_w.txt");
+        fill_file_w(fname, 100);
+        auto istream = OPEN_R<wchar_t>(fname).get();
+        VERIFY_IS_TRUE(istream.has_size());
+        VERIFY_ARE_EQUAL(istream.size(), 2600);
+    }
+
+    TEST(file_with_one_byte_size)
+    {
+        // Create a file with one byte.
+        concurrency::streams::streambuf<char> file_buf = OPEN<char>(U("one_byte_file.txt"), std::ios_base::out).get();
+        file_buf.putc('a').wait();
+        file_buf.close().wait();
+
+        // Try to read from file with a 2 byte character.
+        concurrency::streams::basic_istream<wchar_t> inFile(OPEN<wchar_t>(U("one_byte_file.txt"), std::ios::in).get());
+        concurrency::streams::container_buffer<std::wstring> buffer;
+        VERIFY_ARE_EQUAL(inFile.read(buffer, 1).get(), 0);
+        VERIFY_IS_TRUE(inFile.is_eof());
+    }
+#endif
+
+#if defined(_WIN32) && (!defined(__cplusplus_winrt)) && defined(_WIN64)
+    // since casablanca does not use sparse file apis we're not doing the reverse test (write one byte at 4Gb and verify
+    // with std apis) because the file created would be too big
+    TEST(read_one_byte_at_4G)
+    {
+        // Create a file with one byte.
+        string_t filename = U("read_one_byte_at_4G.txt");
+        // create a sparse file with sparse file apis
+        auto handle = CreateSparseFile(filename.c_str());
+        VERIFY_ARE_NOT_EQUAL(handle, INVALID_HANDLE_VALUE);
+
+        // write 1 byte
+        auto data = 'a';
+
+        DWORD dwBytesWritten;
+        LARGE_INTEGER i;
+        i.QuadPart = 0x100000000;
+
+        SetFilePointerEx(handle, i /*4GB*/, NULL, FILE_END);
+        WriteFile(handle, &data, 1, &dwBytesWritten, NULL);
+
+        CloseHandle(handle);
+
+        // read the file with casablanca streams
+        concurrency::streams::streambuf<char> file_buf = OPEN<char>(filename, std::ios_base::in).get();
+        file_buf.seekoff(4 * 1024 * 1024 * 1024ll, ::std::ios_base::beg, ::std::ios_base::in);
+
+        int aCharacter = file_buf.getc().get();
+        file_buf.close().wait();
+
+        VERIFY_ARE_EQUAL(aCharacter, data);
+    }
+#endif
+
+#if !defined(_WIN32) && defined(__x86_64__)
+
+    struct TidyStream
+    {
+        string_t _fileName;
+        concurrency::streams::streambuf<char> _stream;
+
+        TidyStream(string_t filename)
+        {
+            _fileName = filename;
+            _stream = OPEN<char>(filename, std::ios_base::out | ::std::ios_base::in).get();
+        }
+
+        ~TidyStream()
+        {
+            _stream.close().wait();
+            std::remove(_fileName.c_str());
+        }
+    };
+
+    TEST(write_one_byte_at_4G)
+    {
+        // write using casablanca streams
+        concurrency::streams::streambuf<char>::off_type pos = 4 * 1024 * 1024 * 1024ll;
+
+        string_t filename = U("write_one_byte_at_4G.txt");
+        TidyStream file_buf(filename);
+        file_buf._stream.seekoff(pos, ::std::ios_base::beg, ::std::ios_base::out);
+        file_buf._stream.putc('a').wait();
+        file_buf._stream.sync().get();
+
+        // verify with std streams
+        std::fstream stream(get_full_name(filename), std::ios_base::in);
+        stream.seekg(pos);
+        char c;
+        stream >> c;
+        stream.close();
+        VERIFY_ARE_EQUAL(c, 'a');
+    }
+
+    TEST(read_one_byte_at_4G)
+    {
+        // write with std stream
+        concurrency::streams::streambuf<char>::off_type pos = 4 * 1024 * 1024 * 1024ll;
+        // Create a file with one byte.
+        string_t filename = U("read_one_byte_at_4G.txt");
+
+        std::fstream stream(get_full_name(filename), std::ios_base::out);
+        stream.seekg(pos);
+        stream << 'a';
+        stream.close();
+
+        // verify with casablanca streams
+        TidyStream file_buf(filename);
+        file_buf._stream.seekoff(pos, ::std::ios_base::beg, ::std::ios_base::in);
+        int aCharacter = file_buf._stream.getc().get();
+
+        VERIFY_ARE_EQUAL(aCharacter, 'a');
+    }
+
+#endif
+
+    TEST(alloc_acquire_not_supported)
+    {
+        concurrency::streams::streambuf<char> file_buf =
+            OPEN<char>(U("alloc_not_supported.txt"), std::ios::out | std::ios::in).get();
+        VERIFY_IS_TRUE(file_buf.alloc(1) == nullptr);
+        char* temp;
+        size_t size;
+        VERIFY_IS_FALSE(file_buf.acquire(temp, size));
+    }
+
+    TEST(read_alloc_acquire_not_supported)
+    {
+        auto file_buf1 = OPEN<char>(U("read_acquire_not_supported1.txt"), std::ios::out | std::ios::in).get();
+        auto file_buf2 = OPEN<char>(U("read_acquire_not_supported2.txt"), std::ios::out | std::ios::in).get();
+
+        concurrency::streams::stringstreambuf data_buf("A");
+        file_buf1.create_ostream().write(data_buf, 1).wait();
+        file_buf1.sync().wait();
+        file_buf2.create_ostream().write(file_buf1, 1).wait();
+        file_buf2.sync().wait();
+
+        file_buf2.create_istream().read(file_buf1, 1).wait();
+        file_buf1.sync().wait();
+        file_buf1.seekpos(0, std::ios::in);
+        data_buf = concurrency::streams::stringstreambuf();
+        file_buf1.create_istream().read(data_buf, 2).wait();
+        const auto& data = data_buf.collection();
+        VERIFY_ARE_EQUAL(data[0], 'A');
+        VERIFY_ARE_EQUAL(data[1], 'A');
+
+        file_buf1.close().wait();
+        file_buf2.close().wait();
+    }
+
+    TEST(winrt_filestream_close)
+    {
+        std::string str("test data");
+        auto t = OPEN_W<uint8_t>(U("file.txt")).then([this, str](concurrency::streams::ostream stream) {
+            concurrency::streams::container_buffer<std::string> rbuf(str);
+            concurrency::streams::istream is(rbuf);
+            size_t size = 0;
+            is.read(stream.streambuf(), 1).wait();
+            while (!is.is_eof())
+            {
+                is.read(stream.streambuf(), 1).wait();
+                size += 1;
+            }
+
+            return stream.flush().then([size, stream]() {
+                stream.close();
+                return size;
+            });
+        });
+
+        VERIFY_ARE_EQUAL(t.get(), str.length());
+    }
+} // SUITE(file_buffer_tests)
+
+} // namespace streams
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/streams/fuzz_tests.cpp b/Release/tests/functional/streams/fuzz_tests.cpp
new file mode 100644 (file)
index 0000000..7b10e12
--- /dev/null
@@ -0,0 +1,116 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Fuzzing tests for streams read operations that involve parsing of data.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+using namespace concurrency::streams;
+
+namespace tests
+{
+namespace functional
+{
+namespace streams
+{
+using namespace utility;
+using namespace ::pplx;
+
+SUITE(streams_fuzz_tests)
+{
+    std::string get_fuzzed_file_path(std::string requires_str)
+    {
+        std::string ipfile;
+
+        if (UnitTest::GlobalSettings::Has(requires_str))
+        {
+            ipfile = UnitTest::GlobalSettings::Get(requires_str);
+        }
+
+        return ipfile;
+    }
+
+    concurrency::streams::basic_istream<char> get_input_stream(std::string requires_str)
+    {
+        utility::string_t ipfile = utility::conversions::to_string_t(get_fuzzed_file_path(requires_str));
+        concurrency::streams::basic_istream<char> ifs;
+
+        if (true == ipfile.empty())
+        {
+            VERIFY_IS_TRUE(false, "Input file is empty");
+            return ifs;
+        }
+
+        ifs = concurrency::streams::file_stream<char>::open_istream(ipfile, std::ios::in).get();
+
+        // Look for UTF-8 BOM
+        if (ifs.read().get() != 0xEF || ifs.read().get() != 0xBB || ifs.read().get() != 0xBF)
+        {
+            VERIFY_IS_TRUE(false, "Input file encoding is not UTF-8. Test will not parse the file.");
+            ifs.close().get();
+        }
+        return ifs;
+    }
+
+    TEST(fuzz_read_line, "Requires", "fuzz_read_line_ipfile", "Timeout", "600000")
+    {
+        auto ifs = get_input_stream("fuzz_read_line_ipfile");
+        if (!ifs.is_valid() || !ifs.is_open()) return;
+
+        size_t num_lines = 0;
+        while (false == ifs.is_eof())
+        {
+            container_buffer<std::vector<uint8_t>> buf;
+            ifs.read_line(buf).get();
+            num_lines++;
+        }
+        ifs.close().get();
+        std::wcout << U("Number of lines read:") << num_lines;
+    }
+
+    template<class T>
+    void extract(const basic_istream<char>& ifs)
+    {
+        try
+        {
+            ifs.extract<T>().get();
+        }
+        catch (std::exception)
+        {
+        }
+        return;
+    }
+
+    TEST(fuzz_extract, "Requires", "fuzz_extract_ipfile", "Timeout", "600000")
+    {
+        auto ifs = get_input_stream("fuzz_extract_ipfile");
+        if (!ifs.is_valid() || !ifs.is_open()) return;
+
+        int num_lines = 0;
+        while (false == ifs.is_eof())
+        {
+            extract<std::string>(ifs);
+            extract<std::string>(ifs);
+            extract<unsigned int>(ifs);
+            extract<uint64_t>(ifs);
+            extract<bool>(ifs);
+            extract<std::string>(ifs);
+            extract<int>(ifs);
+            container_buffer<std::vector<uint8_t>> buf;
+            ifs.read_line(buf).get();
+            num_lines++;
+        }
+        ifs.close().get();
+        std::wcout << L"Number of lines read:" << num_lines << std::endl;
+    }
+
+} // SUITE(streams_fuzz_tests)
+
+} // namespace streams
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/streams/istream_tests.cpp b/Release/tests/functional/streams/istream_tests.cpp
new file mode 100644 (file)
index 0000000..32cb545
--- /dev/null
@@ -0,0 +1,1574 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests for async input stream operations.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "unittestpp.h"
+#include <float.h>
+
+#ifdef max
+#undef max
+#endif
+
+#if defined(__cplusplus_winrt)
+using namespace Windows::Storage;
+#endif
+
+#ifdef _WIN32
+#define DEFAULT_PROT (int)std::ios_base::_Openprot
+#else
+#define DEFAULT_PROT 0
+#endif
+
+namespace tests
+{
+namespace functional
+{
+namespace streams
+{
+using namespace ::pplx;
+using namespace utility;
+using namespace concurrency::streams;
+
+// Used to prepare data for file-stream read tests
+
+utility::string_t get_full_name(const utility::string_t& name)
+{
+#if defined(__cplusplus_winrt)
+    // On WinRT, we must compensate for the fact that we will be accessing files in the
+    // Documents folder
+    auto file = pplx::create_task(KnownFolders::DocumentsLibrary->CreateFileAsync(
+                                      ref new Platform::String(name.c_str()), CreationCollisionOption::ReplaceExisting))
+                    .get();
+    return file->Path->Data();
+#else
+    return name;
+#endif
+}
+
+void fill_file(const utility::string_t& name, size_t repetitions = 1)
+{
+    std::fstream stream(get_full_name(name), std::ios_base::out | std::ios_base::trunc);
+
+    for (size_t i = 0; i < repetitions; i++)
+        stream << "abcdefghijklmnopqrstuvwxyz";
+}
+
+void fill_file_with_lines(const utility::string_t& name, const std::string& end, size_t repetitions = 1)
+{
+    std::fstream stream(get_full_name(name), std::ios_base::out | std::ios_base::trunc | std::ios_base::binary);
+
+    for (size_t i = 0; i < repetitions; i++)
+        stream << "abcdefghijklmnopqrstuvwxyz" << end;
+}
+
+#ifdef _WIN32
+
+// Disabling warning in test because we check for nullptr.
+#pragma warning(push)
+#pragma warning(disable : 6387)
+void fill_file_w(const utility::string_t& name, size_t repetitions = 1)
+{
+    FILE* stream = nullptr;
+    _wfopen_s(&stream, get_full_name(name).c_str(), L"w");
+    if (stream == nullptr)
+    {
+        VERIFY_IS_TRUE(false, "FILE pointer is null");
+    }
+
+    for (size_t i = 0; i < repetitions; i++)
+        for (wchar_t ch = L'a'; ch <= L'z'; ++ch)
+            fwrite(&ch, sizeof(wchar_t), 1, stream);
+
+    fclose(stream);
+}
+#pragma warning(pop)
+
+#endif
+
+//
+// The following functions will help mask the differences between non-WinRT environments and
+// WinRT: on the latter, a file path is typically not used to open files. Rather, a UI element is used
+// to get a 'StorageFile' reference and you go from there. However, to test the library properly,
+// we need to get a StorageFile reference somehow, and one way to do that is to create all the files
+// used in testing in the Documents folder.
+//
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4100) // Because of '_Prot' in WinRT builds.
+#endif
+template<typename _CharType>
+pplx::task<streams::streambuf<_CharType>> OPEN_R(const utility::string_t& name, int _Prot = DEFAULT_PROT)
+{
+#if !defined(__cplusplus_winrt)
+    return streams::file_buffer<_CharType>::open(name, std::ios_base::in, _Prot);
+#else
+    auto file =
+        pplx::create_task(KnownFolders::DocumentsLibrary->GetFileAsync(ref new Platform::String(name.c_str()))).get();
+
+    return streams::file_buffer<_CharType>::open(file, std::ios_base::in);
+#endif
+}
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+SUITE(istream_tests)
+{
+    // Tests using memory stream buffers.
+    TEST(stream_read_1)
+    {
+        producer_consumer_buffer<char> rbuf;
+
+        VERIFY_ARE_EQUAL(26u, rbuf.putn_nocopy("abcdefghijklmnopqrstuvwxyz", 26).get());
+
+        istream stream(rbuf);
+
+        VERIFY_IS_FALSE(stream.can_seek());
+
+        for (char c = 'a'; c <= 'z'; c++)
+        {
+            char ch = (char)stream.read().get();
+            VERIFY_ARE_EQUAL(c, ch);
+        }
+
+        stream.close().get();
+    }
+
+    TEST(fstream_read_1)
+    {
+        utility::string_t fname = U("fstream_read_1.txt");
+        fill_file(fname);
+
+        streams::basic_istream<char> stream = OPEN_R<char>(fname).get().create_istream();
+
+        VERIFY_IS_TRUE(stream.is_open());
+
+        for (char c = 'a'; c <= 'z'; c++)
+        {
+            char ch = (char)stream.read().get();
+            VERIFY_ARE_EQUAL(c, ch);
+        }
+
+        stream.close().get();
+    }
+
+    TEST(stream_read_1_fail)
+    {
+        producer_consumer_buffer<char> rbuf;
+
+        VERIFY_ARE_EQUAL(26u, rbuf.putn_nocopy("abcdefghijklmnopqrstuvwxyz", 26).get());
+
+        istream stream(rbuf);
+        rbuf.close(std::ios_base::in).get();
+
+        VERIFY_THROWS(stream.read().get(), std::runtime_error);
+        // Closing again should not throw.
+        stream.close().wait();
+    }
+
+    TEST(stream_read_2)
+    {
+        producer_consumer_buffer<char> rbuf;
+
+        VERIFY_ARE_EQUAL(26u, rbuf.putn_nocopy("abcdefghijklmnopqrstuvwxyz", 26).get());
+
+        istream stream(rbuf);
+
+        uint8_t buffer[128];
+        streams::rawptr_buffer<uint8_t> tbuf(buffer, 128);
+
+        VERIFY_ARE_EQUAL(26u, stream.read(tbuf, 26).get());
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        rbuf.close(std::ios_base::out).get();
+
+        VERIFY_ARE_EQUAL(0u, stream.read(tbuf, 26).get());
+
+        stream.close().get();
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(fstream_read_2)
+    {
+        utility::string_t fname = U("fstream_read_2.txt");
+        fill_file(fname);
+
+        streams::basic_istream<char> stream = OPEN_R<char>(fname).get().create_istream();
+
+        char buffer[128];
+        streams::rawptr_buffer<char> tbuf(buffer, 128);
+
+        VERIFY_ARE_EQUAL(26u, stream.read(tbuf, 26).get());
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        VERIFY_ARE_EQUAL(0u, stream.read(tbuf, 26).get());
+
+        stream.close().get();
+    }
+
+    TEST(stream_read_3)
+    {
+        producer_consumer_buffer<char> rbuf;
+
+        // There's no newline int the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+        rbuf.close(std::ios_base::out).get();
+
+        istream stream(rbuf);
+
+        uint8_t buffer[128];
+        streams::rawptr_buffer<uint8_t> tbuf(buffer, 128);
+
+        VERIFY_ARE_EQUAL(52u, stream.read(tbuf, sizeof(buffer)).get());
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'A', buffer[i + 26]);
+        }
+
+        stream.close().get();
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(stream_read_3_fail)
+    {
+        producer_consumer_buffer<char> rbuf;
+
+        // There's no newline int the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+        rbuf.close(std::ios_base::out).get();
+
+        istream stream(rbuf);
+
+        uint8_t buffer[128];
+        streams::rawptr_buffer<uint8_t> tbuf(buffer, 128);
+        tbuf.close(std::ios_base::out).get();
+
+        VERIFY_THROWS(stream.read(tbuf, sizeof(buffer)).get(), std::runtime_error);
+
+        stream.close().get();
+    }
+
+    TEST(stream_read_4)
+    {
+        producer_consumer_buffer<char> rbuf;
+        producer_consumer_buffer<uint8_t> trg;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> stream = rbuf;
+
+        VERIFY_ARE_EQUAL(52u, stream.read_to_delim(trg, '\n').get());
+
+        uint8_t buffer[128];
+        VERIFY_ARE_EQUAL(52u, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'A', buffer[i + 26]);
+        }
+
+        stream.close().get();
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(fstream_read_4)
+    {
+        producer_consumer_buffer<uint8_t> trg;
+
+        utility::string_t fname = U("fstream_read_4.txt");
+        fill_file(fname, 2);
+
+        streams::basic_istream<char> stream = OPEN_R<char>(fname).get().create_istream();
+
+        VERIFY_ARE_EQUAL(52u, stream.read_to_delim(trg, '\n').get());
+
+        uint8_t buffer[128];
+        VERIFY_ARE_EQUAL(52u, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i + 26]);
+        }
+
+        stream.close().get();
+    }
+
+    TEST(stream_read_4_fail)
+    {
+        producer_consumer_buffer<char> rbuf;
+        producer_consumer_buffer<uint8_t> trg;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> stream = rbuf;
+
+        trg.close(std::ios::out).get();
+
+        VERIFY_THROWS(stream.read_to_delim(trg, '\n').get(), std::runtime_error);
+
+        stream.close().get();
+    }
+
+    TEST(stream_read_5)
+    {
+        producer_consumer_buffer<char> rbuf;
+        producer_consumer_buffer<uint8_t> trg;
+
+        // There's one newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz\n\nABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+
+        istream stream(rbuf);
+
+        VERIFY_IS_FALSE(stream.is_eof());
+        VERIFY_ARE_EQUAL(26u, stream.read_to_delim(trg, '\n').get());
+        VERIFY_IS_FALSE(stream.is_eof());
+        VERIFY_ARE_EQUAL(0u, stream.read_to_delim(trg, '\n').get());
+        VERIFY_IS_FALSE(stream.is_eof());
+        VERIFY_ARE_EQUAL('A', (char)rbuf.getc().get());
+
+        uint8_t buffer[128];
+        VERIFY_ARE_EQUAL(26u, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        stream.close().get();
+    }
+
+    TEST(fstream_read_5)
+    {
+        producer_consumer_buffer<uint8_t> trg;
+
+        utility::string_t fname = U("fstream_read_5.txt");
+        fill_file_with_lines(fname, "\n", 2);
+
+        streams::basic_istream<char> stream = OPEN_R<char>(fname).get().create_istream();
+
+        VERIFY_ARE_EQUAL(26u, stream.read_to_delim(trg, '\n').get());
+        VERIFY_ARE_EQUAL('a', (char)stream.read().get());
+
+        uint8_t buffer[128];
+        VERIFY_ARE_EQUAL(26u, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        stream.close().get();
+    }
+
+    TEST(stream_readline_1)
+    {
+        producer_consumer_buffer<char> rbuf;
+        producer_consumer_buffer<uint8_t> trg;
+
+        // There's one newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+
+        istream stream(rbuf);
+
+        VERIFY_ARE_EQUAL(26u, stream.read_line(trg).get());
+        VERIFY_ARE_EQUAL('A', (char)rbuf.getc().get());
+
+        uint8_t buffer[128];
+        VERIFY_ARE_EQUAL(26u, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        stream.close().get();
+    }
+
+    TEST(stream_readline_1_fail)
+    {
+        producer_consumer_buffer<char> rbuf;
+        producer_consumer_buffer<uint8_t> trg;
+
+        // There's one newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+
+        istream stream(rbuf);
+
+        trg.close(std::ios_base::out).get();
+
+        VERIFY_THROWS(stream.read_line(trg).get(), std::runtime_error);
+
+        stream.close().get();
+    }
+
+    TEST(stream_readline_2)
+    {
+        producer_consumer_buffer<char> rbuf;
+        producer_consumer_buffer<uint8_t> trg;
+
+        // There's one newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz\r\n\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+
+        istream stream(rbuf);
+
+        VERIFY_IS_FALSE(stream.is_eof());
+        VERIFY_ARE_EQUAL(26u, stream.read_line(trg).get());
+        VERIFY_IS_FALSE(stream.is_eof());
+        VERIFY_ARE_EQUAL(0u, stream.read_line(trg).get());
+        VERIFY_IS_FALSE(stream.is_eof());
+        VERIFY_ARE_EQUAL('A', (char)rbuf.getc().get());
+
+        uint8_t buffer[128];
+        VERIFY_ARE_EQUAL(26u, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        stream.close().get();
+    }
+
+    TEST(fstream_readline_1)
+    {
+        producer_consumer_buffer<uint8_t> trg;
+
+        utility::string_t fname = U("fstream_readline_1.txt");
+        fill_file_with_lines(fname, "\n", 2);
+
+        streams::basic_istream<char> stream = OPEN_R<char>(fname).get().create_istream();
+
+        VERIFY_ARE_EQUAL(26u, stream.read_line(trg).get());
+        VERIFY_ARE_EQUAL('a', (char)stream.read().get());
+
+        uint8_t buffer[128];
+        VERIFY_ARE_EQUAL(26u, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        stream.close().get();
+    }
+
+    TEST(fstream_readline_2)
+    {
+        producer_consumer_buffer<uint8_t> trg;
+
+        utility::string_t fname = U("fstream_readline_2.txt");
+        fill_file_with_lines(fname, "\r\n", 2);
+
+        streams::basic_istream<char> stream = OPEN_R<char>(fname).get().create_istream();
+
+        VERIFY_ARE_EQUAL(26u, stream.read_line(trg).get());
+        VERIFY_ARE_EQUAL('a', (char)stream.read().get());
+
+        uint8_t buffer[128];
+        VERIFY_ARE_EQUAL(26u, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        stream.close().get();
+    }
+
+    TEST(stream_read_6)
+    {
+        producer_consumer_buffer<char> rbuf;
+        producer_consumer_buffer<uint8_t> trg;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+        rbuf.close(std::ios_base::out).get();
+
+        istream stream(rbuf);
+
+        VERIFY_ARE_EQUAL(52u, stream.read_to_delim(trg, '|').get());
+
+        uint8_t buffer[128];
+        VERIFY_ARE_EQUAL(52u, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'A', buffer[i + 26]);
+        }
+
+        stream.close().get();
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(stream_read_7)
+    {
+        producer_consumer_buffer<char> rbuf;
+        producer_consumer_buffer<uint8_t> trg;
+
+        // There's one delimiter in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz|ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+
+        istream stream(rbuf);
+
+        VERIFY_ARE_EQUAL(26u, stream.read_to_delim(trg, '|').get());
+        VERIFY_ARE_EQUAL('A', (char)rbuf.getc().get());
+
+        uint8_t buffer[128];
+        VERIFY_ARE_EQUAL(26u, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        stream.close().get();
+    }
+
+    TEST(stream_read_to_end_1)
+    {
+        // Create a really large (200KB) stream and read into a stream buffer.
+        // It should not take a long time to do this test.
+
+        producer_consumer_buffer<char> rbuf;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        for (int i = 0; i < 4096; ++i)
+        {
+            VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+        }
+
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> stream = rbuf;
+
+        streams::stringstreambuf sbuf;
+        auto& target = sbuf.collection();
+
+        VERIFY_ARE_EQUAL(len * 4096, stream.read_to_end(sbuf).get());
+        VERIFY_ARE_EQUAL(len * 4096, target.size());
+
+        stream.close().get();
+        sbuf.close().get();
+    }
+
+    TEST(stream_read_to_end_1_fail)
+    {
+        producer_consumer_buffer<char> rbuf;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        for (int i = 0; i < 4096; ++i)
+        {
+            VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+        }
+
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> stream = rbuf;
+
+        streams::stringstreambuf sbuf;
+        sbuf.close(std::ios_base::out).get();
+
+        VERIFY_THROWS(stream.read_to_end(sbuf).get(), std::runtime_error);
+
+        stream.close().get();
+        // This should not throw
+        sbuf.close().wait();
+    }
+
+    TEST(fstream_read_to_end_1)
+    {
+        // Create a really large (100KB) stream and read into a stream buffer.
+        // It should not take a long time to do this test.
+
+        utility::string_t fname = U("fstream_read_to_end_1.txt");
+        fill_file(fname, 4096);
+
+        streams::basic_istream<char> stream = OPEN_R<char>(fname).get().create_istream();
+
+        streams::stringstreambuf sbuf;
+        VERIFY_IS_FALSE(stream.is_eof());
+        auto& target = sbuf.collection();
+
+        VERIFY_ARE_EQUAL(26 * 4096, stream.read_to_end(sbuf).get());
+        VERIFY_ARE_EQUAL(26 * 4096, target.size());
+        VERIFY_IS_TRUE(stream.is_eof());
+
+        stream.close().get();
+        sbuf.close().get();
+    }
+
+    TEST(fstream_read_to_end_2)
+    {
+        // Read a file to end with is_eof tests.
+        utility::string_t fname = U("fstream_read_to_end_2.txt");
+        fill_file(fname);
+
+        streams::basic_istream<char> stream = OPEN_R<char>(fname).get().create_istream();
+
+        streams::stringstreambuf sbuf;
+        int c;
+        while (c = stream.read().get(), !stream.is_eof())
+            sbuf.putc(static_cast<char>(c)).get();
+        auto& target = sbuf.collection();
+        VERIFY_ARE_EQUAL(26, target.size());
+        VERIFY_IS_TRUE(stream.is_eof());
+
+        stream.close().get();
+        sbuf.close().get();
+    }
+
+    TEST(fstream_read_to_end_3)
+    {
+        // Async Read a file to end with is_eof tests.
+        utility::string_t fname = U("fstream_read_to_end_3.txt");
+        fill_file(fname, 1);
+
+        streams::basic_istream<char> stream = OPEN_R<char>(fname).get().create_istream();
+
+        streams::stringstreambuf sbuf;
+        // workaround VC10 's bug.
+        auto lambda2 = [](int) { return true; };
+        auto lambda1 = [sbuf, stream, lambda2](int val) mutable -> pplx::task<bool> {
+            if (!stream.is_eof())
+                return sbuf.putc(static_cast<char>(val)).then(lambda2);
+            else
+                return pplx::task_from_result(false);
+        };
+        pplx::details::_do_while([=]() -> pplx::task<bool> { return stream.read().then(lambda1); }).wait();
+
+        auto& target = sbuf.collection();
+        VERIFY_ARE_EQUAL(26, target.size());
+        VERIFY_IS_TRUE(stream.is_eof());
+
+        stream.close().get();
+        sbuf.close().get();
+    }
+
+    TEST(stream_read_to_delim_flush)
+    {
+        producer_consumer_buffer<char> rbuf;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz|ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> stream = rbuf;
+
+        producer_consumer_buffer<char> sbuf;
+
+        char chars[128];
+
+        VERIFY_ARE_EQUAL(26u, stream.read_to_delim(sbuf, '|').get());
+        // The read_to_delim() should have flushed, so we should be getting what's there,
+        // less than we asked for.
+        VERIFY_ARE_EQUAL(26u, sbuf.getn(chars, 100).get());
+
+        stream.close().get();
+        sbuf.close().get();
+    }
+
+    TEST(stream_read_line_flush)
+    {
+        producer_consumer_buffer<char> rbuf;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> stream = rbuf;
+
+        producer_consumer_buffer<char> sbuf;
+
+        char chars[128];
+
+        VERIFY_ARE_EQUAL(26u, stream.read_line(sbuf).get());
+        // The read_line() should have flushed, so we should be getting what's there,
+        // less than we asked for.
+        VERIFY_ARE_EQUAL(26u, sbuf.getn(chars, 100).get());
+
+        stream.close().get();
+        sbuf.close().get();
+    }
+
+    TEST(stream_read_to_end_flush)
+    {
+        producer_consumer_buffer<char> rbuf;
+        streams::basic_istream<char> stream = rbuf;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        size_t len = strlen(text);
+
+        VERIFY_ARE_EQUAL(len, rbuf.putn_nocopy(text, len).get());
+
+        rbuf.close(std::ios_base::out).get();
+
+        producer_consumer_buffer<char> sbuf;
+
+        char chars[128];
+
+        VERIFY_ARE_EQUAL(len, stream.read_to_end(sbuf).get());
+        // The read_to_end() should have flushed, so we should be getting what's there,
+        // less than we asked for.
+        VERIFY_ARE_EQUAL(len, sbuf.getn(chars, len * 2).get());
+
+        stream.close().get();
+        sbuf.close().get();
+    }
+
+    TEST(istream_extract_string)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = " abc defgsf ";
+
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> is = rbuf;
+        std::string str1 = is.extract<std::string>().get();
+        std::string str2 = is.extract<std::string>().get();
+
+        VERIFY_ARE_EQUAL(str1, "abc");
+        VERIFY_ARE_EQUAL(str2, "defgsf");
+    }
+#ifdef _WIN32 // On Linux, this becomes the exact copy of istream_extract_string1, hence disabled
+    TEST(istream_extract_wstring_1)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = " abc defgsf ";
+
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> is = rbuf;
+        utility::string_t str1 = is.extract<utility::string_t>().get();
+        utility::string_t str2 = is.extract<utility::string_t>().get();
+
+        VERIFY_ARE_EQUAL(str1, L"abc");
+        VERIFY_ARE_EQUAL(str2, L"defgsf");
+    }
+
+    TEST(istream_extract_wstring_2) // On Linux, this becomes the exact copy of istream_extract_string2, hence disabled
+    {
+        producer_consumer_buffer<signed char> rbuf;
+        const char* text = " abc defgsf ";
+
+        size_t len = strlen(text);
+        rbuf.putn_nocopy((const signed char*)text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> is = rbuf;
+        utility::string_t str1 = is.extract<utility::string_t>().get();
+        utility::string_t str2 = is.extract<utility::string_t>().get();
+
+        VERIFY_ARE_EQUAL(str1, L"abc");
+        VERIFY_ARE_EQUAL(str2, L"defgsf");
+    }
+
+    TEST(istream_extract_wstring_3)
+    {
+        producer_consumer_buffer<unsigned char> rbuf;
+        const char* text = " abc defgsf ";
+
+        size_t len = strlen(text);
+        rbuf.putn_nocopy((const unsigned char*)text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> is = rbuf;
+        utility::string_t str1 = is.extract<utility::string_t>().get();
+        utility::string_t str2 = is.extract<utility::string_t>().get();
+
+        VERIFY_ARE_EQUAL(str1, L"abc");
+        VERIFY_ARE_EQUAL(str2, L"defgsf");
+    }
+
+#endif
+
+    TEST(istream_extract_int64)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = "1024 -17134711";
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<char> is = rbuf;
+        int64_t i1 = is.extract<int64_t>().get();
+        int64_t i2 = is.extract<int64_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024);
+        VERIFY_ARE_EQUAL(i2, -17134711);
+    }
+
+    TEST(istream_extract_uint64)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = "1024 12000000000";
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::istream is(rbuf);
+        uint64_t i1 = is.extract<uint64_t>().get();
+        uint64_t i2 = is.extract<uint64_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024);
+        VERIFY_ARE_EQUAL(i2, (uint64_t)12000000000);
+    }
+#ifdef _WIN32
+    TEST(istream_extract_int64w)
+    {
+        producer_consumer_buffer<wchar_t> rbuf;
+        const wchar_t* text = L"1024 -17134711";
+        size_t len = wcslen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::wistream is(rbuf);
+        int64_t i1 = is.extract<int64_t>().get();
+        int64_t i2 = is.extract<int64_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024);
+        VERIFY_ARE_EQUAL(i2, -17134711);
+    }
+
+    TEST(istream_extract_uint64w)
+    {
+        producer_consumer_buffer<wchar_t> rbuf;
+        const wchar_t* text = L"1024 12000000000";
+        size_t len = wcslen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::wistream is(rbuf);
+        uint64_t i1 = is.extract<uint64_t>().get();
+        uint64_t i2 = is.extract<uint64_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024);
+        VERIFY_ARE_EQUAL(i2, (uint64_t)12000000000);
+    }
+#endif
+
+    TEST(istream_extract_int32)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = "1024 -17134711 12000000000";
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::istream is(rbuf);
+        int32_t i1 = is.extract<int32_t>().get();
+        int32_t i2 = is.extract<int32_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024);
+        VERIFY_ARE_EQUAL(i2, -17134711);
+        VERIFY_THROWS(is.extract<int32_t>().get(), std::range_error);
+    }
+
+    TEST(istream_extract_uint32)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = "1024 3000000000 12000000000";
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::istream is(rbuf);
+        uint32_t i1 = is.extract<uint32_t>().get();
+        uint32_t i2 = is.extract<uint32_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024u);
+        VERIFY_ARE_EQUAL(i2, (uint32_t)3000000000);
+        VERIFY_THROWS(is.extract<uint32_t>().get(), std::range_error);
+    }
+#ifdef _WIN32
+    TEST(istream_extract_int32w)
+    {
+        producer_consumer_buffer<wchar_t> rbuf;
+        const wchar_t* text = L"1024 -17134711 12000000000";
+        size_t len = wcslen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::wistream is(rbuf);
+        int32_t i1 = is.extract<int32_t>().get();
+        int32_t i2 = is.extract<int32_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024);
+        VERIFY_ARE_EQUAL(i2, -17134711);
+        VERIFY_THROWS(is.extract<int32_t>().get(), std::range_error);
+    }
+
+    TEST(istream_extract_uint32w)
+    {
+        producer_consumer_buffer<wchar_t> rbuf;
+        const wchar_t* text = L"1024 3000000000 12000000000";
+        size_t len = wcslen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::wistream is(rbuf);
+        uint32_t i1 = is.extract<uint32_t>().get();
+        uint32_t i2 = is.extract<uint32_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024u);
+        VERIFY_ARE_EQUAL(i2, 3000000000u);
+        VERIFY_THROWS(is.extract<uint32_t>().get(), std::range_error);
+    }
+#endif
+
+    TEST(istream_extract_int16)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = "1024 -4711 100000";
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::istream is(rbuf);
+        int16_t i1 = is.extract<int16_t>().get();
+        int16_t i2 = is.extract<int16_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024);
+        VERIFY_ARE_EQUAL(i2, -4711);
+        VERIFY_THROWS(is.extract<int16_t>().get(), std::range_error);
+    }
+
+    TEST(istream_extract_uint16)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = "1024 50000 100000";
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::istream is(rbuf);
+        uint16_t i1 = is.extract<uint16_t>().get();
+        uint16_t i2 = is.extract<uint16_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024);
+        VERIFY_ARE_EQUAL(i2, 50000);
+        VERIFY_THROWS(is.extract<uint16_t>().get(), std::range_error);
+    }
+
+#ifdef _WIN32
+    TEST(istream_extract_int16w)
+    {
+        producer_consumer_buffer<wchar_t> rbuf;
+        const wchar_t* text = L"1024 -4711 100000";
+        size_t len = wcslen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::wistream is(rbuf);
+        int16_t i1 = is.extract<int16_t>().get();
+        int16_t i2 = is.extract<int16_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024);
+        VERIFY_ARE_EQUAL(i2, -4711);
+        VERIFY_THROWS(is.extract<int16_t>().get(), std::range_error);
+    }
+
+    TEST(istream_extract_uint16w)
+    {
+        producer_consumer_buffer<wchar_t> rbuf;
+        const wchar_t* text = L"1024 50000 100000";
+        size_t len = wcslen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::wistream is(rbuf);
+        uint16_t i1 = is.extract<uint16_t>().get();
+        uint16_t i2 = is.extract<uint16_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, 1024);
+        VERIFY_ARE_EQUAL(i2, 50000);
+        VERIFY_THROWS(is.extract<uint16_t>().get(), std::range_error);
+    }
+#endif
+
+    TEST(istream_extract_int8)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = "0 -125 512";
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<unsigned char> is(rbuf);
+        int8_t i1 = is.extract<int8_t>().get();
+        int8_t i2 = is.extract<int8_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, '0');
+        VERIFY_ARE_EQUAL(i2, '-');
+    }
+
+    TEST(istream_extract_uint8)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = "0 150 512";
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::basic_istream<unsigned char> is(rbuf);
+        uint8_t i1 = is.extract<uint8_t>().get();
+        uint8_t i2 = is.extract<uint8_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, '0');
+        VERIFY_ARE_EQUAL(i2, '1');
+    }
+
+#ifdef _WIN32
+    TEST(istream_extract_int8w)
+    {
+        producer_consumer_buffer<wchar_t> rbuf;
+        const wchar_t* text = L"0 -125 512";
+        size_t len = wcslen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::wistream is(rbuf);
+        int8_t i1 = is.extract<int8_t>().get();
+        int8_t i2 = is.extract<int8_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, '0');
+        VERIFY_ARE_EQUAL(i2, '-');
+    }
+
+    TEST(istream_extract_uint8w)
+    {
+        producer_consumer_buffer<wchar_t> rbuf;
+        const wchar_t* text = L"0 150 512";
+        size_t len = wcslen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::wistream is(rbuf);
+        uint8_t i1 = is.extract<uint8_t>().get();
+        uint8_t i2 = is.extract<uint8_t>().get();
+
+        VERIFY_ARE_EQUAL(i1, '0');
+        VERIFY_ARE_EQUAL(i2, '1');
+    }
+#endif
+
+    TEST(istream_extract_bool)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = " true false NOT_OK";
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::istream is(rbuf);
+        bool i1 = is.extract<bool>().get();
+        bool i2 = is.extract<bool>().get();
+
+        VERIFY_IS_TRUE(i1);
+        VERIFY_IS_FALSE(i2);
+        VERIFY_THROWS(is.extract<bool>().get(), std::runtime_error);
+    }
+
+    TEST(istream_extract_bool_from_number)
+    {
+        producer_consumer_buffer<char> rbuf;
+        const char* text = " 1 0 NOT_OK";
+
+        size_t len = strlen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::istream is(rbuf);
+        bool i1 = is.extract<bool>().get();
+        bool i2 = is.extract<bool>().get();
+
+        VERIFY_IS_TRUE(i1);
+        VERIFY_IS_FALSE(i2);
+        // Make sure parsing consumes just the right amount of characters.
+        VERIFY_ARE_EQUAL(7u, rbuf.in_avail());
+        VERIFY_THROWS(is.extract<bool>().get(), std::runtime_error);
+    }
+
+#ifdef _WIN32
+    TEST(istream_extract_bool_w)
+    {
+        producer_consumer_buffer<wchar_t> rbuf;
+        const wchar_t* text = L" true false NOT_OK";
+        size_t len = wcslen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::wistream is(rbuf);
+        bool i1 = is.extract<bool>().get();
+        bool i2 = is.extract<bool>().get();
+
+        VERIFY_IS_TRUE(i1);
+        VERIFY_IS_FALSE(i2);
+        VERIFY_THROWS(is.extract<bool>().get(), std::runtime_error);
+    }
+
+    TEST(istream_extract_bool_from_number_w)
+    {
+        producer_consumer_buffer<wchar_t> rbuf;
+        const wchar_t* text = L" 1 0 NOT_OK";
+
+        size_t len = wcslen(text);
+        rbuf.putn_nocopy(text, len).wait();
+        rbuf.close(std::ios_base::out).get();
+
+        streams::wistream is(rbuf);
+        bool i1 = is.extract<bool>().get();
+        bool i2 = is.extract<bool>().get();
+
+        VERIFY_IS_TRUE(i1);
+        VERIFY_IS_FALSE(i2);
+        // Make sure parsing consumes just the right amount of characters.
+        VERIFY_ARE_EQUAL(7u, rbuf.in_avail());
+        VERIFY_THROWS(is.extract<bool>().get(), std::runtime_error);
+    }
+
+#endif
+
+    template<typename _CharType, typename _LongType>
+    void istream_extract_long_impl(streambuf<_CharType> buf)
+    {
+        basic_istream<_CharType> is(buf);
+        const _LongType v1 = is.template extract<_LongType>().get();
+        const _LongType v2 = is.template extract<_LongType>().get();
+
+        VERIFY_ARE_EQUAL(123, v1);
+        VERIFY_ARE_EQUAL(-567, v2);
+        VERIFY_THROWS(is.template extract<_LongType>().get(), std::runtime_error);
+    }
+
+    TEST(istream_extract_long)
+    {
+        istream_extract_long_impl<char, long>(
+            container_buffer<std::string>("123 -567 120000000000000000000000000000000000000000000000"));
+#ifdef _WIN32
+        istream_extract_long_impl<wchar_t, long>(container_buffer<std::wstring>(L"123 -567 12000000000"));
+#endif
+    }
+
+    template<typename _CharType, typename _LongType>
+    void istream_extract_unsigned_long_impl(streambuf<_CharType> buf)
+    {
+        basic_istream<_CharType> is(buf);
+        const _LongType v1 = is.template extract<_LongType>().get();
+        const _LongType v2 = is.template extract<_LongType>().get();
+
+        VERIFY_ARE_EQUAL(876, v1);
+        VERIFY_ARE_EQUAL(3, v2);
+        VERIFY_THROWS(is.template extract<_LongType>().get(), std::runtime_error);
+    }
+
+    TEST(istream_extract_unsigned_long)
+    {
+        istream_extract_unsigned_long_impl<char, unsigned long>(container_buffer<std::string>("876 3 -44"));
+#ifdef _WIN32
+        istream_extract_unsigned_long_impl<wchar_t, unsigned long>(container_buffer<std::wstring>(L"876 3 -44"));
+#endif
+    }
+
+    TEST(istream_extract_long_long)
+    {
+        istream_extract_long_impl<char, long long>(container_buffer<std::string>("123 -567 92233720368547758078"));
+#ifdef _WIN32
+        istream_extract_long_impl<wchar_t, long long>(container_buffer<std::wstring>(L"123 -567 92233720368547758078"));
+#endif
+    }
+
+    TEST(istream_extract_unsigned_long_long)
+    {
+        istream_extract_unsigned_long_impl<char, unsigned long long>(container_buffer<std::string>("876 3 -44"));
+#ifdef _WIN32
+        istream_extract_unsigned_long_impl<wchar_t, unsigned long long>(container_buffer<std::wstring>(L"876 3 -44"));
+#endif
+    }
+
+    template<typename T>
+    void compare_floating(T expected, T actual, T relativeDiff)
+    {
+        // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+        if (expected != actual)
+        {
+            const auto diff = fabs(expected - actual);
+            const auto absExpected = fabs(expected);
+            const auto absActual = fabs(actual);
+            const auto largest = absExpected > absActual ? absExpected : absActual;
+            if (diff > largest * relativeDiff)
+            {
+                VERIFY_IS_TRUE(false);
+            }
+        }
+    }
+    void compare_double(double expected, double actual) { compare_floating(expected, actual, DBL_EPSILON); }
+    void compare_float(float expected, float actual) { compare_floating(expected, actual, FLT_EPSILON); }
+
+    TEST(extract_floating_point)
+    {
+        std::string test_string;
+        test_string.append(" 9.81E05 3.14");
+        test_string.append(" 2.71.5"); // two numbers merged after comma
+        test_string.append(" 6E+4.5"); // two numbers merged in exponent
+        test_string.append(" 6E-4.5"); // two numbers merged in exponent
+        test_string.append(" 3.14 -10 +42.0 -1234.567 .01 +0 -0");
+#ifndef __APPLE__
+        test_string.append(" 12345678901234567890123456789012345678901234567890"); // a big number
+#endif
+        test_string.append(" 9.81E05 6.0221413e+23 1.6e-14"); // numbers with exponent
+        test_string.append(" 6.");                            // a number ending with a dot
+
+        std::stringstream std_istream;
+        std_istream << test_string;
+
+        producer_consumer_buffer<uint8_t> buff, bufd;
+        auto ostream_float = buff.create_ostream();
+        auto istream_float = buff.create_istream();
+        auto ostream_double = bufd.create_ostream();
+        auto istream_double = bufd.create_istream();
+
+        ostream_float.print(test_string);
+        ostream_double.print(test_string);
+        ostream_float.close().wait();
+        ostream_double.close().wait();
+
+        do
+        {
+            double expected = 0;
+            std_istream >> expected;
+
+            const auto actual = istream_double.extract<double>().get();
+            compare_double(expected, actual);
+
+            if (actual <= (std::numeric_limits<float>::max)())
+                compare_float(float(expected), istream_float.extract<float>().get());
+            else
+                VERIFY_THROWS(istream_float.extract<float>().get(), std::exception);
+
+            // Checking positive and negative zero's by dividing 1 with it. They should result in positive and negative
+            // infinity.
+            if (expected == 0) VERIFY_ARE_EQUAL(1 / expected, 1 / actual);
+        } while (!std_istream.eof());
+    }
+
+    TEST(extract_floating_point_with_exceptions)
+    {
+        std::vector<std::pair<std::string, std::string>> tests;
+        tests.push_back(std::pair<std::string, std::string>("a", "Invalid character 'a'"));
+        tests.push_back(std::pair<std::string, std::string>("x", "Invalid character 'x'"));
+        tests.push_back(std::pair<std::string, std::string>("e", "Invalid character 'e'"));
+        tests.push_back(std::pair<std::string, std::string>("E", "Invalid character 'E'"));
+        tests.push_back(std::pair<std::string, std::string>("6.022e+t", "Invalid character 't' in exponent"));
+        tests.push_back(std::pair<std::string, std::string>("9.81e-.", "Invalid character '.' in exponent"));
+        tests.push_back(std::pair<std::string, std::string>("9.81e-", "Incomplete exponent"));
+        tests.push_back(std::pair<std::string, std::string>("1.2e+", "Incomplete exponent"));
+        tests.push_back(std::pair<std::string, std::string>("10E+-23", "The exponent sign already set"));
+        tests.push_back(std::pair<std::string, std::string>("15E-+45", "The exponent sign already set"));
+        tests.push_back(std::pair<std::string, std::string>("5.34e", "Incomplete exponent"));
+        tests.push_back(std::pair<std::string, std::string>("2E+308", "The value is too big"));
+        tests.push_back(std::pair<std::string, std::string>("-2E+308", "The value is too big"));
+        tests.push_back(std::pair<std::string, std::string>("1E-324", "The value is too small"));
+        tests.push_back(std::pair<std::string, std::string>("-1E-324", "The value is too small"));
+
+        for (auto iter = tests.begin(); iter != tests.end(); iter++)
+        {
+            std::stringstream std_istream;
+            std_istream << iter->first;
+            VERIFY_IS_TRUE(std_istream.good());
+            double x;
+            std_istream >> x;
+            VERIFY_IS_FALSE(std_istream.good());
+
+            producer_consumer_buffer<uint8_t> buf;
+            auto stream = buf.create_ostream();
+            auto istream_double = buf.create_istream();
+
+            stream.print(iter->first);
+            stream.close().wait();
+
+            try
+            {
+                istream_double.extract<double>().get();
+                VERIFY_IS_TRUE(false, "No exception has been thrown");
+            }
+            catch (const std::exception& exc)
+            {
+                VERIFY_ARE_EQUAL(std::string(exc.what()), iter->second);
+            }
+            catch (...)
+            {
+                VERIFY_IS_TRUE(false, "A wrong exception has been thrown");
+            }
+        }
+    }
+
+    TEST(streambuf_read_delim)
+    {
+        producer_consumer_buffer<uint8_t> rbuf;
+        std::string s("Hello  World"); // there are 2 spaces here
+
+        streams::stringstreambuf data;
+
+        streams::istream is(rbuf);
+
+        auto t = is.read_to_delim(data, ' ')
+                     .then([&data, is](size_t size) -> pplx::task<size_t> {
+                         std::string r("Hello");
+                         VERIFY_ARE_EQUAL(size, r.size());
+                         VERIFY_IS_FALSE(is.is_eof());
+
+                         auto& s2 = data.collection();
+                         VERIFY_ARE_EQUAL(s2, r);
+                         return is.read_to_delim(data, ' ');
+                     })
+                     .then([&data, is](size_t size) -> pplx::task<size_t> {
+                         VERIFY_ARE_EQUAL(size, 0);
+                         VERIFY_IS_FALSE(is.is_eof());
+                         return is.read_to_delim(data, ' ');
+                     })
+                     .then([&data, is](size_t size) -> void {
+                         VERIFY_ARE_EQUAL(size, 5);
+                         VERIFY_IS_TRUE(is.is_eof());
+                     });
+        rbuf.putn_nocopy((uint8_t*)s.data(), s.size()).wait();
+        rbuf.close(std::ios_base::out).get();
+        t.wait();
+    }
+
+    TEST(uninitialized_stream)
+    {
+        streams::basic_ostream<uint8_t> test_ostream;
+        streams::basic_istream<uint8_t> test_istream;
+
+        VERIFY_IS_FALSE(test_ostream.is_valid());
+        VERIFY_IS_FALSE(test_istream.is_valid());
+
+        VERIFY_THROWS(test_istream.read(), std::logic_error);
+        VERIFY_THROWS(test_ostream.flush(), std::logic_error);
+
+        test_istream.close().wait();
+        test_ostream.close().wait();
+    }
+
+    TEST(uninitialized_streambuf)
+    {
+        streams::streambuf<uint8_t> strbuf;
+
+        // The bool operator shall not throw
+        VERIFY_IS_TRUE(!strbuf);
+
+        // All operations should throw
+        uint8_t* ptr = nullptr;
+        size_t count = 0;
+
+        VERIFY_THROWS(strbuf.acquire(ptr, count), std::invalid_argument);
+        VERIFY_THROWS(strbuf.release(ptr, count), std::invalid_argument);
+
+        VERIFY_THROWS(strbuf.alloc(count), std::invalid_argument);
+        VERIFY_THROWS(strbuf.commit(count), std::invalid_argument);
+
+        VERIFY_THROWS(strbuf.can_read(), std::invalid_argument);
+        VERIFY_THROWS(strbuf.can_write(), std::invalid_argument);
+        VERIFY_THROWS(strbuf.can_seek(), std::invalid_argument);
+
+        VERIFY_THROWS(strbuf.is_eof(), std::invalid_argument);
+        VERIFY_THROWS(strbuf.is_open(), std::invalid_argument);
+
+        VERIFY_THROWS(strbuf.in_avail(), std::invalid_argument);
+        VERIFY_THROWS(strbuf.get_base(), std::invalid_argument);
+
+        VERIFY_THROWS(strbuf.putc('a').get(), std::invalid_argument);
+        VERIFY_THROWS(strbuf.putn_nocopy(ptr, count).get(), std::invalid_argument);
+        VERIFY_THROWS(strbuf.sync().get(), std::invalid_argument);
+
+        VERIFY_THROWS(strbuf.getc().get(), std::invalid_argument);
+        VERIFY_THROWS(strbuf.ungetc().get(), std::invalid_argument);
+        VERIFY_THROWS(strbuf.bumpc().get(), std::invalid_argument);
+        VERIFY_THROWS(strbuf.nextc().get(), std::invalid_argument);
+        VERIFY_THROWS(strbuf.getn(ptr, count).get(), std::invalid_argument);
+
+        VERIFY_THROWS(strbuf.close().get(), std::invalid_argument);
+
+        // The destructor shall not throw
+    }
+
+    TEST(create_istream_from_output_only)
+    {
+        container_buffer<std::string> sourceBuf;
+        VERIFY_THROWS(sourceBuf.create_istream(), std::runtime_error);
+    }
+
+    TEST(extract_close_with_exception)
+    {
+        container_buffer<std::string> sourceBuf(std::ios::in);
+        auto inStream = sourceBuf.create_istream();
+        inStream.close(std::make_exception_ptr(std::invalid_argument("test exception"))).wait();
+        auto extractTask = inStream.extract<std::string>();
+        VERIFY_THROWS(extractTask.get(), std::invalid_argument);
+    }
+
+    TEST(streambuf_close_with_exception_read)
+    {
+        container_buffer<std::string> sourceBuf("test data string");
+        sourceBuf.close(std::ios::in, std::make_exception_ptr(std::invalid_argument("custom exception")));
+
+        const size_t size = 4;
+        char targetBuf[size];
+        auto t = sourceBuf.getn(targetBuf, size);
+        VERIFY_THROWS(t.get(), std::invalid_argument);
+    }
+
+    TEST(stream_close_with_exception_read)
+    {
+        container_buffer<std::string> sourceBuf("test data string");
+        auto inStream = sourceBuf.create_istream();
+        inStream.close(std::make_exception_ptr(std::invalid_argument("custom exception")));
+
+        container_buffer<std::string> targetBuf;
+        auto t1 = inStream.read(targetBuf, 4);
+        VERIFY_THROWS(t1.get(), std::invalid_argument);
+        VERIFY_THROWS(inStream.streambuf().sbumpc(), std::invalid_argument);
+        VERIFY_THROWS(inStream.streambuf().sgetc(), std::invalid_argument);
+    }
+
+    TEST(istream_input_after_close)
+    {
+        container_buffer<std::string> sourceBuf("test data");
+        auto inStream = sourceBuf.create_istream();
+        inStream.close().wait();
+
+        container_buffer<std::string> targetBuf;
+        VERIFY_THROWS(inStream.peek().get(), std::runtime_error);
+        VERIFY_THROWS(inStream.read(targetBuf, 1).get(), std::runtime_error);
+        VERIFY_THROWS(inStream.read_line(targetBuf).get(), std::runtime_error);
+        VERIFY_THROWS(inStream.read_to_delim(targetBuf, '-').get(), std::runtime_error);
+        VERIFY_THROWS(inStream.read_to_end(targetBuf).get(), std::runtime_error);
+        VERIFY_THROWS(inStream.seek(0), std::runtime_error);
+        VERIFY_THROWS(inStream.seek(1, std::ios::beg), std::runtime_error);
+        VERIFY_THROWS(inStream.tell(), std::runtime_error);
+        VERIFY_THROWS(inStream.extract<std::string>().get(), std::runtime_error);
+    }
+
+    TEST(extract_from_empty_stream)
+    {
+        container_buffer<std::string> sourceBuf(std::ios::in);
+        auto inStream = sourceBuf.create_istream();
+
+        VERIFY_THROWS(inStream.extract<int64_t>().get(), std::range_error);
+        VERIFY_THROWS(inStream.extract<char>().get(), std::runtime_error);
+        VERIFY_THROWS(inStream.extract<unsigned char>().get(), std::runtime_error);
+        VERIFY_THROWS(inStream.extract<signed char>().get(), std::runtime_error);
+        VERIFY_THROWS(inStream.extract<bool>().get(), std::runtime_error);
+
+        const std::string strValue = inStream.extract<std::string>().get();
+        VERIFY_ARE_EQUAL("", strValue);
+#ifdef _WIN32
+        const std::wstring wstrValue = inStream.extract<std::wstring>().get();
+        VERIFY_ARE_EQUAL(L"", wstrValue);
+#endif
+    }
+
+    TEST(seek_after_eof)
+    {
+        container_buffer<std::string> sourceBuf(std::ios::in);
+        VERIFY_ARE_EQUAL(basic_istream<char>::traits::eof(), sourceBuf.seekoff(1, std::ios::cur, std::ios::in));
+    }
+
+} // SUITE(istream_tests)
+
+} // namespace streams
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/streams/memstream_tests.cpp b/Release/tests/functional/streams/memstream_tests.cpp
new file mode 100644 (file)
index 0000000..3bdbd68
--- /dev/null
@@ -0,0 +1,2535 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests for async memory stream buffer operations.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+#if defined(__cplusplus_winrt)
+#include <wrl.h>
+#endif
+#ifdef _WIN32
+
+#include <Windows.h>
+#endif
+
+namespace tests
+{
+namespace functional
+{
+namespace streams
+{
+using namespace ::pplx;
+using namespace utility;
+using namespace concurrency::streams;
+
+template<class StreamBufferType>
+void streambuf_putc(StreamBufferType& wbuf)
+{
+    VERIFY_IS_TRUE(wbuf.can_write());
+
+    std::basic_string<typename StreamBufferType::char_type> s;
+    s.push_back((typename StreamBufferType::char_type)0);
+    s.push_back((typename StreamBufferType::char_type)1);
+    s.push_back((typename StreamBufferType::char_type)2);
+    s.push_back((typename StreamBufferType::char_type)3);
+
+    // Verify putc synchronously
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[0], wbuf.putc(s[0]).get());
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[1], wbuf.putc(s[1]).get());
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[2], wbuf.putc(s[2]).get());
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[3], wbuf.putc(s[3]).get());
+
+    VERIFY_ARE_EQUAL(s.size(), wbuf.in_avail());
+
+    // Verify putc async
+    size_t count = 10;
+    auto seg2 = [&count](typename StreamBufferType::int_type) { return (--count > 0); };
+    auto seg1 = [&s, &wbuf, seg2]() { return wbuf.putc(s[0]).then(seg2); };
+    pplx::details::_do_while(seg1).wait();
+
+    VERIFY_ARE_EQUAL(s.size() + 10, wbuf.in_avail());
+
+    VERIFY_IS_TRUE(wbuf.close().get());
+    VERIFY_IS_FALSE(wbuf.can_write());
+
+    // verify putc after close
+    VERIFY_ARE_EQUAL(StreamBufferType::traits::eof(), wbuf.putc(s[0]).get());
+}
+
+template<class CharType>
+void streambuf_putc(concurrency::streams::rawptr_buffer<CharType>& wbuf)
+{
+    VERIFY_IS_TRUE(wbuf.can_write());
+    typedef concurrency::streams::rawptr_buffer<CharType> StreamBufferType;
+
+    std::basic_string<CharType> s;
+    s.push_back((CharType)0);
+    s.push_back((CharType)1);
+    s.push_back((CharType)2);
+    s.push_back((CharType)3);
+
+    // Verify putc synchronously
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[0], wbuf.putc(s[0]).get());
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[1], wbuf.putc(s[1]).get());
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[2], wbuf.putc(s[2]).get());
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[3], wbuf.putc(s[3]).get());
+
+    VERIFY_ARE_EQUAL(s.size(), wbuf.block().size());
+
+    // Verify putc async
+    size_t count = 10;
+    auto seg2 = [&count](typename StreamBufferType::int_type) { return (--count > 0); };
+    auto seg1 = [&s, &wbuf, seg2]() { return wbuf.putc(s[0]).then(seg2); };
+    pplx::details::_do_while(seg1).wait();
+
+    VERIFY_ARE_EQUAL(s.size() + 10, wbuf.block().size());
+
+    VERIFY_IS_TRUE(wbuf.close().get());
+    VERIFY_IS_FALSE(wbuf.can_write());
+
+    // verify putc after close
+    VERIFY_ARE_EQUAL(StreamBufferType::traits::eof(), wbuf.putc(s[0]).get());
+}
+
+template<class CollectionType>
+void streambuf_putc(concurrency::streams::container_buffer<CollectionType>& wbuf)
+{
+    VERIFY_IS_TRUE(wbuf.can_write());
+    typedef concurrency::streams::container_buffer<CollectionType> StreamBufferType;
+    typedef typename concurrency::streams::container_buffer<CollectionType>::char_type CharType;
+
+    std::basic_string<CharType> s;
+    s.push_back((CharType)0);
+    s.push_back((CharType)1);
+    s.push_back((CharType)2);
+    s.push_back((CharType)3);
+
+    // Verify putc synchronously
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[0], wbuf.putc(s[0]).get());
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[1], wbuf.putc(s[1]).get());
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[2], wbuf.putc(s[2]).get());
+    VERIFY_ARE_EQUAL((typename StreamBufferType::int_type)s[3], wbuf.putc(s[3]).get());
+
+    VERIFY_ARE_EQUAL(s.size(), wbuf.collection().size());
+
+    // Verify putc async
+    size_t count = 10;
+    auto seg2 = [&count](typename StreamBufferType::int_type) { return (--count > 0); };
+    auto seg1 = [&s, &wbuf, seg2]() { return wbuf.putc(s[0]).then(seg2); };
+    pplx::details::_do_while(seg1).wait();
+
+    VERIFY_ARE_EQUAL(s.size() + 10, wbuf.collection().size());
+
+    wbuf.close().get();
+    VERIFY_IS_FALSE(wbuf.can_write());
+
+    // verify putc after close
+    VERIFY_ARE_EQUAL(StreamBufferType::traits::eof(), wbuf.putc(s[0]).get());
+}
+
+template<class StreamBufferType>
+void streambuf_putn(StreamBufferType& wbuf)
+{
+    VERIFY_IS_TRUE(wbuf.can_write());
+
+    std::basic_string<typename StreamBufferType::char_type> s;
+    s.push_back((typename StreamBufferType::char_type)0);
+    s.push_back((typename StreamBufferType::char_type)1);
+    s.push_back((typename StreamBufferType::char_type)2);
+    s.push_back((typename StreamBufferType::char_type)3);
+
+    VERIFY_ARE_EQUAL(s.size(), wbuf.putn_nocopy(s.data(), s.size()).get());
+    VERIFY_ARE_EQUAL(s.size() * 1, wbuf.in_avail());
+
+    VERIFY_ARE_EQUAL(s.size(), wbuf.putn_nocopy(s.data(), s.size()).get());
+    VERIFY_ARE_EQUAL(s.size() * 2, wbuf.in_avail());
+
+    int count = 10;
+    auto seg2 = [&count](size_t) { return (--count > 0); };
+    auto seg1 = [&s, &wbuf, seg2]() { return wbuf.putn_nocopy(s.data(), s.size()).then(seg2); };
+    pplx::details::_do_while(seg1).wait();
+    VERIFY_ARE_EQUAL(s.size() * 12, wbuf.in_avail());
+
+    wbuf.close().get();
+    VERIFY_IS_FALSE(wbuf.can_write());
+
+    // verify putn after close
+    VERIFY_ARE_EQUAL(0, wbuf.putn_nocopy(s.data(), s.size()).get());
+}
+
+template<class CharType>
+void streambuf_putn(concurrency::streams::rawptr_buffer<CharType>& wbuf)
+{
+    VERIFY_IS_TRUE(wbuf.can_write());
+
+    typedef concurrency::streams::rawptr_buffer<CharType> StreamBufferType;
+
+    std::basic_string<CharType> s;
+    s.push_back((CharType)0);
+    s.push_back((CharType)1);
+    s.push_back((CharType)2);
+    s.push_back((CharType)3);
+
+    VERIFY_ARE_EQUAL(s.size(), wbuf.putn_nocopy(s.data(), s.size()).get());
+
+    VERIFY_ARE_EQUAL(s.size(), wbuf.putn_nocopy(s.data(), s.size()).get());
+
+    int count = 10;
+    auto seg2 = [&count](size_t) { return (--count > 0); };
+    auto seg1 = [&s, &wbuf, seg2]() { return wbuf.putn_nocopy(s.data(), s.size()).then(seg2); };
+    pplx::details::_do_while(seg1).wait();
+
+    wbuf.close().get();
+    VERIFY_IS_FALSE(wbuf.can_write());
+
+    // verify putn after close
+    VERIFY_ARE_EQUAL(0, wbuf.putn_nocopy(s.data(), s.size()).get());
+}
+
+template<class CollectionType>
+void streambuf_putn(concurrency::streams::container_buffer<CollectionType>& wbuf)
+{
+    VERIFY_IS_TRUE(wbuf.can_write());
+    typedef concurrency::streams::container_buffer<CollectionType> StreamBufferType;
+    typedef typename concurrency::streams::container_buffer<CollectionType>::char_type CharType;
+
+    std::basic_string<CharType> s;
+    s.push_back((CharType)0);
+    s.push_back((CharType)1);
+    s.push_back((CharType)2);
+    s.push_back((CharType)3);
+
+    VERIFY_ARE_EQUAL(s.size(), wbuf.putn_nocopy(s.data(), s.size()).get());
+
+    VERIFY_ARE_EQUAL(s.size(), wbuf.putn_nocopy(s.data(), s.size()).get());
+
+    int count = 10;
+    auto seg2 = [&count](size_t) { return (--count > 0); };
+    auto seg1 = [&s, &wbuf, seg2]() { return wbuf.putn_nocopy(s.data(), s.size()).then(seg2); };
+    pplx::details::_do_while(seg1).wait();
+
+    wbuf.close().get();
+    VERIFY_IS_FALSE(wbuf.can_write());
+
+    // verify putn_nocopy after close
+    VERIFY_ARE_EQUAL(0, wbuf.putn_nocopy(s.data(), s.size()).get());
+}
+
+template<class StreamBufferType>
+void streambuf_alloc_commit(StreamBufferType& wbuf)
+{
+    VERIFY_IS_TRUE(wbuf.can_write());
+
+    VERIFY_ARE_EQUAL(0, wbuf.in_avail());
+
+    size_t allocSize = 10;
+    size_t commitSize = 2;
+
+    for (size_t i = 0; i < allocSize / commitSize; i++)
+    {
+        // Allocate space for 10 chars
+        auto data = wbuf.alloc(allocSize);
+
+        VERIFY_IS_TRUE(data != nullptr);
+
+        // commit 2
+        wbuf.commit(commitSize);
+
+        VERIFY_ARE_EQUAL((i + 1) * commitSize, wbuf.in_avail());
+    }
+
+    VERIFY_ARE_EQUAL(allocSize, wbuf.in_avail());
+
+    wbuf.close().get();
+    VERIFY_IS_FALSE(wbuf.can_write());
+}
+
+template<class CollectionType>
+void streambuf_alloc_commit(concurrency::streams::container_buffer<CollectionType>& wbuf)
+{
+    VERIFY_IS_TRUE(wbuf.can_write());
+
+    VERIFY_ARE_EQUAL(0, wbuf.collection().size());
+
+    size_t allocSize = 10;
+    size_t commitSize = 2;
+
+    for (size_t i = 0; i < allocSize / commitSize; i++)
+    {
+        // Allocate space for 10 chars
+        auto data = wbuf.alloc(allocSize);
+
+        VERIFY_IS_TRUE(data != nullptr);
+
+        // commit 2
+        wbuf.commit(commitSize);
+
+        VERIFY_IS_TRUE((i + 1) * commitSize <= wbuf.collection().size());
+    }
+
+    VERIFY_IS_TRUE(allocSize <= wbuf.collection().size());
+
+    wbuf.close().get();
+    VERIFY_IS_FALSE(wbuf.can_write());
+}
+
+template<class CharType>
+void streambuf_alloc_commit(concurrency::streams::rawptr_buffer<CharType>& wbuf)
+{
+    VERIFY_IS_TRUE(wbuf.can_write());
+
+    VERIFY_ARE_EQUAL(0, wbuf.block().size());
+
+    size_t allocSize = 10;
+    size_t commitSize = 2;
+
+    for (size_t i = 0; i < allocSize / commitSize; i++)
+    {
+        // Allocate space for 10 chars
+        auto data = wbuf.alloc(allocSize);
+
+        VERIFY_IS_TRUE(data != nullptr);
+
+        // commit 2
+        wbuf.commit(commitSize);
+
+        VERIFY_IS_TRUE((i + 1) * commitSize <= wbuf.block().size());
+    }
+
+    VERIFY_IS_TRUE(allocSize <= wbuf.block().size());
+
+    VERIFY_IS_TRUE(wbuf.close().get());
+    VERIFY_IS_FALSE(wbuf.can_write());
+}
+template<class StreamBufferType>
+void streambuf_seek_write(StreamBufferType& wbuf)
+{
+    VERIFY_IS_TRUE(wbuf.can_write());
+    VERIFY_IS_TRUE(wbuf.can_seek());
+
+    auto beg = wbuf.seekoff(0, std::ios_base::beg, std::ios_base::out);
+    auto cur = wbuf.seekoff(0, std::ios_base::cur, std::ios_base::out);
+
+    // current should be at the begining
+    VERIFY_ARE_EQUAL(beg, cur);
+
+    auto end = wbuf.seekoff(0, std::ios_base::end, std::ios_base::out);
+    VERIFY_ARE_EQUAL(end, wbuf.seekpos(end, std::ios_base::out));
+
+    wbuf.close().get();
+    VERIFY_IS_FALSE(wbuf.can_write());
+    VERIFY_IS_FALSE(wbuf.can_seek());
+}
+
+template<class StreamBufferType>
+void streambuf_getc(StreamBufferType& rbuf, typename StreamBufferType::char_type contents)
+{
+    VERIFY_IS_TRUE(rbuf.can_read());
+
+    auto c = rbuf.getc().get();
+
+    VERIFY_ARE_EQUAL(c, contents);
+
+    // Calling getc again should return the same character (getc do not advance read head)
+    VERIFY_ARE_EQUAL(c, rbuf.getc().get());
+
+    rbuf.close().get();
+    VERIFY_IS_FALSE(rbuf.can_read());
+
+    // getc should return eof after close
+    VERIFY_ARE_EQUAL(StreamBufferType::traits::eof(), rbuf.getc().get());
+}
+
+template<class StreamBufferType>
+void streambuf_sgetc(StreamBufferType& rbuf, typename StreamBufferType::char_type contents)
+{
+    VERIFY_IS_TRUE(rbuf.can_read());
+
+    auto c = rbuf.sgetc();
+
+    VERIFY_ARE_EQUAL(c, contents);
+
+    // Calling getc again should return the same character (getc do not advance read head)
+    VERIFY_ARE_EQUAL(c, rbuf.sgetc());
+
+    rbuf.close().get();
+    VERIFY_IS_FALSE(rbuf.can_read());
+
+    // sgetc should return eof after close
+    VERIFY_ARE_EQUAL(StreamBufferType::traits::eof(), rbuf.sgetc());
+}
+
+template<class StreamBufferType>
+void streambuf_bumpc(StreamBufferType& rbuf, const std::vector<typename StreamBufferType::char_type>& contents)
+{
+    VERIFY_IS_TRUE(rbuf.can_read());
+
+    auto c = rbuf.bumpc().get();
+
+    VERIFY_ARE_EQUAL(c, contents[0]);
+
+    // Calling bumpc again should return the next character
+    // Read till eof
+    auto d = rbuf.bumpc().get();
+
+    size_t index = 1;
+
+    while (d != StreamBufferType::traits::eof())
+    {
+        VERIFY_ARE_EQUAL(d, contents[index]);
+        d = rbuf.bumpc().get();
+        index++;
+    }
+
+    rbuf.close().get();
+    VERIFY_IS_FALSE(rbuf.can_read());
+
+    // operation should return eof after close
+    VERIFY_ARE_EQUAL(StreamBufferType::traits::eof(), rbuf.bumpc().get());
+}
+
+template<class StreamBufferType>
+void streambuf_sbumpc(StreamBufferType& rbuf, const std::vector<typename StreamBufferType::char_type>& contents)
+{
+    VERIFY_IS_TRUE(rbuf.can_read());
+
+    auto c = rbuf.sbumpc();
+
+    VERIFY_ARE_EQUAL(c, contents[0]);
+
+    // Calling sbumpc again should return the next character
+    // Read till eof
+    auto d = rbuf.sbumpc();
+
+    size_t index = 1;
+
+    while (d != StreamBufferType::traits::eof())
+    {
+        VERIFY_ARE_EQUAL(d, contents[index]);
+        d = rbuf.sbumpc();
+        index++;
+    }
+
+    rbuf.close().get();
+    VERIFY_IS_FALSE(rbuf.can_read());
+
+    // operation should return eof after close
+    VERIFY_ARE_EQUAL(StreamBufferType::traits::eof(), rbuf.sbumpc());
+}
+
+template<class StreamBufferType>
+void streambuf_nextc(StreamBufferType& rbuf, const std::vector<typename StreamBufferType::char_type>& contents)
+{
+    VERIFY_IS_TRUE(rbuf.can_read());
+
+    auto c = rbuf.nextc().get();
+
+    VERIFY_ARE_EQUAL(c, contents[1]);
+
+    // Calling getc should return the same contents as before.
+    VERIFY_ARE_EQUAL(c, rbuf.getc().get());
+
+    size_t index = 1;
+
+    while (c != StreamBufferType::traits::eof())
+    {
+        VERIFY_ARE_EQUAL(c, contents[index]);
+        c = rbuf.nextc().get();
+        index++;
+    }
+
+    rbuf.close().get();
+    VERIFY_IS_FALSE(rbuf.can_read());
+
+    // operation should return eof after close
+    VERIFY_ARE_EQUAL(StreamBufferType::traits::eof(), rbuf.nextc().get());
+}
+
+template<class StreamBufferType>
+void streambuf_ungetc(StreamBufferType& rbuf, const std::vector<typename StreamBufferType::char_type>& contents)
+{
+    VERIFY_IS_TRUE(rbuf.can_read());
+
+    // ungetc from the begining should return eof
+    VERIFY_ARE_EQUAL(StreamBufferType::traits::eof(), rbuf.ungetc().get());
+
+    VERIFY_ARE_EQUAL(contents[0], rbuf.bumpc().get());
+    VERIFY_ARE_EQUAL(contents[1], rbuf.getc().get());
+
+    auto c = rbuf.ungetc().get();
+
+    // ungetc could be unsupported!
+    if (c != StreamBufferType::traits::eof())
+    {
+        VERIFY_ARE_EQUAL(contents[0], c);
+    }
+
+    rbuf.close().get();
+    VERIFY_IS_FALSE(rbuf.can_read());
+}
+
+template<class StreamBufferType>
+void streambuf_getn(StreamBufferType& rbuf, const std::vector<typename StreamBufferType::char_type>& contents)
+{
+    VERIFY_IS_TRUE(rbuf.can_read());
+    VERIFY_IS_FALSE(rbuf.can_write());
+
+    auto ptr = new typename StreamBufferType::char_type[contents.size()];
+    VERIFY_ARE_EQUAL(contents.size(), rbuf.getn(ptr, contents.size()).get());
+
+    // We shouldn't be able to read any more
+    VERIFY_ARE_EQUAL(0, rbuf.getn(ptr, contents.size()).get());
+
+    rbuf.close().get();
+    VERIFY_IS_FALSE(rbuf.can_read());
+
+    // We shouldn't be able to read any more
+    VERIFY_ARE_EQUAL(0, rbuf.getn(ptr, contents.size()).get());
+
+    delete[] ptr;
+}
+
+template<class StreamBufferType>
+void streambuf_acquire_release(StreamBufferType& rbuf, const std::vector<typename StreamBufferType::char_type>&)
+{
+    VERIFY_IS_TRUE(rbuf.can_read());
+
+    typename StreamBufferType::char_type* ptr = nullptr;
+    size_t size = 0;
+    rbuf.acquire(ptr, size);
+
+    if (ptr != nullptr)
+    {
+        VERIFY_IS_TRUE(size > 0);
+        rbuf.release(ptr, size - 1);
+
+        rbuf.acquire(ptr, size);
+        VERIFY_IS_TRUE(size > 0);
+        rbuf.release(ptr, 0);
+
+        rbuf.acquire(ptr, size);
+        VERIFY_IS_TRUE(size > 0);
+        rbuf.release(ptr, size);
+    }
+    else
+    {
+        // This shouldn't crash
+        rbuf.release(ptr, size);
+    }
+
+    rbuf.close().get();
+    VERIFY_IS_FALSE(rbuf.can_read());
+}
+
+template<class StreamBufferType>
+void streambuf_seek_read(StreamBufferType& rbuf)
+{
+    VERIFY_IS_TRUE(rbuf.can_read());
+    VERIFY_IS_TRUE(rbuf.can_seek());
+
+    auto beg = rbuf.seekoff(0, std::ios_base::beg, std::ios_base::in);
+    auto cur = rbuf.seekoff(0, std::ios_base::cur, std::ios_base::in);
+
+    // current should be at the begining
+    VERIFY_ARE_EQUAL(beg, cur);
+
+    auto end = rbuf.seekoff(0, std::ios_base::end, std::ios_base::in);
+    VERIFY_ARE_EQUAL(end, rbuf.seekpos(end, std::ios_base::in));
+
+    rbuf.close().get();
+    VERIFY_IS_FALSE(rbuf.can_read());
+    VERIFY_IS_FALSE(rbuf.can_seek());
+}
+
+template<class StreamBufferType>
+void streambuf_putn_getn(StreamBufferType& rwbuf)
+{
+    VERIFY_IS_TRUE(rwbuf.is_open());
+    VERIFY_IS_TRUE(rwbuf.can_read());
+    VERIFY_IS_TRUE(rwbuf.can_write());
+    VERIFY_IS_FALSE(rwbuf.is_eof());
+    std::basic_string<typename StreamBufferType::char_type> s;
+    s.push_back((typename StreamBufferType::char_type)0);
+    s.push_back((typename StreamBufferType::char_type)1);
+    s.push_back((typename StreamBufferType::char_type)2);
+    s.push_back((typename StreamBufferType::char_type)3);
+
+    typename StreamBufferType::char_type ptr[4];
+
+    auto readTask = pplx::create_task([&s, &ptr, &rwbuf]() {
+        VERIFY_ARE_EQUAL(rwbuf.getn(ptr, 4).get(), 4);
+
+        for (size_t i = 0; i < 4; i++)
+        {
+            VERIFY_ARE_EQUAL(s[i], ptr[i]);
+        }
+        VERIFY_IS_FALSE(rwbuf.is_eof());
+        VERIFY_ARE_EQUAL(rwbuf.getc().get(), std::char_traits<char>::eof());
+        VERIFY_IS_TRUE(rwbuf.is_eof());
+    });
+
+    auto writeTask = pplx::create_task([&s, &rwbuf]() {
+        VERIFY_ARE_EQUAL(rwbuf.putn_nocopy(s.data(), s.size()).get(), s.size());
+        rwbuf.close(std::ios_base::out);
+    });
+
+    writeTask.wait();
+    readTask.wait();
+
+    rwbuf.close().get();
+}
+
+template<class StreamBufferType>
+void streambuf_acquire_alloc(StreamBufferType& rwbuf)
+{
+    VERIFY_IS_TRUE(rwbuf.is_open());
+    VERIFY_IS_TRUE(rwbuf.can_read());
+    VERIFY_IS_TRUE(rwbuf.can_write());
+
+    {
+        // There should be nothing to read
+        typename StreamBufferType::char_type* ptr = nullptr;
+        size_t count = 0;
+        rwbuf.acquire(ptr, count);
+        VERIFY_ARE_EQUAL(count, 0);
+        rwbuf.release(ptr, count);
+    }
+
+    auto writeTask = pplx::create_task([&rwbuf]() {
+        auto ptr = rwbuf.alloc(8);
+        VERIFY_IS_TRUE(ptr != nullptr);
+        rwbuf.commit(4);
+    });
+
+    typename StreamBufferType::char_type* ptr = nullptr;
+    auto readTask = pplx::create_task([&rwbuf, &ptr, writeTask]() {
+        size_t count = 0;
+
+        int repeat = 10;
+        while ((count == 0) && (repeat-- > 0))
+        {
+            rwbuf.acquire(ptr, count);
+        }
+
+        if (count == 0)
+        {
+            writeTask.wait();
+        }
+
+        rwbuf.acquire(ptr, count);
+        VERIFY_ARE_EQUAL(count, 4);
+        rwbuf.release(ptr, count);
+    });
+
+    writeTask.wait();
+    readTask.wait();
+
+    rwbuf.close().get();
+}
+
+template<class StreamBufferType>
+void streambuf_close(StreamBufferType& rwbuf)
+{
+    VERIFY_IS_TRUE(rwbuf.is_open());
+
+    bool can_rd = rwbuf.can_read();
+    bool can_wr = rwbuf.can_write();
+
+    if (can_wr)
+    {
+        // Close the write head
+        rwbuf.close(std::ios_base::out).get();
+        VERIFY_IS_FALSE(rwbuf.can_write());
+
+        if (can_rd)
+        {
+            VERIFY_IS_FALSE(rwbuf.can_write());
+            VERIFY_IS_TRUE(rwbuf.can_read());
+
+            // The buffer should still be open for read
+            VERIFY_IS_TRUE(rwbuf.is_open());
+
+            // Closing the write head again should not throw
+            rwbuf.close(std::ios_base::out).wait();
+
+            // The read head should still be open
+            VERIFY_IS_TRUE(rwbuf.can_read());
+        }
+    }
+
+    if (can_rd)
+    {
+        // Close the read head
+        rwbuf.close(std::ios_base::in).get();
+        VERIFY_IS_FALSE(rwbuf.can_read());
+
+        // Closing the read head again should not throw
+        rwbuf.close(std::ios_base::in).wait();
+    }
+
+    // The buffer should now be closed
+    VERIFY_IS_FALSE(rwbuf.is_open());
+}
+
+template<class StreamBufferType>
+void streambuf_close_read_with_pending_read(StreamBufferType& rwbuf)
+{
+    VERIFY_IS_TRUE(rwbuf.is_open());
+    VERIFY_IS_TRUE(rwbuf.can_read());
+    VERIFY_IS_TRUE(rwbuf.can_write());
+
+    // Write 4 characters
+    std::basic_string<typename StreamBufferType::char_type> s;
+    s.push_back((typename StreamBufferType::char_type)0);
+    s.push_back((typename StreamBufferType::char_type)1);
+    s.push_back((typename StreamBufferType::char_type)2);
+    s.push_back((typename StreamBufferType::char_type)3);
+
+    VERIFY_ARE_EQUAL(s.size(), rwbuf.putn_nocopy(s.data(), s.size()).get());
+    VERIFY_ARE_EQUAL(s.size() * 1, rwbuf.in_avail());
+
+    // Try to read 8 characters - this should block
+    typename StreamBufferType::char_type data[8];
+    auto readTask = rwbuf.getn(data, 8);
+
+    // Close the write head
+    rwbuf.close(std::ios_base::out).get();
+    VERIFY_IS_FALSE(rwbuf.can_write());
+
+    // The buffer should still be open for read
+    VERIFY_IS_TRUE(rwbuf.is_open());
+
+    // The read head should still be open
+    VERIFY_IS_TRUE(rwbuf.can_read());
+
+    // Closing the write head should trigger the completion of the read request
+    VERIFY_ARE_EQUAL(4, readTask.get());
+
+    // Close the read head
+    rwbuf.close(std::ios_base::in).get();
+    VERIFY_IS_FALSE(rwbuf.can_read());
+
+    // The buffer should now be closed
+    VERIFY_IS_FALSE(rwbuf.is_open());
+}
+
+template<class StreamBufferType>
+void streambuf_close_write_with_pending_read(StreamBufferType& rwbuf)
+{
+    VERIFY_IS_TRUE(rwbuf.is_open());
+    VERIFY_IS_TRUE(rwbuf.can_read());
+    VERIFY_IS_TRUE(rwbuf.can_write());
+
+    // Write 4 characters
+    std::basic_string<typename StreamBufferType::char_type> s;
+    s.push_back((typename StreamBufferType::char_type)0);
+    s.push_back((typename StreamBufferType::char_type)1);
+    s.push_back((typename StreamBufferType::char_type)2);
+    s.push_back((typename StreamBufferType::char_type)3);
+
+    VERIFY_ARE_EQUAL(s.size(), rwbuf.putn_nocopy(s.data(), s.size()).get());
+    VERIFY_ARE_EQUAL(s.size() * 1, rwbuf.in_avail());
+
+    // Try to read 8 characters - this should block
+    typename StreamBufferType::char_type data[8];
+    auto readTask = rwbuf.getn(data, 8);
+
+    // Close the read head
+    rwbuf.close(std::ios_base::in).get();
+    VERIFY_IS_FALSE(rwbuf.can_read());
+
+    // The read task should not be completed
+    VERIFY_IS_FALSE(readTask.is_done());
+
+    // Close the write head
+    rwbuf.close(std::ios_base::out).get();
+    VERIFY_IS_FALSE(rwbuf.can_write());
+
+    // Closing the write head should trigger the completion of the read request
+    VERIFY_ARE_EQUAL(4, readTask.get());
+
+    // The buffer should now be closed
+    VERIFY_IS_FALSE(rwbuf.is_open());
+}
+
+template<class StreamBufferType>
+void streambuf_close_parallel(StreamBufferType& rwbuf)
+{
+    VERIFY_IS_TRUE(rwbuf.is_open());
+    VERIFY_IS_TRUE(rwbuf.can_read());
+    VERIFY_IS_TRUE(rwbuf.can_write());
+
+    // Close the read and write head in parallel
+    auto closeReadTask = pplx::create_task([&rwbuf]() {
+        VERIFY_IS_TRUE(rwbuf.can_read());
+
+        // Close the read head
+        rwbuf.close(std::ios_base::in).get();
+        VERIFY_IS_FALSE(rwbuf.can_read());
+
+        // Closing the read head again should not throw
+        rwbuf.close(std::ios_base::in).wait();
+    });
+
+    auto closeWriteTask = pplx::create_task([&rwbuf]() {
+        VERIFY_IS_TRUE(rwbuf.can_write());
+
+        // Close the write head
+        rwbuf.close(std::ios_base::out).get();
+        VERIFY_IS_FALSE(rwbuf.can_write());
+
+        // Closing the write head again should not throw
+        rwbuf.close(std::ios_base::out).wait();
+    });
+
+    closeReadTask.wait();
+    closeWriteTask.wait();
+
+    // The buffer should now be closed
+    VERIFY_IS_FALSE(rwbuf.is_open());
+}
+
+streams::producer_consumer_buffer<uint8_t> create_producer_consumer_buffer_with_data(const std::vector<uint8_t>& s)
+{
+    streams::producer_consumer_buffer<uint8_t> buf;
+    VERIFY_ARE_EQUAL(buf.putn_nocopy(s.data(), s.size()).get(), s.size());
+    buf.close(std::ios_base::out).get();
+    return buf;
+}
+
+SUITE(memstream_tests)
+{
+    TEST(string_buffer_putc)
+    {
+        stringstreambuf buf;
+        streambuf_putc(buf);
+    }
+
+    TEST(charptr_buffer_putc_fail)
+    {
+        char chars[26];
+        rawptr_buffer<char> buf(chars, 26);
+        VERIFY_ARE_EQUAL(buf.putn_nocopy("abcdefghijklmnopqrstuvwxyz", 26).get(), 26);
+        VERIFY_ARE_EQUAL(buf.putc('a').get(), std::char_traits<char>::eof());
+    }
+
+    TEST(wstring_buffer_putc)
+    {
+        wstringstreambuf buf;
+        streambuf_putc(buf);
+    }
+
+    TEST(string_buffer_putn)
+    {
+        stringstreambuf buf;
+        streambuf_putn(buf);
+    }
+    TEST(wstring_buffer_putn)
+    {
+        wstringstreambuf buf;
+        streambuf_putn(buf);
+    }
+    TEST(charptr_buffer_putn)
+    {
+        {
+            char chars[128];
+            rawptr_buffer<char> buf(chars, sizeof(chars));
+            streambuf_putn(buf);
+        }
+        {
+            uint8_t chars[128];
+            rawptr_buffer<uint8_t> buf(chars, sizeof(chars));
+            streambuf_putn(buf);
+        }
+        {
+            utf16char chars[128];
+            rawptr_buffer<utf16char> buf(chars, sizeof(chars) / sizeof(utf16char));
+            streambuf_putn(buf);
+        }
+    }
+    TEST(charptr_buffer_putn_fail)
+    {
+        {
+            char chars[128];
+            rawptr_buffer<char> buf(chars, 12);
+            VERIFY_THROWS(buf.putn_nocopy("abcdefghijklmnopqrstuvwxyz", 26).get(), std::runtime_error);
+        }
+    }
+
+    TEST(bytevec_buffer_putn)
+    {
+        {
+            container_buffer<std::vector<uint8_t>> buf;
+            streambuf_putn(buf);
+        }
+        {
+            container_buffer<std::vector<char>> buf;
+            streambuf_putn(buf);
+        }
+        {
+            container_buffer<std::vector<utf16char>> buf;
+            streambuf_putn(buf);
+        }
+    }
+    TEST(mem_buffer_putn)
+    {
+        {
+            streams::producer_consumer_buffer<char> buf;
+            streambuf_putn(buf);
+        }
+
+        {
+            streams::producer_consumer_buffer<uint8_t> buf;
+            streambuf_putn(buf);
+        }
+
+        {
+            streams::producer_consumer_buffer<utf16char> buf;
+            streambuf_putn(buf);
+        }
+    }
+
+    TEST(string_buffer_alloc_commit)
+    {
+        stringstreambuf buf;
+        streambuf_alloc_commit(buf);
+    }
+
+    TEST(wstring_buffer_alloc_commit)
+    {
+        wstringstreambuf buf;
+        streambuf_alloc_commit(buf);
+    }
+
+    TEST(mem_buffer_alloc_commit)
+    {
+        {
+            streams::producer_consumer_buffer<char> buf;
+            streambuf_alloc_commit(buf);
+        }
+
+        {
+            streams::producer_consumer_buffer<uint8_t> buf;
+            streambuf_alloc_commit(buf);
+        }
+
+        {
+            streams::producer_consumer_buffer<utf16char> buf;
+            streambuf_alloc_commit(buf);
+        }
+    }
+
+    TEST(string_buffer_seek_write)
+    {
+        stringstreambuf buf;
+        streambuf_seek_write(buf);
+    }
+    TEST(wstring_buffer_seek_write)
+    {
+        wstringstreambuf buf;
+        streambuf_seek_write(buf);
+    }
+    TEST(charptr_buffer_seek_write)
+    {
+        {
+            char chars[128];
+            rawptr_buffer<char> buf(chars, sizeof(chars));
+            streambuf_seek_write(buf);
+        }
+        {
+            uint8_t chars[128];
+            rawptr_buffer<uint8_t> buf(chars, sizeof(chars));
+            streambuf_seek_write(buf);
+        }
+        {
+            utf16char chars[128];
+            rawptr_buffer<utf16char> buf(chars, sizeof(chars) / sizeof(utf16char));
+            streambuf_seek_write(buf);
+        }
+    }
+    TEST(bytevec_buffer_seek_write)
+    {
+        {
+            container_buffer<std::vector<uint8_t>> buf;
+            streambuf_seek_write(buf);
+        }
+        {
+            container_buffer<std::vector<char>> buf;
+            streambuf_seek_write(buf);
+        }
+        {
+            container_buffer<std::vector<utf16char>> buf;
+            streambuf_seek_write(buf);
+        }
+    }
+    TEST(mem_buffer_seek_write)
+    {
+        streams::producer_consumer_buffer<char> buf;
+        VERIFY_IS_FALSE(buf.can_seek());
+    }
+
+    TEST(string_buffer_getc)
+    {
+        std::string s("Hello World");
+        std::vector<char> v(std::begin(s), std::end(s));
+        streams::stringstreambuf buf(s);
+        streambuf_getc(buf, v[0]);
+    }
+    TEST(wstring_buffer_getc)
+    {
+        utility::string_t s(U("Hello World"));
+        std::vector<utility::char_t> v(std::begin(s), std::end(s));
+        streams::wstringstreambuf buf(s);
+        streambuf_getc(buf, v[0]);
+    }
+    TEST(charptr_buffer_getc)
+    {
+        {
+            char chars[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+            rawptr_buffer<char> buf(chars, sizeof(chars), std::ios::in);
+            streambuf_getc(buf, chars[0]);
+        }
+        {
+            uint8_t chars[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+            rawptr_buffer<uint8_t> buf(chars, sizeof(chars), std::ios::in);
+            streambuf_getc(buf, chars[0]);
+        }
+        {
+            utf16char chars[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+            rawptr_buffer<utf16char> buf(chars, sizeof(chars) / sizeof(utf16char), std::ios::in);
+            streambuf_getc(buf, chars[0]);
+        }
+    }
+    TEST(bytevec_buffer_getc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        container_buffer<std::vector<uint8_t>> buf(s);
+        streambuf_getc(buf, s[0]);
+    }
+    TEST(mem_buffer_getc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        streams::producer_consumer_buffer<uint8_t> buf = create_producer_consumer_buffer_with_data(s);
+        streambuf_getc(buf, s[0]);
+    }
+
+    TEST(string_buffer_sgetc)
+    {
+        std::string s("Hello World");
+        std::vector<char> v(std::begin(s), std::end(s));
+        streams::stringstreambuf buf(s);
+        streambuf_sgetc(buf, v[0]);
+    }
+    TEST(wstring_buffer_sgetc)
+    {
+        utility::string_t s(U("Hello World"));
+        std::vector<utility::char_t> v(std::begin(s), std::end(s));
+        streams::wstringstreambuf buf(s);
+        streambuf_sgetc(buf, v[0]);
+    }
+    TEST(charptr_buffer_sgetc)
+    {
+        char chars[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        rawptr_buffer<char> buf(chars, sizeof(chars), std::ios::in);
+        streambuf_sgetc(buf, chars[0]);
+    }
+    TEST(bytevec_buffer_sgetc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        container_buffer<std::vector<uint8_t>> buf(s);
+        streambuf_sgetc(buf, s[0]);
+    }
+    TEST(mem_buffer_sgetc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        streams::producer_consumer_buffer<uint8_t> buf = create_producer_consumer_buffer_with_data(s);
+        streambuf_sgetc(buf, s[0]);
+    }
+
+    TEST(string_buffer_bumpc)
+    {
+        std::string s("Hello World");
+        std::vector<char> v(std::begin(s), std::end(s));
+        streams::stringstreambuf buf(s);
+        streambuf_bumpc(buf, v);
+    }
+    TEST(wstring_buffer_bumpc)
+    {
+        utility::string_t s(U("Hello World"));
+        std::vector<utility::char_t> v(std::begin(s), std::end(s));
+        streams::wstringstreambuf buf(s);
+        streambuf_bumpc(buf, v);
+    }
+    TEST(charptr_buffer_bumpc)
+    {
+        uint8_t chars[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(chars), std::end(chars));
+        rawptr_buffer<uint8_t> buf(chars, sizeof(chars), std::ios::in);
+        streambuf_bumpc(buf, s);
+    }
+    TEST(bytevec_buffer_bumpc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        container_buffer<std::vector<uint8_t>> buf(s);
+        streambuf_bumpc(buf, s);
+    }
+
+    TEST(mem_buffer_bumpc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        streams::producer_consumer_buffer<uint8_t> buf = create_producer_consumer_buffer_with_data(s);
+        streambuf_bumpc(buf, s);
+    }
+
+    TEST(string_buffer_sbumpc)
+    {
+        std::string s("Hello World");
+        std::vector<char> v(std::begin(s), std::end(s));
+        streams::stringstreambuf buf(s);
+        streambuf_sbumpc(buf, v);
+    }
+    TEST(wstring_buffer_sbumpc)
+    {
+        utility::string_t s(U("Hello World"));
+        std::vector<utility::char_t> v(std::begin(s), std::end(s));
+        streams::wstringstreambuf buf(s);
+        streambuf_sbumpc(buf, v);
+    }
+    TEST(charptr_buffer_sbumpc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        rawptr_buffer<uint8_t> buf(data, sizeof(data), std::ios::in);
+        streambuf_sbumpc(buf, s);
+    }
+    TEST(bytevec_buffer_sbumpc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        container_buffer<std::vector<uint8_t>> buf(s);
+        streambuf_sbumpc(buf, s);
+    }
+
+    TEST(mem_buffer_sbumpc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        streams::producer_consumer_buffer<uint8_t> buf = create_producer_consumer_buffer_with_data(s);
+        streambuf_sbumpc(buf, s);
+    }
+    TEST(string_buffer_nextc)
+    {
+        std::string s("Hello World");
+        std::vector<char> v(std::begin(s), std::end(s));
+        streams::stringstreambuf buf(s);
+        streambuf_nextc(buf, v);
+    }
+    TEST(wstring_buffer_nextc)
+    {
+        utility::string_t s(U("Hello World"));
+        std::vector<utility::char_t> v(std::begin(s), std::end(s));
+        streams::wstringstreambuf buf(s);
+        streambuf_nextc(buf, v);
+    }
+    TEST(charptr_buffer_nextc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        rawptr_buffer<uint8_t> buf(data, sizeof(data), std::ios::in);
+        streambuf_nextc(buf, s);
+    }
+    TEST(bytevec_buffer_nextc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        container_buffer<std::vector<uint8_t>> buf(s);
+        streambuf_nextc(buf, s);
+    }
+    TEST(mem_buffer_nextc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        streams::producer_consumer_buffer<uint8_t> buf = create_producer_consumer_buffer_with_data(s);
+        streambuf_nextc(buf, s);
+    }
+
+    TEST(string_buffer_ungetc)
+    {
+        std::string s("Hello World");
+        std::vector<char> v(std::begin(s), std::end(s));
+        streams::stringstreambuf buf(s);
+        streambuf_ungetc(buf, v);
+    }
+    TEST(wstring_buffer_ungetc)
+    {
+        utility::string_t s(U("Hello World"));
+        std::vector<utility::char_t> v(std::begin(s), std::end(s));
+        streams::wstringstreambuf buf(s);
+        streambuf_ungetc(buf, v);
+    }
+    TEST(charptr_buffer_ungetc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        rawptr_buffer<uint8_t> buf(data, sizeof(data), std::ios::in);
+        streambuf_ungetc(buf, s);
+    }
+    TEST(bytevec_buffer_ungetc)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        container_buffer<std::vector<uint8_t>> buf(s);
+        streambuf_ungetc(buf, s);
+    }
+
+    TEST(string_buffer_getn)
+    {
+        std::string s("Hello World");
+        std::vector<char> v(std::begin(s), std::end(s));
+        streams::stringstreambuf buf(s);
+        streambuf_getn(buf, v);
+    }
+    TEST(wstring_buffer_getn)
+    {
+        utility::string_t s(U("Hello World"));
+        std::vector<utility::char_t> v(std::begin(s), std::end(s));
+        streams::wstringstreambuf buf(s);
+        streambuf_getn(buf, v);
+    }
+    TEST(charptr_buffer_getn)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        rawptr_buffer<uint8_t> buf(data, sizeof(data), std::ios::in);
+        streambuf_getn(buf, s);
+    }
+    TEST(bytevec_buffer_getn)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        container_buffer<std::vector<uint8_t>> buf(s);
+        streambuf_getn(buf, s);
+    }
+    TEST(mem_buffer_getn)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        streams::producer_consumer_buffer<uint8_t> buf = create_producer_consumer_buffer_with_data(s);
+        streambuf_getn(buf, s);
+    }
+
+    TEST(string_buffer_acquire_release)
+    {
+        std::string s("Hello World");
+        std::vector<char> v(std::begin(s), std::end(s));
+        streams::stringstreambuf buf(s);
+        streambuf_acquire_release(buf, v);
+    }
+    TEST(wstring_buffer_acquire_release)
+    {
+        utility::string_t s(U("Hello World"));
+        std::vector<utility::char_t> v(std::begin(s), std::end(s));
+        streams::wstringstreambuf buf(s);
+        streambuf_acquire_release(buf, v);
+    }
+    TEST(charptr_buffer_acquire_release)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        rawptr_buffer<uint8_t> buf(data, sizeof(data), std::ios::in);
+        streambuf_acquire_release(buf, s);
+    }
+    TEST(bytevec_buffer_acquire_release)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        container_buffer<std::vector<uint8_t>> buf(s);
+        streambuf_acquire_release(buf, s);
+    }
+    TEST(mem_buffer_acquire_release)
+    {
+        uint8_t data[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
+        std::vector<uint8_t> s(std::begin(data), std::end(data));
+        streams::producer_consumer_buffer<uint8_t> buf = create_producer_consumer_buffer_with_data(s);
+        streambuf_acquire_release(buf, s);
+    }
+    TEST(string_buffer_seek_read)
+    {
+        std::string s("Hello World");
+        std::vector<char> v(std::begin(s), std::end(s));
+        streams::stringstreambuf buf(s);
+        streambuf_seek_read(buf);
+    }
+
+    TEST(mem_buffer_putn_getn)
+    {
+        streams::producer_consumer_buffer<uint8_t> buf;
+        streambuf_putn_getn(buf);
+    }
+
+    TEST(mem_buffer_acquire_alloc)
+    {
+        streams::producer_consumer_buffer<uint8_t> buf;
+        streambuf_acquire_alloc(buf);
+    }
+
+    TEST(string_buffer_close)
+    {
+        streams::stringstreambuf buf;
+        streambuf_close(buf);
+    }
+    TEST(wstring_buffer_close)
+    {
+        streams::wstringstreambuf buf;
+        streambuf_close(buf);
+    }
+    TEST(bytevec_buffer_close)
+    {
+        container_buffer<std::vector<uint8_t>> buf;
+        streambuf_close(buf);
+    }
+    TEST(mem_buffer_close)
+    {
+        streams::producer_consumer_buffer<uint8_t> buf;
+        streambuf_close(buf);
+    }
+
+    TEST(mem_buffer_close_read_with_pending_read)
+    {
+        streams::producer_consumer_buffer<uint8_t> buf;
+        streambuf_close_read_with_pending_read(buf);
+    }
+
+    TEST(mem_buffer_close_write_with_pending_read)
+    {
+        streams::producer_consumer_buffer<uint8_t> buf;
+        streambuf_close_write_with_pending_read(buf);
+    }
+
+    TEST(mem_buffer_close_parallel)
+    {
+        streams::producer_consumer_buffer<uint8_t> buf;
+        streambuf_close_parallel(buf);
+    }
+
+    TEST(mem_buffer_close_destroy)
+    {
+        std::vector<pplx::task<void>> taskVector;
+
+        for (int i = 0; i < 1000; i++)
+        {
+            streams::producer_consumer_buffer<uint8_t> buf;
+            taskVector.push_back(buf.close());
+        }
+
+        pplx::when_all(std::begin(taskVector), std::end(taskVector)).wait();
+    }
+
+    TEST(string_buffer_ctor)
+    {
+        std::string src("abcdef ghij");
+        auto instream = streams::stringstream::open_istream(src);
+
+        streams::stringstreambuf sbuf;
+        auto outstream = sbuf.create_ostream();
+
+        for (;;)
+        {
+            const int count = 4;
+            char temp[count];
+            streams::rawptr_buffer<char> buf1(temp, count);
+            streams::rawptr_buffer<char> buf2(temp, count, std::ios::in);
+            auto size = instream.read(buf1, count).get();
+            VERIFY_IS_TRUE(size <= count);
+            VERIFY_ARE_EQUAL(size, outstream.write(buf2, size).get());
+
+            if (size != count) break;
+        }
+
+        auto& dest = sbuf.collection();
+        VERIFY_ARE_EQUAL(src, dest);
+    }
+
+    TEST(vec_buffer_ctor)
+    {
+        std::string srcstr("abcdef ghij");
+        std::vector<uint8_t> src(begin(srcstr), end(srcstr));
+        auto instream = streams::bytestream::open_istream(src);
+
+        container_buffer<std::vector<uint8_t>> sbuf;
+        auto outstream = sbuf.create_ostream();
+
+        for (;;)
+        {
+            const int count = 4;
+            uint8_t temp[count];
+            streams::rawptr_buffer<uint8_t> buf1(temp, count);
+            streams::rawptr_buffer<uint8_t> buf2(temp, count, std::ios::in);
+            auto size = instream.read(buf1, count).get();
+            VERIFY_IS_TRUE(size <= count);
+            VERIFY_ARE_EQUAL(size, outstream.write(buf2, size).get());
+
+            if (size != count) break;
+        }
+
+        auto& dest = sbuf.collection();
+        VERIFY_ARE_EQUAL(src, dest);
+    }
+
+    TEST(charptr_buffer_ctor_1)
+    {
+        char chars[] = {'a', 'b', 'c', 'd', 'e', 'f', ' ', 'g', 'h', 'i', 'j'};
+        auto instream = streams::rawptr_stream<char>::open_istream(chars, sizeof(chars));
+
+        stringstreambuf sbuf;
+        auto outstream = sbuf.create_ostream();
+
+        for (;;)
+        {
+            const int count = 4;
+            char temp[count];
+            streams::rawptr_buffer<char> buf1(temp, count);
+            streams::rawptr_buffer<char> buf2(temp, count, std::ios::in);
+            auto size = instream.read(buf1, count).get();
+            VERIFY_IS_TRUE(size <= count);
+            VERIFY_ARE_EQUAL(size, outstream.write(buf2, size).get());
+
+            if (size != count) break;
+        }
+
+        auto& dest = sbuf.collection();
+        VERIFY_ARE_EQUAL(memcmp(chars, &(dest)[0], sizeof(chars)), 0);
+    }
+
+    TEST(charptr_buffer_ctor_2)
+    {
+        char chars[] = {'a', 'b', 'c', 'd', 'e', 'f', ' ', 'g', 'h', 'i', 'j'};
+        auto instream = streams::rawptr_stream<char>::open_istream(chars, sizeof(chars));
+
+        stringstreambuf sbuf;
+        auto outstream = sbuf.create_ostream();
+
+        for (;;)
+        {
+            const int count = 4;
+            char temp[count];
+            streams::rawptr_buffer<char> buf1(temp, count);
+            streams::rawptr_buffer<char> buf2(temp, count, std::ios::in);
+            auto size = instream.read(buf1, count).get();
+            VERIFY_IS_TRUE(size <= count);
+            VERIFY_ARE_EQUAL(size, outstream.write(buf2, size).get());
+
+            if (size != count) break;
+        }
+
+        auto& dest = sbuf.collection();
+        VERIFY_ARE_EQUAL(memcmp(chars, &(dest)[0], sizeof(chars)), 0);
+    }
+
+    TEST(charptr_buffer_ctor_3)
+    {
+        char chars[128];
+        memset(chars, 0, sizeof(chars));
+
+        rawptr_buffer<char> buf(chars, sizeof(chars));
+
+        auto outstream = buf.create_ostream();
+
+        auto t1 = outstream.print("Hello ");
+        auto t2 = outstream.print(10);
+        auto t3 = outstream.print(" Again!");
+        (t1 && t2 && t3).wait();
+
+        std::string result(chars);
+
+        VERIFY_ARE_EQUAL(result, "Hello 10 Again!");
+    }
+
+    TEST(validate_stream_mode)
+    {
+        VERIFY_THROWS(concurrency::streams::container_buffer<std::vector<char>>(std::ios::in | std::ios::out),
+                      std::invalid_argument);
+        std::vector<char> vc;
+        VERIFY_THROWS(concurrency::streams::container_buffer<std::vector<char>>(vc, std::ios::in | std::ios::out),
+                      std::invalid_argument);
+    }
+
+    TEST(write_stream_test_1)
+    {
+        char chars[128];
+        memset(chars, 0, sizeof(chars));
+
+        auto stream = streams::rawptr_stream<char>::open_ostream(chars, sizeof(chars));
+
+        std::vector<uint8_t> vect;
+
+        for (char ch = 'a'; ch <= 'z'; ch++)
+        {
+            vect.push_back(ch);
+        }
+
+        size_t vsz = vect.size();
+
+        concurrency::streams::container_stream<std::vector<uint8_t>>::buffer_type txtbuf(std::move(vect),
+                                                                                         std::ios_base::in);
+
+        VERIFY_ARE_EQUAL(stream.write(txtbuf, vsz).get(), vsz);
+        VERIFY_ARE_EQUAL(strcmp(chars, "abcdefghijklmnopqrstuvwxyz"), 0);
+
+        auto close = stream.close();
+
+        VERIFY_IS_TRUE(close.is_done());
+    }
+
+    TEST(mem_buffer_large_data)
+    {
+        // stream large amounts of data
+        // If the stream stores all the data then we will run out of VA space!
+        streams::producer_consumer_buffer<char> membuf;
+
+        const size_t size = 4 * 1024 * 1024; // 4 MB
+        char* ptr = new char[size];
+
+        // stream 4 GB
+        for (size_t i = 0; i < 1024; i++)
+        {
+            // Fill some random positions in the buffer
+            ptr[i + 0] = 'a';
+            ptr[i + 100] = 'b';
+
+            VERIFY_ARE_EQUAL(size, membuf.putn_nocopy(ptr, size).get());
+
+            // overwrite the values in ptr
+            ptr[i + 0] = 'c';
+            ptr[i + 100] = 'd';
+
+            VERIFY_ARE_EQUAL(size, membuf.getn(ptr, size).get());
+
+            VERIFY_ARE_EQUAL(ptr[i + 0], 'a');
+            VERIFY_ARE_EQUAL(ptr[i + 100], 'b');
+        }
+
+        delete[] ptr;
+    }
+
+#ifdef _WIN32
+
+    class ISequentialStream_bridge
+#if defined(__cplusplus_winrt)
+        : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+                                              ISequentialStream>
+#endif
+    {
+    public:
+        ISequentialStream_bridge(streambuf<char> buf) : m_buffer(buf) {}
+
+        // ISequentialStream implementation
+        virtual HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead)
+        {
+            size_t count = m_buffer.getn((char*)pv, (size_t)cb).get();
+            if (pcbRead != nullptr) *pcbRead = (ULONG)count;
+            return S_OK;
+        }
+
+        virtual HRESULT STDMETHODCALLTYPE Write(const void* pv, ULONG cb, ULONG* pcbWritten)
+        {
+            size_t count = m_buffer.putn_nocopy((const char*)pv, (size_t)cb).get();
+            if (pcbWritten != nullptr) *pcbWritten = (ULONG)count;
+            return S_OK;
+        }
+
+    private:
+        streambuf<char> m_buffer;
+    };
+
+    template<typename _StreamBufferType>
+    void IStreamTest1()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        std::string text = "This is a test";
+        size_t len = text.size();
+
+        ULONG pcbWritten = 0;
+
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text[0], (ULONG)text.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(text.size(), pcbWritten);
+
+        text = " - but this is not";
+        len += text.size();
+        pcbWritten = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text[0], (ULONG)text.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(text.size(), pcbWritten);
+
+        char buf[128];
+        memset(buf, 0, sizeof(buf));
+
+        rbuf.getn((char*)buf, len).wait();
+
+        VERIFY_ARE_EQUAL(0, strcmp("This is a test - but this is not", buf));
+
+        rbuf.close().get();
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(membuf_IStreamTest1) { IStreamTest1<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest2()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        std::string text = "This is a test";
+        size_t len = text.size();
+
+        ULONG pcbWritten = 0;
+
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text[0], (ULONG)text.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(text.size(), pcbWritten);
+
+        text = " - but this is not";
+        len += text.size();
+        pcbWritten = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text[0], (ULONG)text.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(text.size(), pcbWritten);
+
+        char buf[128];
+        memset(buf, 0, sizeof(buf));
+
+        rbuf.getn((char*)buf, len).wait();
+
+        VERIFY_ARE_EQUAL(0, strcmp("This is a test - but this is not", buf));
+
+        rbuf.close().get();
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(membuf_IStreamTest2) { IStreamTest2<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest3()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        std::string text1 = "This is a test";
+        size_t len1 = text1.size();
+        std::string text2 = " - but this is not";
+        size_t len2 = text2.size();
+
+        char buf[128];
+        memset(buf, 0, sizeof(buf));
+
+        // The read happens before the write.
+
+        auto read = rbuf.getn((char*)buf, len1 + len2);
+
+        ULONG pcbWritten = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text1[0], (ULONG)text1.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(len1, pcbWritten);
+        pcbWritten = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text2[0], (ULONG)text2.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(len2, pcbWritten);
+
+        read.wait();
+
+        // We may or may not read data from both writes here. It depends on the
+        // stream in use. Both are correct behaviors.
+        if (read.get() == len1 + len2)
+            VERIFY_ARE_EQUAL(0, strcmp("This is a test - but this is not", (char*)buf));
+        else
+            VERIFY_ARE_EQUAL(0, strcmp("This is a test", (char*)buf));
+
+        rbuf.close().get();
+    }
+
+    TEST(membuf_IStreamTest3) { IStreamTest3<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest4()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        std::string text1 = "This is a test";
+        size_t len1 = text1.size();
+        std::string text2 = " - but this is not";
+        size_t len2 = text2.size();
+
+        char buf1[128];
+        memset(buf1, 0, sizeof(buf1));
+        char buf2[128];
+        memset(buf2, 0, sizeof(buf2));
+
+        // The read happens before the write.
+
+        auto read1 = rbuf.getn(buf1, 8);
+        auto read2 = rbuf.getn(buf2, 12);
+
+        ULONG pcbWritten = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text1[0], (ULONG)text1.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(len1, pcbWritten);
+        pcbWritten = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text2[0], (ULONG)text2.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(len2, pcbWritten);
+
+        VERIFY_ARE_EQUAL(8u, read1.get());
+        // Different results depending on stream implementation. Both correct.
+        VERIFY_IS_TRUE(read2.get() == 12u || read2.get() == 6u);
+
+        VERIFY_ARE_EQUAL(0, strcmp("This is ", (char*)buf1));
+        if (read2.get() == 12u)
+            VERIFY_ARE_EQUAL(0, strcmp("a test - but", (char*)buf2));
+        else
+            VERIFY_ARE_EQUAL(0, strcmp("a test", (char*)buf2));
+
+        rbuf.close().get();
+    }
+
+    TEST(membuf_IStreamTest4) { IStreamTest4<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest5()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        std::string text1 = "This is a test";
+        size_t len1 = text1.size();
+
+        char buf1[128];
+        memset(buf1, 0, sizeof(buf1));
+
+        // The read happens before the write.
+
+        auto read1 = rbuf.getn(buf1, 28);
+
+        ULONG pcbWritten = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text1[0], (ULONG)text1.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(len1, pcbWritten);
+
+        // We close the stream buffer before enough bytes have been written.
+
+        rbuf.close().get();
+
+        VERIFY_ARE_EQUAL(len1, read1.get());
+        VERIFY_ARE_EQUAL(len1, strlen((char*)buf1));
+        VERIFY_ARE_EQUAL(0, strcmp("This is a test", (char*)buf1));
+    }
+
+    TEST(membuf_IStreamTest5) { IStreamTest5<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest6()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        std::string text1 = "abcdefghijklmnopqrstuvwxyz";
+        size_t len1 = text1.size();
+
+        ULONG pcbWritten = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text1[0], (ULONG)text1.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(len1, pcbWritten);
+
+        bool validated = true;
+        for (int expected = 'a'; expected <= 'z'; expected++)
+        {
+            validated = validated && (expected == rbuf.bumpc().get());
+        }
+
+        VERIFY_IS_TRUE(validated);
+        rbuf.close().get();
+    }
+
+    TEST(membuf_IStreamTest6) { IStreamTest6<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest7()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        std::vector<task<int>> reads;
+
+        for (int i = 0; i < 26; i++)
+        {
+            reads.push_back(rbuf.bumpc());
+        }
+
+        std::string text1 = "abcdefghijklmnopqrstuvwxyz";
+        size_t len1 = text1.size();
+
+        ULONG pcbWritten = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text1[0], (ULONG)text1.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(len1, pcbWritten);
+
+        bool validated = true;
+        for (int i = 0; i < 26; i++)
+        {
+            int expected = 'a' + i;
+            validated = validated && (expected == reads[i].get());
+        }
+
+        VERIFY_IS_TRUE(validated);
+        rbuf.close().get();
+    }
+
+    TEST(membuf_IStreamTest7) { IStreamTest7<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest8()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        std::string text1 = "This is a test";
+        size_t len1 = text1.size();
+
+        char buf1[128];
+        memset(buf1, 0, sizeof(buf1));
+
+        // The read happens before the write.
+
+        auto read1 = rbuf.getn(buf1, 28);
+        auto read2 = rbuf.getn(buf1, 8);
+
+        ULONG pcbWritten = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Write((const void*)&text1[0], (ULONG)text1.size(), &pcbWritten));
+        VERIFY_ARE_EQUAL(len1, pcbWritten);
+
+        // We close the stream buffer before enough bytes have been written.
+        // Make sure that the first read results in fewer bytes than requested
+        // and that the second read returns -1.
+
+        rbuf.close(std::ios_base::out).get();
+
+        VERIFY_ARE_EQUAL(len1, read1.get());
+        VERIFY_ARE_EQUAL(-0, (int)read2.get());
+        VERIFY_ARE_EQUAL(len1, strlen((char*)buf1));
+        VERIFY_ARE_EQUAL(0, strcmp("This is a test", (char*)buf1));
+    }
+
+    TEST(membuf_IStreamTest8) { IStreamTest8<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest9()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        VERIFY_ARE_EQUAL((int)'a', rbuf.putc('a').get());
+        VERIFY_ARE_EQUAL((int)'n', rbuf.putc('n').get());
+        VERIFY_ARE_EQUAL((int)'q', rbuf.putc('q').get());
+        VERIFY_ARE_EQUAL((int)'s', rbuf.putc('s').get());
+
+        VERIFY_ARE_EQUAL(4u, rbuf.in_avail());
+
+        std::string chars(32, '\0');
+        ULONG pcbRead = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Read(&chars[0], 4, &pcbRead));
+        VERIFY_ARE_EQUAL(4u, pcbRead);
+
+        VERIFY_ARE_EQUAL("anqs", chars.c_str());
+
+        rbuf.close().get();
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(membuf_IStreamTest9) { IStreamTest9<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest10()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        std::string text(128, '\0');
+        strcpy_s(&text[0], 128, "This is a test");
+        size_t len1 = strlen(&text[0]);
+
+        VERIFY_ARE_EQUAL(len1, rbuf.putn_nocopy(&text[0], len1).get());
+
+        strcpy_s(&text[0], 128, " - but this is not");
+        size_t len2 = strlen(&text[0]);
+
+        VERIFY_ARE_EQUAL(len2, rbuf.putn_nocopy(&text[0], len2).get());
+
+        VERIFY_ARE_EQUAL(len1 + len2, rbuf.in_avail());
+
+        std::string chars(128, '\0');
+        size_t was_available = rbuf.in_avail();
+        ULONG pcbRead = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Read(&chars[0], (ULONG)was_available, &pcbRead));
+        VERIFY_ARE_EQUAL(was_available, pcbRead);
+
+        VERIFY_ARE_EQUAL("This is a test - but this is not", chars.c_str());
+
+        rbuf.close().get();
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(membuf_IStreamTest10) { IStreamTest10<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest11()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        char ch = 'a';
+
+        auto seg2 = [&ch](int val) { return (val != -1) && (++ch <= 'z'); };
+        auto seg1 = [=, &ch, &rbuf]() { return rbuf.putc(ch).then(seg2); };
+
+        pplx::details::_do_while(seg1).wait();
+
+        VERIFY_ARE_EQUAL(26u, rbuf.in_avail());
+
+        std::string chars(128, '\0');
+        size_t was_available = rbuf.in_avail();
+        ULONG pcbRead = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Read(&chars[0], (ULONG)was_available, &pcbRead));
+        VERIFY_ARE_EQUAL(was_available, pcbRead);
+
+        VERIFY_ARE_EQUAL("abcdefghijklmnopqrstuvwxyz", chars.c_str());
+
+        rbuf.close().get();
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(membuf_IStreamTest11) { IStreamTest11<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest12()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        std::string text(128, '\0');
+        strcpy_s(&text[0], 128, "This is a test");
+        size_t len1 = strlen(&text[0]);
+
+        VERIFY_ARE_EQUAL(len1, rbuf.putn_nocopy(&text[0], len1).get());
+
+        strcpy_s(&text[0], 128, " - but this is not");
+        size_t len2 = strlen(&text[0]);
+
+        VERIFY_ARE_EQUAL(len2, rbuf.putn_nocopy(&text[0], len2).get());
+
+        VERIFY_ARE_EQUAL(len1 + len2, rbuf.in_avail());
+
+        std::string chars(128, '\0');
+        size_t was_available = rbuf.in_avail();
+        ULONG pcbRead = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Read(&chars[0], (ULONG)was_available, &pcbRead));
+        VERIFY_ARE_EQUAL(was_available, pcbRead);
+
+        VERIFY_ARE_EQUAL("This is a test - but this is not", chars.c_str());
+
+        rbuf.close().get();
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(membuf_IStreamTest12) { IStreamTest12<streams::producer_consumer_buffer<char>>(); }
+
+    template<typename _StreamBufferType>
+    void IStreamTest13()
+    {
+        _StreamBufferType rbuf;
+        ISequentialStream_bridge stream(rbuf);
+
+        streams::basic_ostream<char> os(rbuf);
+
+        auto a = os.print("This is a test");
+        auto b = os.print(" ");
+        auto c = os.print("- but this is not");
+        (a && b && c).wait();
+
+        VERIFY_ARE_EQUAL(32u, rbuf.in_avail());
+
+        std::string chars(128, '\0');
+        size_t was_available = rbuf.in_avail();
+        ULONG pcbRead = 0;
+        VERIFY_ARE_EQUAL(S_OK, stream.Read(&chars[0], (ULONG)was_available, &pcbRead));
+        VERIFY_ARE_EQUAL(was_available, pcbRead);
+
+        VERIFY_ARE_EQUAL("This is a test - but this is not", chars.c_str());
+
+        os.close().get();
+
+        // The read end should still be open
+        VERIFY_IS_TRUE(rbuf.is_open());
+
+        // close the read end
+        rbuf.close(std::ios_base::in).get();
+
+        // Now the buffer should no longer be open
+        VERIFY_IS_FALSE(rbuf.is_open());
+    }
+
+    TEST(membuf_IStreamTest13) { IStreamTest13<streams::producer_consumer_buffer<char>>(); }
+#endif
+
+    TEST(producer_consumer_buffer_flush_1)
+    {
+        streams::producer_consumer_buffer<char> rwbuf;
+
+        VERIFY_IS_TRUE(rwbuf.is_open());
+        VERIFY_IS_TRUE(rwbuf.can_read());
+        VERIFY_IS_TRUE(rwbuf.can_write());
+
+        char buf1[128], buf2[128];
+        memset(buf1, 0, sizeof(buf1));
+        memset(buf2, 0, sizeof(buf2));
+
+        // The read happens before the write.
+
+        auto read1 = rwbuf.getn(buf1, 128);
+        auto read2 = rwbuf.getn(buf2, 128);
+
+        std::string text1 = "This is a test";
+        size_t len1 = text1.size();
+        VERIFY_ARE_EQUAL(rwbuf.putn_nocopy(&text1[0], len1).get(), len1);
+        rwbuf.sync().wait();
+
+        std::string text2 = "- but this is not";
+        size_t len2 = text2.size();
+        VERIFY_ARE_EQUAL(rwbuf.putn_nocopy(&text2[0], len2).get(), len2);
+        rwbuf.sync().wait();
+
+        VERIFY_ARE_EQUAL(read1.get(), len1);
+        VERIFY_ARE_EQUAL(read2.get(), len2);
+
+        rwbuf.close().get();
+    }
+
+    TEST(producer_consumer_buffer_flush_2)
+    {
+        streams::producer_consumer_buffer<char> rwbuf;
+
+        VERIFY_IS_TRUE(rwbuf.is_open());
+        VERIFY_IS_TRUE(rwbuf.can_read());
+        VERIFY_IS_TRUE(rwbuf.can_write());
+
+        // The read happens after the write.
+
+        std::string text1 = "This is a test";
+        std::string text2 = "- but this is not";
+        size_t len1 = text1.size();
+        size_t len2 = text2.size();
+        VERIFY_ARE_EQUAL(rwbuf.putn_nocopy(&text1[0], len1).get(), len1);
+        VERIFY_ARE_EQUAL(rwbuf.putn_nocopy(&text2[0], len2).get(), len2);
+        rwbuf.sync().wait();
+
+        char buf1[128], buf2[128];
+        memset(buf1, 0, sizeof(buf1));
+        memset(buf2, 0, sizeof(buf2));
+
+        auto read1 = rwbuf.getn(buf1, 128);
+
+        VERIFY_ARE_EQUAL(read1.get(), len1 + len2);
+
+        rwbuf.close().get();
+    }
+
+    TEST(producer_consumer_buffer_flush_3)
+    {
+        streams::producer_consumer_buffer<char> rwbuf;
+
+        VERIFY_IS_TRUE(rwbuf.is_open());
+        VERIFY_IS_TRUE(rwbuf.can_read());
+        VERIFY_IS_TRUE(rwbuf.can_write());
+
+        // The read happens before the write.
+
+        char buf1[128], buf2[128];
+        memset(buf1, 0, sizeof(buf1));
+        memset(buf2, 0, sizeof(buf2));
+
+        auto read1 = rwbuf.getn(buf1, 128);
+        auto read2 = rwbuf.getn(buf2, 128);
+
+        for (char c = 'a'; c <= 'z'; ++c)
+            rwbuf.putc(c);
+        rwbuf.sync().wait();
+        for (char c = 'a'; c <= 'z'; ++c)
+            rwbuf.putc(c);
+
+        VERIFY_ARE_EQUAL(read1.get(), 26);
+
+        rwbuf.close().get();
+
+        VERIFY_ARE_EQUAL(read2.get(), 26);
+    }
+
+    TEST(producer_consumer_buffer_flush_4)
+    {
+        streams::producer_consumer_buffer<char> rwbuf;
+
+        VERIFY_IS_TRUE(rwbuf.is_open());
+        VERIFY_IS_TRUE(rwbuf.can_read());
+        VERIFY_IS_TRUE(rwbuf.can_write());
+
+        // The read happens after the write.
+
+        for (char c = 'a'; c <= 'z'; ++c)
+            rwbuf.putc(c);
+        rwbuf.sync().wait();
+
+        char buf1[128], buf2[128];
+        memset(buf1, 0, sizeof(buf1));
+        memset(buf2, 0, sizeof(buf2));
+
+        auto read1 = rwbuf.getn(buf1, 20);
+        auto read2 = rwbuf.getn(buf1, 128);
+
+        VERIFY_ARE_EQUAL(read1.get(), 20);
+        VERIFY_ARE_EQUAL(read2.get(), 6);
+
+        rwbuf.close().get();
+    }
+
+    TEST(producer_consumer_buffer_flush_5)
+    {
+        streams::producer_consumer_buffer<char> rwbuf;
+
+        VERIFY_IS_TRUE(rwbuf.is_open());
+        VERIFY_IS_TRUE(rwbuf.can_read());
+        VERIFY_IS_TRUE(rwbuf.can_write());
+
+        // The read happens before the write.
+
+        pplx::task<int> buf1[128];
+
+        for (int i = 0; i < 128; ++i)
+        {
+            buf1[i] = rwbuf.bumpc();
+        }
+
+        for (char c = 'a'; c <= 'z'; ++c)
+            rwbuf.putc(c);
+        rwbuf.sync().wait();
+
+        for (int i = 0; i < 26; ++i)
+        {
+            VERIFY_ARE_EQUAL('a' + i, buf1[i].get());
+        }
+        for (int i = 26; i < 128; ++i)
+        {
+            VERIFY_IS_FALSE(buf1[i].is_done());
+        }
+        rwbuf.close().get();
+    }
+
+    TEST(producer_consumer_buffer_flush_6)
+    {
+        streams::producer_consumer_buffer<char> rwbuf;
+
+        VERIFY_IS_TRUE(rwbuf.is_open());
+        VERIFY_IS_TRUE(rwbuf.can_read());
+        VERIFY_IS_TRUE(rwbuf.can_write());
+
+        // The read happens after the write.
+
+        for (char c = 'a'; c <= 'z'; ++c)
+            rwbuf.putc(c);
+        rwbuf.sync().wait();
+
+        pplx::task<int> buf1[128];
+
+        for (int i = 0; i < 128; ++i)
+        {
+            buf1[i] = rwbuf.bumpc();
+        }
+
+        for (int i = 0; i < 26; ++i)
+        {
+            VERIFY_IS_TRUE(buf1[i].is_done());
+        }
+        for (int i = 26; i < 128; ++i)
+        {
+            VERIFY_IS_FALSE(buf1[i].is_done());
+        }
+        rwbuf.close().get();
+    }
+
+    TEST(producer_consumer_buffer_close_reader_early)
+    {
+        streams::producer_consumer_buffer<char> rwbuf;
+
+        VERIFY_IS_TRUE(rwbuf.is_open());
+        VERIFY_IS_TRUE(rwbuf.can_read());
+        VERIFY_IS_TRUE(rwbuf.can_write());
+
+        rwbuf.close(std::ios::in).wait();
+
+        // Even though we have closed for read, we should
+        // still be able to write.
+
+        auto size = rwbuf.in_avail();
+
+        for (char c = 'a'; c <= 'z'; ++c)
+            VERIFY_ARE_EQUAL((int)c, rwbuf.putc(c).get());
+
+        VERIFY_ARE_EQUAL(size, rwbuf.in_avail());
+
+        std::string text1 = "This is a test";
+        size_t len1 = text1.size();
+        VERIFY_ARE_EQUAL(rwbuf.putn_nocopy(&text1[0], len1).get(), len1);
+
+        VERIFY_ARE_EQUAL(size, rwbuf.in_avail());
+
+        rwbuf.close().get();
+    }
+
+    TEST(container_buffer_exception_propagation)
+    {
+        struct MyException
+        {
+        };
+        {
+            streams::stringstreambuf rwbuf(std::string("this is the test"));
+            rwbuf.close(std::ios::out, std::make_exception_ptr(MyException())).wait();
+            char buffer[100];
+            VERIFY_ARE_EQUAL(rwbuf.getn(buffer, 100).get(), 16);
+            VERIFY_THROWS(rwbuf.getn(buffer, 100).get(), MyException);
+            VERIFY_THROWS(rwbuf.getc().get(), MyException);
+            VERIFY_IS_FALSE(rwbuf.exception() == nullptr);
+        }
+        {
+            streams::stringstreambuf rwbuf(std::string("this is the test"));
+            rwbuf.close(std::ios::in, std::make_exception_ptr(MyException()));
+            char buffer[100];
+            VERIFY_THROWS(rwbuf.getn(buffer, 100).get(), MyException);
+            VERIFY_THROWS(rwbuf.getc().get(), MyException);
+            VERIFY_IS_FALSE(rwbuf.exception() == nullptr);
+        }
+
+        {
+            streams::stringstreambuf rwbuf;
+            rwbuf.putn_nocopy("this is the test", 16);
+            rwbuf.close(std::ios::out, std::make_exception_ptr(MyException()));
+            VERIFY_THROWS(rwbuf.putn_nocopy("this is the test", 16).get(), MyException);
+            VERIFY_THROWS(rwbuf.putc('c').get(), MyException);
+            VERIFY_IS_FALSE(rwbuf.exception() == nullptr);
+        }
+    }
+
+    TEST(producer_consumer_buffer_exception_propagation)
+    {
+        struct MyException
+        {
+        };
+        {
+            streams::producer_consumer_buffer<char> rwbuf;
+            rwbuf.putn_nocopy("this is the test", 16);
+            rwbuf.close(std::ios::out, std::make_exception_ptr(MyException()));
+            char buffer[100];
+            VERIFY_ARE_EQUAL(rwbuf.getn(buffer, 100).get(), 16);
+            VERIFY_THROWS(rwbuf.getn(buffer, 100).get(), MyException);
+            VERIFY_THROWS(rwbuf.getc().get(), MyException);
+            VERIFY_IS_FALSE(rwbuf.exception() == nullptr);
+        }
+        {
+            streams::producer_consumer_buffer<char> rwbuf;
+            rwbuf.putn_nocopy("this is the test", 16);
+            rwbuf.close(std::ios::in, std::make_exception_ptr(MyException()));
+            char buffer[100];
+            VERIFY_THROWS(rwbuf.getn(buffer, 100).get(), MyException);
+            VERIFY_THROWS(rwbuf.getc().get(), MyException);
+            VERIFY_IS_FALSE(rwbuf.exception() == nullptr);
+        }
+
+        {
+            streams::producer_consumer_buffer<char> rwbuf;
+            rwbuf.putn_nocopy("this is the test", 16);
+            rwbuf.close(std::ios::out, std::make_exception_ptr(MyException()));
+            VERIFY_THROWS(rwbuf.putn_nocopy("this is the test", 16).get(), MyException);
+            VERIFY_THROWS(rwbuf.putc('c').get(), MyException);
+            VERIFY_IS_FALSE(rwbuf.exception() == nullptr);
+        }
+    }
+
+    TEST(producer_consumer_alloc_after_close)
+    {
+        producer_consumer_buffer<char> buffer;
+        buffer.close().wait();
+        VERIFY_IS_TRUE(buffer.alloc(2) == nullptr);
+
+        buffer = producer_consumer_buffer<char>();
+        buffer.close(std::ios::out);
+        VERIFY_IS_TRUE(buffer.alloc(2) == nullptr);
+    }
+
+    TEST(producer_consumer_acquire_after_close)
+    {
+        char* temp = nullptr;
+        size_t size = 0;
+        producer_consumer_buffer<char> buffer;
+        buffer.close().wait();
+        VERIFY_IS_FALSE(buffer.acquire(temp, size));
+        VERIFY_IS_TRUE(nullptr == temp);
+        VERIFY_ARE_EQUAL(0, size);
+        buffer.release(temp, size);
+
+        buffer = producer_consumer_buffer<char>();
+        buffer.close(std::ios::out);
+        temp = (char*)1;
+        size = 1;
+        VERIFY_IS_TRUE(buffer.acquire(temp, size));
+        VERIFY_IS_TRUE(nullptr == temp);
+        VERIFY_ARE_EQUAL(0, size);
+        buffer.release(temp, size);
+    }
+
+    TEST(create_buffers_inout_error)
+    {
+        VERIFY_THROWS(container_buffer<std::string>(std::ios::in | std::ios::out), std::invalid_argument);
+        VERIFY_THROWS(container_buffer<std::string>("test data", std::ios::in | std::ios::out), std::invalid_argument);
+        char* data = nullptr;
+        VERIFY_THROWS(rawptr_buffer<char>(data, 2, std::ios::in | std::ios::out), std::invalid_argument);
+    }
+
+    TEST(memstream_length)
+    {
+        producer_consumer_buffer<unsigned char> rbuf;
+        auto istr = rbuf.create_istream();
+
+        auto curr = istr.tell();
+        VERIFY_ARE_EQUAL((long long)curr, 0);
+    }
+
+    TEST(buffer_size)
+    {
+        {
+            container_buffer<std::string> buf("test data");
+            VERIFY_IS_TRUE(buf.has_size());
+            VERIFY_ARE_EQUAL(buf.size(), 9);
+            buf.seekoff(1024, std::ios::beg, std::ios::in);
+            VERIFY_ARE_EQUAL(buf.size(), 9);
+        }
+        {
+            container_buffer<std::string> buf;
+            VERIFY_IS_TRUE(buf.has_size());
+            VERIFY_ARE_EQUAL(buf.size(), 0);
+            buf.seekoff(1024, std::ios::beg, std::ios::out);
+            VERIFY_ARE_EQUAL(buf.size(), 1024);
+            VERIFY_ARE_EQUAL(buf.collection().size(), 1024);
+        }
+        {
+            producer_consumer_buffer<uint8_t> buf;
+            VERIFY_IS_FALSE(buf.has_size());
+            VERIFY_ARE_EQUAL(buf.size(), 0);
+        }
+    }
+
+    TEST(rawptr_alloc_after_close)
+    {
+        char data[2];
+        rawptr_buffer<char> buffer(&data[0], sizeof(data), std::ios::out);
+        buffer.close().wait();
+        VERIFY_IS_TRUE(buffer.alloc(2) == nullptr);
+
+        buffer = rawptr_buffer<char>(&data[0], sizeof(data), std::ios::out);
+        buffer.close(std::ios::out);
+        VERIFY_IS_TRUE(buffer.alloc(2) == nullptr);
+    }
+
+    TEST(rawptr_alloc_too_large)
+    {
+        char data[4];
+        rawptr_buffer<char> buffer(&data[0], sizeof(data), std::ios::out);
+        VERIFY_IS_TRUE(buffer.alloc(10) == nullptr);
+    }
+
+    TEST(rawptr_buffer_acquire_after_close)
+    {
+        char* temp = nullptr;
+        size_t size = 0;
+        char data[2];
+        rawptr_buffer<char> buffer(&data[0], sizeof(data), std::ios::in);
+        buffer.close().wait();
+        VERIFY_IS_FALSE(buffer.acquire(temp, size));
+        VERIFY_IS_TRUE(nullptr == temp);
+        VERIFY_ARE_EQUAL(0, size);
+        buffer.release(temp, size);
+
+        buffer = rawptr_buffer<char>(nullptr, 0, std::ios::in);
+        temp = (char*)1;
+        size = 1;
+        VERIFY_IS_TRUE(buffer.acquire(temp, size));
+        VERIFY_IS_TRUE(nullptr == temp);
+        VERIFY_ARE_EQUAL(0, size);
+        buffer.release(temp, size);
+    }
+
+    TEST(container_buffer_alloc_after_close)
+    {
+        container_buffer<std::string> buffer;
+        buffer.close().wait();
+        VERIFY_IS_TRUE(buffer.alloc(2) == nullptr);
+
+        buffer = container_buffer<std::string>();
+        buffer.close(std::ios::out);
+        VERIFY_IS_TRUE(buffer.alloc(2) == nullptr);
+    }
+
+    TEST(container_buffer_acquire_after_close)
+    {
+        char* temp = nullptr;
+        size_t size = 0;
+        container_buffer<std::string> buffer("test data");
+        buffer.close().wait();
+        VERIFY_IS_FALSE(buffer.acquire(temp, size));
+        VERIFY_IS_TRUE(nullptr == temp);
+        VERIFY_ARE_EQUAL(0, size);
+        buffer.release(temp, size);
+
+        buffer = container_buffer<std::string>(std::ios::in);
+        temp = (char*)1;
+        size = 1;
+        VERIFY_IS_TRUE(buffer.acquire(temp, size));
+        VERIFY_IS_TRUE(nullptr == temp);
+        VERIFY_ARE_EQUAL(0, size);
+        buffer.release(temp, size);
+    }
+
+    TEST(bytestream_length)
+    {
+        // test byte stream
+        std::string s("12345");
+        auto istr = bytestream::open_istream(s);
+        test_stream_length(istr, s.size());
+    }
+
+    TEST(read_pending_close_with_exception)
+    {
+        producer_consumer_buffer<char> sourceBuf;
+
+        const size_t size = 4;
+        char buf[size];
+        memset(&buf[0], '0', size);
+        auto firstRead = sourceBuf.getn(buf, size);
+        sourceBuf.putc('a').wait();
+
+        sourceBuf.close(std::ios::in | std::ios::out, std::make_exception_ptr(std::runtime_error("test exception")))
+            .wait();
+        VERIFY_ARE_EQUAL(firstRead.get(), 1);
+        VERIFY_ARE_EQUAL(buf[0], 'a');
+        VERIFY_ARE_EQUAL(buf[1], '0');
+        VERIFY_ARE_EQUAL(buf[2], '0');
+        VERIFY_ARE_EQUAL(buf[3], '0');
+
+        VERIFY_THROWS(sourceBuf.getn(buf, size).get(), std::runtime_error);
+        VERIFY_ARE_EQUAL(buf[0], 'a');
+        VERIFY_ARE_EQUAL(buf[1], '0');
+        VERIFY_ARE_EQUAL(buf[2], '0');
+        VERIFY_ARE_EQUAL(buf[3], '0');
+    }
+
+    TEST(close_on_one_head_write)
+    {
+        producer_consumer_buffer<char> sourceBuf;
+        sourceBuf.putc('a').wait();
+
+        auto ostream = sourceBuf.create_ostream();
+
+        ostream.close().wait();
+
+        // Check that the exception is generated by the 'get(),' not the operation.
+        auto t1 = sourceBuf.putc('b');
+        auto t2 = ostream.write('b');
+        VERIFY_ARE_EQUAL(t1.get(), streams::streambuf<char>::traits::eof());
+        VERIFY_THROWS(t2.get(), std::runtime_error);
+        VERIFY_ARE_EQUAL(sourceBuf.getc().get(), 'a');
+    }
+
+    TEST(close_on_one_head_read)
+    {
+        producer_consumer_buffer<char> sourceBuf;
+        sourceBuf.putc('a').wait();
+
+        auto istream = sourceBuf.create_istream();
+
+        istream.close().wait();
+
+        // Check that the exception is generated by the 'get(),' not the operation.
+        auto t1 = sourceBuf.bumpc();
+        auto t2 = istream.read();
+        VERIFY_ARE_EQUAL(t1.get(), streams::streambuf<char>::traits::eof());
+        VERIFY_THROWS(t2.get(), std::runtime_error);
+        VERIFY_ARE_EQUAL(sourceBuf.putc('a').get(), 'a');
+    }
+
+    TEST(close_with_exception_on_one_head_write)
+    {
+        producer_consumer_buffer<char> sourceBuf;
+        sourceBuf.putc('a').wait();
+
+        auto ostream = sourceBuf.create_ostream();
+
+        ostream.close(std::make_exception_ptr(std::invalid_argument("test exception"))).wait();
+
+        // Check that the exception is generated by the 'get(),' not the operation.
+        auto t1 = sourceBuf.putc('b');
+        auto t2 = ostream.write('b');
+        VERIFY_THROWS(t1.get(), std::invalid_argument);
+        VERIFY_THROWS(t2.get(), std::invalid_argument);
+        VERIFY_ARE_EQUAL(sourceBuf.getc().get(), 'a');
+    }
+
+    TEST(close_with_exception_on_one_head_read)
+    {
+        producer_consumer_buffer<char> sourceBuf;
+        sourceBuf.putc('a').wait();
+
+        auto istream = sourceBuf.create_istream();
+
+        istream.close(std::make_exception_ptr(std::invalid_argument("test exception"))).wait();
+
+        // Check that the exception is generated by the 'get(),' not the operation.
+        auto t1 = sourceBuf.bumpc();
+        auto t2 = istream.read();
+        VERIFY_THROWS(t1.get(), std::invalid_argument);
+        VERIFY_THROWS(t2.get(), std::invalid_argument);
+        VERIFY_ARE_EQUAL(sourceBuf.putc('a').get(), 'a');
+    }
+
+    TEST(close_twice)
+    {
+        // This test passes if it does not generate an exception.
+        {
+            producer_consumer_buffer<char> sourceBuf;
+            sourceBuf.close(std::ios::in).wait();
+            sourceBuf.close(std::ios::in).wait();
+        }
+        {
+            producer_consumer_buffer<char> sourceBuf;
+            sourceBuf.close(std::ios::out).wait();
+            sourceBuf.close(std::ios::out).wait();
+        }
+        {
+            producer_consumer_buffer<char> sourceBuf;
+            sourceBuf.close().wait();
+            sourceBuf.close().wait();
+        }
+    }
+}
+
+} // namespace streams
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/streams/ostream_tests.cpp b/Release/tests/functional/streams/ostream_tests.cpp
new file mode 100644 (file)
index 0000000..078337d
--- /dev/null
@@ -0,0 +1,398 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests for async output stream operations.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+using namespace concurrency::streams;
+
+#if defined(__cplusplus_winrt)
+using namespace Windows::Storage;
+#endif
+
+namespace tests
+{
+namespace functional
+{
+namespace streams
+{
+using namespace utility;
+using namespace ::pplx;
+
+//
+// The following two functions will help mask the differences between non-WinRT environments and
+// WinRT: on the latter, a file path is typically not used to open files. Rather, a UI element is used
+// to get a 'StorageFile' reference and you go from there. However, to test the library properly,
+// we need to get a StorageFile reference somehow, and one way to do that is to create all the files
+// used in testing in the Documents folder.
+//
+template<typename _CharType>
+pplx::task<concurrency::streams::basic_ostream<_CharType>> OPENSTR_W(const utility::string_t& name,
+                                                                     std::ios_base::openmode mode = std::ios_base::out)
+{
+#if !defined(__cplusplus_winrt)
+    return concurrency::streams::file_stream<_CharType>::open_ostream(name, mode);
+#else
+    auto file = pplx::create_task(KnownFolders::DocumentsLibrary->CreateFileAsync(
+                                      ref new Platform::String(name.c_str()), CreationCollisionOption::ReplaceExisting))
+                    .get();
+
+    return concurrency::streams::file_stream<_CharType>::open_ostream(file, mode);
+#endif
+}
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4100) // Because of '_Prot' in WinRT builds.
+#endif
+template<typename _CharType>
+pplx::task<concurrency::streams::basic_istream<_CharType>> OPENSTR_R(const utility::string_t& name,
+                                                                     std::ios_base::openmode mode = std::ios_base::in)
+{
+#if !defined(__cplusplus_winrt)
+    return concurrency::streams::file_stream<_CharType>::open_istream(name, mode);
+#else
+    auto file =
+        pplx::create_task(KnownFolders::DocumentsLibrary->GetFileAsync(ref new Platform::String(name.c_str()))).get();
+
+    return concurrency::streams::file_stream<_CharType>::open_istream(file, mode);
+#endif
+}
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+SUITE(ostream_tests)
+{
+    TEST(BasicTest1)
+    {
+        auto open = OPENSTR_W<uint8_t>(U("BasicTest1.txt"));
+        auto basic_stream = open.get();
+        VERIFY_IS_TRUE(basic_stream.can_seek());
+        auto a = basic_stream.print(10);
+        auto b = basic_stream.print("-suffix");
+        (a && b).wait();
+        auto cls = basic_stream.close();
+        cls.get();
+        VERIFY_IS_TRUE(cls.is_done());
+    }
+
+    TEST(BasicTest2)
+    {
+        auto open = OPENSTR_W<uint8_t>(U("BasicTest2.txt"));
+
+        auto cls = open.then([](pplx::task<concurrency::streams::ostream> op) -> pplx::task<void> {
+            auto basic_stream = op.get();
+            auto a = basic_stream.print(10);
+            auto b = basic_stream.print("-suffix");
+            (a && b).wait();
+            return basic_stream.close();
+        });
+
+        cls.get();
+
+        VERIFY_IS_TRUE(cls.is_done());
+    }
+
+    TEST(WriteSingleCharTest2)
+    {
+        auto open = OPENSTR_W<uint8_t>(U("WriteSingleCharStrTest1.txt"));
+        auto stream = open.get();
+
+        VERIFY_IS_TRUE(open.is_done());
+
+        bool elements_equal = true;
+
+        for (uint8_t ch = 'a'; ch <= 'z'; ch++)
+        {
+            elements_equal = elements_equal && (ch == stream.write(ch).get());
+        }
+
+        VERIFY_IS_TRUE(elements_equal);
+
+        auto close = stream.close();
+        close.get();
+
+        VERIFY_IS_TRUE(close.is_done());
+    }
+
+    TEST(WriteBufferTest1)
+    {
+        auto open = OPENSTR_W<uint8_t>(U("WriteBufferStrTest1.txt"));
+        auto stream = open.get();
+
+        VERIFY_IS_TRUE(open.is_done());
+
+        std::vector<uint8_t> vect;
+
+        for (char ch = 'a'; ch <= 'z'; ch++)
+        {
+            vect.push_back(ch);
+        }
+
+        size_t vsz = vect.size();
+
+        concurrency::streams::container_stream<std::vector<uint8_t>>::buffer_type txtbuf(std::move(vect),
+                                                                                         std::ios_base::in);
+
+        VERIFY_ARE_EQUAL(stream.write(txtbuf, vsz).get(), vsz);
+
+        auto close = stream.close();
+        close.get();
+
+        VERIFY_IS_TRUE(close.is_done());
+    }
+
+    TEST(WriteBufferAndSyncTest1)
+    {
+        auto open = OPENSTR_W<uint8_t>(U("WriteBufferAndSyncStrTest1.txt"));
+        auto stream = open.get();
+
+        VERIFY_IS_TRUE(open.is_done());
+
+        std::vector<char> vect;
+
+        for (char ch = 'a'; ch <= 'z'; ch++)
+        {
+            vect.push_back(ch);
+        }
+
+        size_t vsz = vect.size();
+        concurrency::streams::rawptr_buffer<uint8_t> txtbuf(reinterpret_cast<const uint8_t*>(&vect[0]), vsz);
+
+        auto write = stream.write(txtbuf, vsz);
+        stream.flush().get();
+
+        VERIFY_ARE_EQUAL(write.get(), vect.size());
+        VERIFY_IS_TRUE(write.is_done());
+
+        auto close = stream.close();
+        close.get();
+
+        VERIFY_IS_TRUE(close.is_done());
+    }
+
+    TEST(tell_bug)
+    {
+        auto count = OPENSTR_W<uint8_t>(U("tell_bug.txt"), std::ios_base::out | std::ios_base::trunc)
+                         .then([=](concurrency::streams::ostream os) -> std::streamoff {
+                             os.print("A");
+                             auto val = os.tell();
+                             os.close().get();
+                             return val;
+                         })
+                         .get();
+
+        VERIFY_ARE_EQUAL(std::streamoff(1), count);
+    }
+
+    TEST(iostream_container_buffer1)
+    {
+        concurrency::streams::container_buffer<std::vector<char>> buf;
+
+        auto os = buf.create_ostream();
+        os.write('a');
+        os.write('b');
+        os.close();
+
+        auto is = concurrency::streams::container_stream<std::vector<char>>::open_istream(std::move(buf.collection()));
+        VERIFY_ARE_EQUAL(is.read().get(), 'a');
+        VERIFY_ARE_EQUAL(is.read().get(), 'b');
+    }
+
+    TEST(iostream_container_buffer2)
+    {
+        concurrency::streams::container_buffer<std::vector<char>> buf;
+
+        {
+            auto os = buf.create_ostream();
+            os.write('a');
+            os.write('b');
+            os.close();
+        }
+
+        {
+            auto is =
+                concurrency::streams::container_stream<std::vector<char>>::open_istream(std::move(buf.collection()));
+
+            is.read()
+                .then([&is](concurrency::streams::basic_ostream<char>::int_type c) {
+                    VERIFY_ARE_EQUAL(c, 'a');
+                    return is.read();
+                })
+                .then([&is](concurrency::streams::basic_ostream<char>::int_type c) -> pplx::task<void> {
+                    VERIFY_ARE_EQUAL(c, 'b');
+                    return is.close();
+                })
+                .wait();
+        }
+    }
+
+    TEST(extract_on_space)
+    {
+        const int number1 = 42;
+        const int number2 = 123;
+
+        auto open = OPENSTR_W<uint8_t>(U("SpaceWithNumber.txt"), std::ios::trunc);
+        auto stream = open.get();
+        VERIFY_IS_TRUE(open.is_done());
+        stream.print("  \r").wait();
+        stream.print(number1).wait();
+        stream.print("\n \t").wait();
+        stream.print(number2).wait();
+        stream.print(" \f \v ").wait();
+        stream.close().wait();
+
+        auto istream = OPENSTR_R<uint8_t>(U("SpaceWithNumber.txt")).get();
+        VERIFY_IS_TRUE(istream.can_seek());
+        VERIFY_ARE_EQUAL(number1, istream.extract<int>().get());
+        VERIFY_ARE_EQUAL(number2, istream.extract<long long>().get());
+    }
+
+    TEST(file_sequential_write)
+    {
+        auto open = OPENSTR_W<uint8_t>(U("WriteFileSequential.txt"), std::ios::trunc);
+        auto stream = open.get();
+
+        VERIFY_IS_TRUE(open.is_done());
+
+        std::vector<pplx::task<size_t>> v;
+        for (int i = 0; i < 100; i++)
+        {
+            v.push_back(stream.print(i));
+            v.push_back(stream.print(' '));
+        }
+        pplx::when_all(v.begin(), v.end()).wait();
+        stream.close().wait();
+        auto istream = OPENSTR_R<uint8_t>(U("WriteFileSequential.txt")).get();
+        for (int i = 0; i < 100; i++)
+        {
+            int int_read = istream.extract<int>().get();
+            if (int_read != i)
+            {
+                // This will fail
+                VERIFY_ARE_EQUAL(int_read, i);
+
+                // This return statment will prevent the test from hanging,
+                // cause if the numbers are merged there will be less than 100 numbers,
+                // and reading from the file will block
+                return;
+            }
+            istream.read().get();
+        }
+    }
+
+    TEST(implied_out_mode)
+    {
+        auto ostr = OPENSTR_W<char>(U("implied_out_mode.txt"), std::ios::ios_base::app).get();
+
+        std::string str = "abcd";
+        concurrency::streams::stringstreambuf block(str);
+
+        size_t s = ostr.write(block, str.size()).get();
+
+        VERIFY_ARE_EQUAL(s, str.size());
+
+        auto cls = ostr.close();
+
+        cls.get();
+        VERIFY_IS_TRUE(cls.is_done());
+    }
+
+    TEST(create_ostream_from_input_only)
+    {
+        container_buffer<std::string> sourceBuf("test data");
+        VERIFY_THROWS(sourceBuf.create_ostream(), std::runtime_error);
+    }
+
+    TEST(streambuf_close_with_exception_write)
+    {
+        container_buffer<std::string> sourceBuf;
+        sourceBuf.close(std::ios::out, std::make_exception_ptr(std::invalid_argument("custom exception"))).wait();
+
+        const size_t size = 4;
+        char targetBuf[size];
+        auto t1 = sourceBuf.putn_nocopy(targetBuf, size);
+        VERIFY_THROWS(t1.get(), std::invalid_argument);
+    }
+
+    TEST(stream_close_with_exception_write)
+    {
+        container_buffer<std::string> sourceBuf;
+        auto outStream = sourceBuf.create_ostream();
+        outStream.close(std::make_exception_ptr(std::invalid_argument("custom exception"))).wait();
+
+        container_buffer<std::string> targetBuf("test data");
+        auto t1 = outStream.write(targetBuf, 4);
+        VERIFY_THROWS(t1.get(), std::invalid_argument);
+    }
+
+    TEST(input_after_close)
+    {
+        container_buffer<std::string> sourceBuf;
+        auto outStream = sourceBuf.create_ostream();
+        outStream.close().wait();
+
+        container_buffer<std::string> targetBuf;
+
+        auto t1 = outStream.flush();
+        auto t2 = outStream.print('a');
+        auto t3 = outStream.print(std::string("abc"));
+
+        VERIFY_THROWS(t1.get(), std::runtime_error);
+        VERIFY_THROWS(t2.get(), std::runtime_error);
+        VERIFY_THROWS(t3.get(), std::runtime_error);
+        VERIFY_THROWS(outStream.seek(0), std::runtime_error);
+        VERIFY_THROWS(outStream.seek(0, std::ios::beg), std::runtime_error);
+        VERIFY_THROWS(outStream.tell(), std::runtime_error);
+
+        auto t4 = outStream.write('a');
+        auto t5 = outStream.write(targetBuf, 1);
+        VERIFY_THROWS(t4.get(), std::runtime_error);
+        VERIFY_THROWS(t5.get(), std::runtime_error);
+    }
+
+    TEST(write_emptybuffer_to_ostream)
+    {
+        auto ofs = OPENSTR_W<char>(U("file.txt")).get();
+        auto sbuf = concurrency::streams::producer_consumer_buffer<char>();
+        auto result = ofs.write(sbuf, 0);
+        VERIFY_ARE_EQUAL(result.get(), 0);
+    }
+
+    TEST(write_stream_twice)
+    {
+        producer_consumer_buffer<uint8_t> buf1;
+        auto t1 = pplx::create_task([&] {
+            buf1.alloc(8);
+            buf1.alloc(9);
+        });
+        VERIFY_THROWS(t1.get(), std::logic_error);
+
+        std::string strData("test string to write\n");
+        container_buffer<std::string> buf2(std::move(strData));
+        auto t2 = pplx::create_task([&] {
+            buf2.commit(8);
+            buf2.alloc(9);
+        });
+        VERIFY_THROWS(t2.get(), std::logic_error);
+
+        rawptr_buffer<std::string> buf3;
+        auto t3 = pplx::create_task([&] {
+            buf3.commit(8);
+            buf3.commit(9);
+        });
+        VERIFY_THROWS(t3.get(), std::logic_error);
+    }
+
+} // SUITE(ostream_tests)
+
+} // namespace streams
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/streams/prefix.h b/Release/tests/functional/streams/prefix.h
new file mode 100644 (file)
index 0000000..1fd7575
--- /dev/null
@@ -0,0 +1,61 @@
+/***
+ * 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
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#ifndef __PREFIX_H
+#define __PREFIX_H
+
+#include <fstream>
+#include <memory>
+#include <stdio.h>
+#include <time.h>
+#include <vector>
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1800)
+#include <ppltasks.h>
+namespace pplx = Concurrency;
+#else
+#include "pplx/pplxtasks.h"
+#endif
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/containerstream.h"
+#include "cpprest/filestream.h"
+#include "cpprest/interopstream.h"
+#include "cpprest/producerconsumerstream.h"
+#include "cpprest/rawptrstream.h"
+#include "cpprest/streams.h"
+#include "streams_tests.h"
+#include "unittestpp.h"
+
+template class concurrency::streams::file_buffer<char>;
+template class concurrency::streams::file_buffer<wchar_t>;
+template class concurrency::streams::streambuf<char>;
+template class concurrency::streams::streambuf<wchar_t>;
+
+template class concurrency::streams::rawptr_buffer<char>;
+template class concurrency::streams::rawptr_buffer<wchar_t>;
+template class concurrency::streams::rawptr_buffer<uint8_t>;
+template class concurrency::streams::rawptr_buffer<utf16char>;
+
+template class concurrency::streams::container_buffer<std::vector<uint8_t>>;
+template class concurrency::streams::container_buffer<std::vector<char>>;
+template class concurrency::streams::container_buffer<std::vector<utf16char>>;
+
+template class concurrency::streams::producer_consumer_buffer<char>;
+template class concurrency::streams::producer_consumer_buffer<uint8_t>;
+template class concurrency::streams::producer_consumer_buffer<utf16char>;
+
+template class concurrency::streams::container_stream<std::basic_string<char>>;
+template class concurrency::streams::container_stream<std::basic_string<wchar_t>>;
+
+#endif
diff --git a/Release/tests/functional/streams/stdafx.cpp b/Release/tests/functional/streams/stdafx.cpp
new file mode 100644 (file)
index 0000000..5f1ad19
--- /dev/null
@@ -0,0 +1,14 @@
+/***
+ * 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 streams_test_generate_lib = 0;
+#endif
diff --git a/Release/tests/functional/streams/stdafx.h b/Release/tests/functional/streams/stdafx.h
new file mode 100644 (file)
index 0000000..ac54b8c
--- /dev/null
@@ -0,0 +1,38 @@
+/***
+ * 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 <fstream>
+#include <memory>
+#include <stdio.h>
+#include <time.h>
+#include <vector>
+
+#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
+#include <ppltasks.h>
+namespace pplx = Concurrency;
+#else
+#include "pplx/pplxtasks.h"
+#endif
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/containerstream.h"
+#include "cpprest/filestream.h"
+#include "cpprest/interopstream.h"
+#include "cpprest/producerconsumerstream.h"
+#include "cpprest/rawptrstream.h"
+#include "cpprest/streams.h"
+#include "os_utilities.h"
+#include "streams_tests.h"
+#include "unittestpp.h"
diff --git a/Release/tests/functional/streams/stdstream_tests.cpp b/Release/tests/functional/streams/stdstream_tests.cpp
new file mode 100644 (file)
index 0000000..34b9b3a
--- /dev/null
@@ -0,0 +1,804 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests for integration of async streams with std streams.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "cpprest/filestream.h"
+#include "cpprest/producerconsumerstream.h"
+#include "cpprest/rawptrstream.h"
+
+#if (!defined(_WIN32) || !defined(CPPREST_EXCLUDE_WEBSOCKETS)) && !defined(__cplusplus_winrt)
+#include <boost/interprocess/streams/bufferstream.hpp>
+#endif
+
+#if defined(__cplusplus_winrt)
+using namespace Windows::Storage;
+#endif
+
+#ifdef _WIN32
+#define DEFAULT_PROT (int)std::ios_base::_Openprot
+#else
+#define DEFAULT_PROT 0
+#endif
+
+namespace tests
+{
+namespace functional
+{
+namespace streams
+{
+using namespace ::pplx;
+using namespace utility;
+
+utility::string_t get_full_name(const utility::string_t& name);
+
+template<typename CharType>
+void extract_test(std::basic_istream<CharType>& stream, std::basic_string<CharType> expected)
+{
+    std::basic_string<CharType> s;
+    stream >> s;
+    VERIFY_ARE_EQUAL(s, expected);
+}
+
+// Used to prepare data for read tests
+
+void fill_file(const utility::string_t& name, std::string text, size_t repetitions = 1)
+{
+    std::fstream stream(get_full_name(name), std::ios_base::out | std::ios_base::trunc);
+
+    for (size_t i = 0; i < repetitions; i++)
+        stream << text;
+}
+
+//
+// The following functions will help mask the differences between non-WinRT environments and
+// WinRT: on the latter, a file path is typically not used to open files. Rather, a UI element is used
+// to get a 'StorageFile' reference and you go from there. However, to test the library properly,
+// we need to get a StorageFile reference somehow, and one way to do that is to create all the files
+// used in testing in the Documents folder.
+//
+template<typename _CharType>
+pplx::task<Concurrency::streams::streambuf<_CharType>> OPEN_R(const utility::string_t& name)
+{
+#if !defined(__cplusplus_winrt)
+    return Concurrency::streams::file_buffer<_CharType>::open(name, std::ios_base::in);
+#else
+    auto file =
+        pplx::create_task(KnownFolders::DocumentsLibrary->GetFileAsync(ref new Platform::String(name.c_str()))).get();
+
+    return Concurrency::streams::file_buffer<_CharType>::open(file, std::ios_base::in);
+#endif
+}
+
+SUITE(stdstreambuf_tests)
+{
+    TEST(sync_on_async_write)
+    {
+        Concurrency::streams::stringstreambuf strbuf;
+        auto ss = strbuf.create_ostream();
+        Concurrency::streams::async_ostream<char> bios(ss);
+
+        auto text = "hello!";
+
+        bios.write(text, strlen(text));
+
+        auto buf = ss.streambuf();
+
+        VERIFY_ARE_EQUAL(strbuf.collection(), "hello!");
+    }
+
+    TEST(sync_on_async_put)
+    {
+        Concurrency::streams::stringstreambuf strbuf;
+        auto ss = strbuf.create_ostream();
+        Concurrency::streams::async_ostream<char> bios(ss);
+
+        bios.put('h').put('e').put('l').put('l').put('o').put('!');
+
+        VERIFY_ARE_EQUAL(strbuf.collection(), "hello!");
+    }
+
+    TEST(sync_on_async_insert)
+    {
+        Concurrency::streams::stringstreambuf strbuf;
+        auto ss = strbuf.create_ostream();
+        Concurrency::streams::async_ostream<char> bios(ss);
+
+        bios << "hello"
+             << ", there, this is " << 4711;
+
+        VERIFY_ARE_EQUAL(strbuf.collection(), "hello, there, this is 4711");
+        ss.close().wait();
+    }
+
+    TEST(sync_on_async_seekp)
+    {
+        Concurrency::streams::stringstreambuf strbuf;
+        auto ss = strbuf.create_ostream();
+        Concurrency::streams::async_ostream<char> bios(ss);
+
+        bios << "hello"
+             << ", there, this is " << 4711;
+
+        bios.seekp(10);
+        bios << 'X';
+
+        VERIFY_ARE_EQUAL(strbuf.collection(), "hello, theXe, this is 4711");
+        ss.close().wait();
+    }
+
+    TEST(sync_on_async_getline_1)
+    {
+        std::string s("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+        auto ss = Concurrency::streams::stringstream::open_istream(s);
+
+        Concurrency::streams::async_iostream<char> bios(ss.streambuf());
+
+        char chars[128];
+        bios.getline(chars, sizeof(chars));
+
+        VERIFY_ARE_EQUAL(strcmp(chars, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0);
+    }
+
+    TEST(sync_on_async_getline_2)
+    {
+        std::string s("abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ");
+        auto ss = Concurrency::streams::stringstream::open_istream(s);
+
+        Concurrency::streams::async_iostream<char> bios(ss.streambuf());
+
+        char chars[128];
+
+        bios.getline(chars, sizeof(chars));
+
+        VERIFY_ARE_EQUAL(strcmp(chars, "abcdefghijklmnopqrstuvwxyz"), 0);
+
+        VERIFY_ARE_EQUAL(bios.get(), 'A');
+    }
+
+    TEST(sync_on_async_getline_3)
+    {
+        std::string s("abcdefghijklmnopqrstuvwxyz|ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+        auto ss = Concurrency::streams::stringstream::open_istream(s);
+
+        Concurrency::streams::async_iostream<char> bios(ss.streambuf());
+
+        char chars[128];
+
+        bios.getline(chars, sizeof(chars), '|');
+
+        VERIFY_ARE_EQUAL(strcmp(chars, "abcdefghijklmnopqrstuvwxyz"), 0);
+
+        VERIFY_ARE_EQUAL(bios.get(), 'A');
+    }
+
+    TEST(sync_on_async_get_1)
+    {
+        std::string s("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+        auto ss = Concurrency::streams::stringstream::open_istream(s);
+
+        Concurrency::streams::async_iostream<char> bios(ss.streambuf());
+
+        char chars[128];
+
+        bios.get(chars, sizeof(chars));
+
+        VERIFY_ARE_EQUAL(strcmp(chars, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0);
+    }
+
+    TEST(sync_on_async_fget_1)
+    {
+        utility::string_t fname = U("sync_on_async_fget_1.txt");
+        fill_file(fname, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+
+        auto ofs = OPEN_R<char>(fname).get();
+        Concurrency::streams::async_istream<char> bios(ofs);
+
+        char chars[128];
+
+        bios.get(chars, sizeof(chars));
+
+        VERIFY_ARE_EQUAL(strcmp(chars, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0);
+        ofs.close().wait();
+    }
+
+    TEST(sync_on_async_get_2)
+    {
+        std::string s("abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ");
+        auto ss = Concurrency::streams::stringstream::open_istream(s);
+
+        Concurrency::streams::async_iostream<char> bios(ss.streambuf());
+
+        char chars[128];
+
+        bios.get(chars, sizeof(chars));
+
+        VERIFY_ARE_EQUAL(strcmp(chars, "abcdefghijklmnopqrstuvwxyz"), 0);
+
+        VERIFY_ARE_EQUAL(bios.get(), '\n');
+    }
+
+    TEST(sync_on_async_get_3)
+    {
+        std::string s("abcdefghijklmnopqrstuvwxyz|ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+        auto ss = Concurrency::streams::stringstream::open_istream(s);
+
+        Concurrency::streams::async_iostream<char> bios(ss.streambuf());
+
+        char chars[128];
+
+        bios.get(chars, sizeof(chars), '|');
+
+        VERIFY_ARE_EQUAL(strcmp(chars, "abcdefghijklmnopqrstuvwxyz"), 0);
+
+        VERIFY_ARE_EQUAL(bios.get(), '|');
+    }
+
+    TEST(sync_on_async_extract_1)
+    {
+        auto ss = Concurrency::streams::stringstream::open_istream(std::string("abcdefg 10 1 9.4711"));
+
+        Concurrency::streams::async_iostream<char> bios(ss.streambuf());
+
+        std::string s;
+        int i;
+        bool b;
+        double d;
+
+        bios >> s >> i >> b >> d;
+
+        VERIFY_ARE_EQUAL(s, "abcdefg");
+        VERIFY_ARE_EQUAL(i, 10);
+        VERIFY_IS_TRUE(b);
+        VERIFY_ARE_EQUAL(d, 9.4711);
+    }
+
+    TEST(sync_on_async_fextract_1)
+    {
+        utility::string_t fname = U("sync_on_async_fextract_1.txt");
+        fill_file(fname, "abcdefg 10 1 9.4711");
+
+        auto ofs = OPEN_R<char>(fname).get();
+        Concurrency::streams::async_istream<char> bios(ofs);
+
+        std::string s;
+        int i;
+        bool b;
+        double d;
+
+        bios >> s >> i >> b >> d;
+
+        VERIFY_ARE_EQUAL(s, "abcdefg");
+        VERIFY_ARE_EQUAL(i, 10);
+        VERIFY_IS_TRUE(b);
+        VERIFY_ARE_EQUAL(d, 9.4711);
+
+        ofs.close().wait();
+    }
+
+    TEST(sync_on_async_extract_2)
+    {
+        std::string s("abcdefg 10 1 9.4711");
+        auto is = Concurrency::streams::stringstream::open_istream(s);
+
+        Concurrency::streams::async_istream<char> ss(is.streambuf());
+        extract_test<char>(ss, "abcdefg");
+
+        is.close().wait();
+    }
+
+    TEST(sync_on_async_prodcons)
+    {
+        Concurrency::streams::producer_consumer_buffer<uint8_t> pcbuf;
+
+        auto ostream = pcbuf.create_ostream();
+        auto istream = pcbuf.create_istream();
+
+        const std::streamsize iterations = 100;
+
+        const std::string the_alphabet("abcdefghijklmnopqrstuvwxyz");
+
+        auto writer = pplx::create_task([ostream, iterations, the_alphabet]() {
+            auto os = ostream;
+            for (std::streamsize i = 0; i < iterations; i++)
+            {
+                os.print(the_alphabet).wait();
+                os.flush().wait();
+            }
+            os.close();
+        });
+
+        Concurrency::streams::async_istream<char> ss(istream.streambuf());
+
+        char chars[1024];
+        std::streamsize count = 0;
+
+        while (!ss.eof())
+        {
+            memset(chars, 0, sizeof(chars));
+            ss.read(chars, sizeof(chars) - 1);
+            count += strlen(chars);
+        }
+
+        VERIFY_ARE_EQUAL(the_alphabet.size() * iterations, count);
+
+        writer.wait();
+    }
+
+    TEST(sync_on_async_tellg)
+    {
+        Concurrency::streams::producer_consumer_buffer<uint8_t> pcbuf;
+
+        auto ostream = pcbuf.create_ostream();
+        auto istream = pcbuf.create_istream();
+
+        const std::streamsize iterations = 100;
+
+        const std::string the_alphabet("abcdefghijklmnopqrstuvwxyz");
+
+        auto writer = pplx::create_task([ostream, iterations, the_alphabet]() {
+            auto os = ostream;
+            for (std::streamsize i = 0; i < iterations; i++)
+            {
+                os.print(the_alphabet).wait();
+                os.flush().wait();
+                VERIFY_ARE_EQUAL((i + 1) * the_alphabet.size(), os.tell());
+            }
+            os.close();
+        });
+
+        Concurrency::streams::async_istream<char> ss(istream.streambuf());
+
+        char chars[1024];
+        std::streamsize count = 0;
+
+        while (!ss.eof())
+        {
+            VERIFY_ARE_EQUAL(count, ss.tellg());
+            memset(chars, 0, sizeof(chars));
+            ss.read(chars, sizeof(chars) - 1);
+            count += strlen(chars);
+        }
+
+        VERIFY_ARE_EQUAL(the_alphabet.size() * iterations, count);
+
+        writer.wait();
+    }
+
+    TEST(async_on_sync_read_1)
+    {
+        std::stringstream stream;
+        Concurrency::streams::stdio_istream<char> astream(stream);
+
+        stream << "abcdefghijklmnopqrstuvwxyz";
+
+        for (char c = 'a'; c <= 'z'; c++)
+        {
+            char ch = (char)astream.read().get();
+            VERIFY_ARE_EQUAL(c, ch);
+        }
+
+        astream.close().get();
+    }
+
+    TEST(async_on_sync_read_2)
+    {
+        std::stringstream stream;
+        Concurrency::streams::stdio_istream<char> astream(stream);
+
+        stream << "abcdefghijklmnopqrstuvwxyz";
+
+        char buffer[128];
+        Concurrency::streams::rawptr_buffer<char> txtbuf(buffer, 128);
+
+        VERIFY_ARE_EQUAL(26, astream.read(txtbuf, 26).get());
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        VERIFY_ARE_EQUAL(0, astream.read(txtbuf, 26).get());
+
+        astream.close().get();
+    }
+
+    TEST(async_on_sync_read_3)
+    {
+        Concurrency::streams::producer_consumer_buffer<char> trg;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+        std::stringstream stream;
+        Concurrency::streams::stdio_istream<char> astream(stream);
+
+        stream << text;
+
+        VERIFY_ARE_EQUAL(52, astream.read_to_delim(trg, '\n').get());
+
+        char buffer[128];
+        VERIFY_ARE_EQUAL(52, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'A', buffer[i + 26]);
+        }
+
+        astream.close().get();
+    }
+
+    TEST(async_on_sync_read_4)
+    {
+        Concurrency::streams::producer_consumer_buffer<char> trg;
+
+        // There's one newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+        std::stringstream stream;
+        Concurrency::streams::stdio_istream<char> astream(stream);
+
+        stream << text;
+
+        VERIFY_ARE_EQUAL(26, astream.read_to_delim(trg, '\n').get());
+        VERIFY_ARE_EQUAL('A', (char)astream.read().get());
+
+        char buffer[128];
+        VERIFY_ARE_EQUAL(26, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        astream.close().get();
+    }
+
+    TEST(async_on_sync_read_5)
+    {
+        Concurrency::streams::producer_consumer_buffer<char> trg;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+        std::stringstream stream;
+        Concurrency::streams::stdio_istream<char> astream(stream);
+
+        stream << text;
+
+        VERIFY_ARE_EQUAL(52, astream.read_to_delim(trg, '|').get());
+
+        char buffer[128];
+        VERIFY_ARE_EQUAL(52, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'A', buffer[i + 26]);
+        }
+
+        astream.close().get();
+    }
+
+    TEST(async_on_sync_read_6)
+    {
+        Concurrency::streams::producer_consumer_buffer<char> trg;
+
+        // There's one delimiter in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz|ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+        std::stringstream stream;
+        Concurrency::streams::stdio_istream<char> astream(stream);
+
+        stream << text;
+
+        VERIFY_ARE_EQUAL(26, astream.read_to_delim(trg, '|').get());
+        VERIFY_ARE_EQUAL('A', (char)astream.read().get());
+
+        char buffer[128];
+        VERIFY_ARE_EQUAL(26, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        astream.close().get();
+    }
+
+    TEST(async_on_sync_read_line_1)
+    {
+        Concurrency::streams::producer_consumer_buffer<char> trg;
+
+        // There's no newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+        std::stringstream stream;
+        Concurrency::streams::stdio_istream<char> astream(stream);
+
+        stream << text;
+
+        VERIFY_ARE_EQUAL(26, astream.read_line(trg).get());
+        VERIFY_ARE_EQUAL('A', (char)astream.read().get());
+
+        char buffer[128];
+        VERIFY_ARE_EQUAL(26, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        astream.close().get();
+    }
+
+    TEST(async_on_sync_read_to_end_1)
+    {
+        Concurrency::streams::producer_consumer_buffer<char> trg;
+
+        // There's one newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+        std::stringstream stream;
+        Concurrency::streams::stdio_istream<char> astream(stream);
+
+        stream << text;
+
+        VERIFY_ARE_EQUAL(53, astream.read_to_end(trg).get());
+
+        char buffer[128];
+        VERIFY_ARE_EQUAL(53, trg.in_avail());
+        trg.getn(buffer, trg.in_avail()).get();
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'a', buffer[i]);
+        }
+
+        for (int i = 0; i < 26; i++)
+        {
+            VERIFY_ARE_EQUAL((char)i + 'A', buffer[i + 27]);
+        }
+
+        astream.close().get();
+    }
+
+    TEST(ostream_write_single_char)
+    {
+        std::stringstream stream;
+
+        Concurrency::streams::stdio_ostream<char> os(stream);
+
+        bool elements_equal = true;
+
+        for (char ch = 'a'; ch <= 'z'; ch++)
+        {
+            elements_equal = elements_equal && (ch == os.write(ch).get());
+        }
+
+        VERIFY_IS_TRUE(elements_equal);
+
+        VERIFY_ARE_EQUAL(stream.str(), "abcdefghijklmnopqrstuvwxyz");
+
+        os.close().get();
+    }
+
+    TEST(ostream_write_buffer)
+    {
+        std::stringstream stream;
+
+        Concurrency::streams::stdio_ostream<char> os(stream);
+
+        const char* text = "abcdefghijklmnopqrstuvwxyz";
+        size_t len = strlen(text);
+
+        Concurrency::streams::rawptr_buffer<char> txtbuf(text, len);
+
+        VERIFY_ARE_EQUAL(os.write(txtbuf, len).get(), len);
+
+        VERIFY_ARE_EQUAL(stream.str(), "abcdefghijklmnopqrstuvwxyz");
+
+        os.close().get();
+    }
+
+    TEST(ostream_output_print_string)
+    {
+        std::stringstream stream;
+
+        Concurrency::streams::stdio_ostream<char> os(stream);
+
+        os.print("abcdefghijklmnopqrstuvwxyz").wait();
+
+        VERIFY_ARE_EQUAL(stream.str(), "abcdefghijklmnopqrstuvwxyz");
+
+        os.close().get();
+    }
+
+    TEST(ostream_output_print_types)
+    {
+        std::stringstream stream;
+
+        Concurrency::streams::stdio_ostream<char> os(stream);
+
+        auto a = os.print("data: ");
+        auto b = os.print(10);
+        auto c = os.print(",");
+        auto d = os.print(true);
+        (a && b && c && d).wait();
+
+        VERIFY_ARE_EQUAL(stream.str(), "data: 10,1");
+
+        os.close().get();
+    }
+
+    TEST(ostream_output_print_line_string)
+    {
+        std::stringstream stream;
+
+        Concurrency::streams::stdio_ostream<char> os(stream);
+
+        os.print_line("abcdefghijklmnopqrstuvwxyz").wait();
+
+        VERIFY_ARE_EQUAL(stream.str(), "abcdefghijklmnopqrstuvwxyz\n");
+
+        os.close().get();
+    }
+
+    TEST(ostream_output_print_line_types)
+    {
+        std::stringstream stream;
+
+        Concurrency::streams::stdio_ostream<char> os(stream);
+
+        auto a = os.print_line("data: ");
+        auto b = os.print_line(10);
+        auto c = os.print_line(",");
+        auto d = os.print_line(true);
+        (a && b && c && d).wait();
+
+        VERIFY_ARE_EQUAL(stream.str(), "data: \n10\n,\n1\n");
+
+        os.close().get();
+    }
+
+    TEST(istream_extract_string)
+    {
+        const char* text = " abc defgsf ";
+
+        std::stringstream stream;
+        stream << text;
+
+        Concurrency::streams::stdio_istream<char> is(stream);
+
+        std::string str1 = is.extract<std::string>().get();
+        std::string str2 = is.extract<std::string>().get();
+
+        VERIFY_ARE_EQUAL(str1, "abc");
+        VERIFY_ARE_EQUAL(str2, "defgsf");
+
+        is.close().get();
+    }
+
+    TEST(stdio_istream_error)
+    {
+        std::ifstream inFile;
+        inFile.open("stdio_istream_error.txt");
+        concurrency::streams::stdio_istream<char> is(inFile);
+
+        concurrency::streams::container_buffer<std::string> buffer;
+        VERIFY_ARE_EQUAL(0, is.read_to_end(buffer).get());
+        VERIFY_IS_TRUE(is.is_eof());
+        VERIFY_IS_TRUE(is.is_open());
+
+        is.close().wait();
+    }
+
+    TEST(stdio_istream_setstate)
+    {
+        std::ifstream inFile;
+        inFile.open("stdio_istream_setstate.txt");
+        concurrency::streams::stdio_istream<char> is(inFile);
+        inFile.setstate(std::ios::failbit);
+
+        concurrency::streams::container_buffer<std::string> buffer;
+        VERIFY_ARE_EQUAL(0, is.read_to_end(buffer).get());
+        VERIFY_IS_TRUE(is.is_eof());
+        VERIFY_IS_TRUE(is.is_open());
+
+        is.close().wait();
+    }
+
+    TEST(stdio_istream_close)
+    {
+        std::ifstream inFile;
+        inFile.open("stdio_istream_close.txt");
+        concurrency::streams::stdio_istream<char> is(inFile);
+        inFile.close();
+
+        concurrency::streams::container_buffer<std::string> buffer;
+        VERIFY_ARE_EQUAL(0, is.read_to_end(buffer).get());
+        // Won't fix bug TFS 639208
+        // VERIFY_IS_FALSE(is.is_open());
+        VERIFY_IS_TRUE(is.is_eof());
+    }
+
+    TEST(sync_on_async_close_early)
+    {
+        concurrency::streams::container_buffer<std::string> buffer;
+        concurrency::streams::async_ostream<char> os(buffer);
+        buffer.close();
+
+        os << 10 << std::endl;
+        VERIFY_IS_TRUE((std::ios::badbit & os.rdstate()) == std::ios::badbit);
+    }
+
+    TEST(sync_on_async_close_with_exception)
+    {
+        const std::string& data("abc123");
+
+        // Try with a read.
+        {
+            concurrency::streams::container_buffer<std::string> buffer(data);
+            concurrency::streams::async_istream<char> inputStream(buffer);
+            buffer.close(std::ios::in, std::make_exception_ptr(std::invalid_argument("test exception"))).wait();
+            const size_t tempBufSize = 4;
+            char tempBuf[tempBufSize];
+            inputStream.read(&tempBuf[0], tempBufSize);
+            VERIFY_ARE_EQUAL(std::ios::failbit | std::ios::eofbit, inputStream.rdstate());
+        }
+
+        // Try with a write.
+        {
+            concurrency::streams::container_buffer<std::string> buffer(data);
+            concurrency::streams::async_ostream<char> outputStream(buffer);
+            buffer.close(std::ios::in, std::make_exception_ptr(std::invalid_argument("test exception"))).wait();
+            const size_t tempBufSize = 4;
+            char tempBuf[tempBufSize];
+            outputStream.write(&tempBuf[0], tempBufSize);
+            VERIFY_ARE_EQUAL(std::ios::badbit, outputStream.rdstate());
+        }
+    }
+
+#if (!defined(_WIN32) || !defined(CPPREST_EXCLUDE_WEBSOCKETS)) && !defined(__cplusplus_winrt)
+    TEST(ostream_full_throw_exception)
+    {
+        char tgt_buffer[5];
+        boost::interprocess::bufferstream limited_stream(
+            tgt_buffer, sizeof(tgt_buffer), ::std::ios_base::out | std::ios_base::binary);
+        concurrency::streams::stdio_ostream<char> os_wrapper(limited_stream);
+        concurrency::streams::streambuf<char> os_streambuf = os_wrapper.streambuf();
+
+        // There's one newline in the input.
+        const char* text = "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+        std::stringstream stream;
+        Concurrency::streams::stdio_istream<char> astream(stream);
+
+        stream << text;
+
+        VERIFY_THROWS(astream.read_to_end(os_streambuf).get(), std::exception);
+    }
+#endif
+}
+} // namespace streams
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/streams/streams_tests.h b/Release/tests/functional/streams/streams_tests.h
new file mode 100644 (file)
index 0000000..a7f02f7
--- /dev/null
@@ -0,0 +1,88 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * streams_tests.h
+ *
+ * Common routines for streams tests.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include <system_error>
+#include <unittestpp.h>
+
+namespace tests
+{
+namespace functional
+{
+namespace streams
+{
+template<typename CharType>
+void test_stream_length(concurrency::streams::basic_istream<CharType> istr, size_t length)
+{
+    using namespace concurrency::streams;
+
+    auto curr = istr.tell();
+    auto t1 = (curr != static_cast<typename basic_istream<CharType>::pos_type>(basic_istream<CharType>::traits::eof()));
+    VERIFY_IS_TRUE(t1);
+
+    auto end = istr.seek(0, std::ios_base::end);
+    VERIFY_IS_TRUE(end !=
+                   static_cast<typename basic_istream<CharType>::pos_type>(basic_istream<CharType>::traits::eof()));
+
+    auto len = end - curr;
+
+    VERIFY_ARE_EQUAL(len, length);
+
+    {
+        auto curr2 = istr.tell();
+        VERIFY_IS_TRUE(curr !=
+                       static_cast<typename basic_istream<CharType>::pos_type>(basic_istream<CharType>::traits::eof()));
+
+        auto end2 = istr.seek(0, std::ios_base::end);
+        VERIFY_IS_TRUE(end !=
+                       static_cast<typename basic_istream<CharType>::pos_type>(basic_istream<CharType>::traits::eof()));
+
+        auto len2 = end2 - curr2;
+
+        VERIFY_ARE_EQUAL(len2, 0);
+    }
+
+    auto newpos = istr.seek(curr);
+    VERIFY_IS_TRUE(newpos !=
+                   static_cast<typename basic_istream<CharType>::pos_type>(basic_istream<CharType>::traits::eof()));
+
+    VERIFY_ARE_EQUAL(curr, newpos);
+}
+
+// Helper function to verify std::system_error is thrown with correct error code
+#define VERIFY_THROWS_SYSTEM_ERROR(__expression, __code)                                                               \
+    UNITTEST_MULTILINE_MACRO_BEGIN                                                                                     \
+    try                                                                                                                \
+    {                                                                                                                  \
+        __expression;                                                                                                  \
+        VERIFY_IS_TRUE(false, "Expected std::system_error not thrown");                                                \
+    }                                                                                                                  \
+    catch (const std::system_error& _exc)                                                                              \
+    {                                                                                                                  \
+        VERIFY_IS_TRUE(std::string(_exc.what()).size() > 0);                                                           \
+        /* The reason we can't directly compare with the given std::errc code is because*/                             \
+        /* on Windows the STL implementation of error categories are NOT unique across*/                               \
+        /* dll boundaries.*/                                                                                           \
+        const std::error_condition _condFound = _exc.code().default_error_condition();                                 \
+        VERIFY_ARE_EQUAL(static_cast<int>(__code), _condFound.value());                                                \
+    }                                                                                                                  \
+    catch (...)                                                                                                        \
+    {                                                                                                                  \
+        VERIFY_IS_TRUE(false, "Exception other than std::system_error thrown");                                        \
+    }                                                                                                                  \
+    UNITTEST_MULTILINE_MACRO_END
+
+} // namespace streams
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/streams/winrt_interop_tests.cpp b/Release/tests/functional/streams/winrt_interop_tests.cpp
new file mode 100644 (file)
index 0000000..8781c05
--- /dev/null
@@ -0,0 +1,258 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Basic tests for winrt interop streams.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+using namespace concurrency::streams;
+using namespace utility;
+using namespace ::pplx;
+
+#if defined(__cplusplus_winrt)
+using namespace Windows::Storage;
+#endif
+
+namespace tests
+{
+namespace functional
+{
+namespace streams
+{
+SUITE(winrt_interop_tests)
+{
+    TEST(read_in)
+    {
+        producer_consumer_buffer<char> buf;
+        auto ostream = buf.create_ostream();
+        std::string strData("abcdefghij");
+        buf.putn_nocopy((char*)&strData[0], strData.size() * sizeof(char)).wait();
+
+        auto dr = ref new Windows::Storage::Streams::DataReader(winrt_stream::create_input_stream(buf));
+        dr->ByteOrder = Windows::Storage::Streams::ByteOrder::LittleEndian;
+
+        {
+            VERIFY_ARE_EQUAL(10, pplx::create_task(dr->LoadAsync(10)).get());
+
+            auto value = dr->ReadString(5);
+            VERIFY_ARE_EQUAL(utility::string_t(value->Data()), U("abcde"));
+            value = dr->ReadString(5);
+            VERIFY_ARE_EQUAL(utility::string_t(value->Data()), U("fghij"));
+        }
+        {
+            ostream.write(char(11)).wait();
+            ostream.write(char(17)).wait();
+
+            VERIFY_ARE_EQUAL(2, pplx::create_task(dr->LoadAsync(2)).get());
+
+            auto ival = dr->ReadByte();
+            VERIFY_ARE_EQUAL(ival, 11);
+            ival = dr->ReadByte();
+            VERIFY_ARE_EQUAL(ival, 17);
+        }
+        {
+            for (int i = 0; i < 100; i++)
+            {
+                ostream.write(char(i)).wait();
+            }
+
+            VERIFY_ARE_EQUAL(100, pplx::create_task(dr->LoadAsync(100)).get());
+
+            auto arr = ref new Platform::Array<unsigned char, 1>(100);
+            dr->ReadBytes(arr);
+
+            for (int i = 0; i < 100; i++)
+            {
+                VERIFY_ARE_EQUAL(arr[i], i);
+            }
+        }
+        buf.close(std::ios_base::out);
+    }
+
+    TEST(read_rand)
+    {
+        producer_consumer_buffer<char> buf;
+        auto ostream = buf.create_ostream();
+        std::string strData("abcdefghij");
+        buf.putn_nocopy((char*)&strData[0], strData.size() * sizeof(char)).wait();
+
+        auto dr = ref new Windows::Storage::Streams::DataReader(winrt_stream::create_random_access_stream(buf));
+        dr->ByteOrder = Windows::Storage::Streams::ByteOrder::LittleEndian;
+
+        {
+            VERIFY_ARE_EQUAL(10, pplx::create_task(dr->LoadAsync(10)).get());
+
+            auto value = dr->ReadString(5);
+            VERIFY_ARE_EQUAL(utility::string_t(value->Data()), U("abcde"));
+            value = dr->ReadString(5);
+            VERIFY_ARE_EQUAL(utility::string_t(value->Data()), U("fghij"));
+        }
+        {
+            ostream.write(char(11)).wait();
+            ostream.write(char(17)).wait();
+
+            VERIFY_ARE_EQUAL(2, pplx::create_task(dr->LoadAsync(2)).get());
+
+            auto ival = dr->ReadByte();
+            VERIFY_ARE_EQUAL(ival, 11);
+            ival = dr->ReadByte();
+            VERIFY_ARE_EQUAL(ival, 17);
+        }
+        {
+            for (int i = 0; i < 100; i++)
+            {
+                ostream.write(char(i)).wait();
+            }
+
+            VERIFY_ARE_EQUAL(100, pplx::create_task(dr->LoadAsync(100)).get());
+            auto arr = ref new Platform::Array<unsigned char, 1>(100);
+            dr->ReadBytes(arr);
+
+            for (int i = 0; i < 100; i++)
+            {
+                VERIFY_ARE_EQUAL(arr[i], i);
+            }
+        }
+        buf.close(std::ios_base::out);
+    }
+
+    pplx::task<bool> StoreAndFlush(Windows::Storage::Streams::DataWriter ^ dw)
+    {
+        return pplx::create_task(dw->StoreAsync()).then([dw](unsigned int) {
+            return pplx::create_task(dw->FlushAsync());
+        });
+    }
+
+    TEST(write_out)
+    {
+        producer_consumer_buffer<char> buf;
+
+        auto dw = ref new Windows::Storage::Streams::DataWriter(winrt_stream::create_output_stream(buf));
+        dw->ByteOrder = Windows::Storage::Streams::ByteOrder::LittleEndian;
+
+        auto value = ref new ::Platform::String(U("10 4711 -10.0 hello!"));
+        dw->WriteString(value);
+        dw->WriteByte(11); // Take care to make this a non-character!
+        dw->WriteUInt16(17);
+        dw->WriteUInt32(4711);
+        VERIFY_IS_TRUE(StoreAndFlush(dw).get());
+        buf.close(std::ios_base::out);
+
+        auto istream = buf.create_istream();
+        VERIFY_ARE_EQUAL(10, istream.extract<unsigned int>().get());
+        VERIFY_ARE_EQUAL(4711, istream.extract<int>().get());
+        VERIFY_ARE_EQUAL(-10.0, istream.extract<double>().get());
+        VERIFY_ARE_EQUAL(utility::string_t(U("hello!")), istream.extract<utility::string_t>().get());
+        VERIFY_ARE_EQUAL(11, istream.read().get());
+        uint16_t int16;
+        buf.getn((char*)&int16, sizeof(int16)).wait();
+        VERIFY_ARE_EQUAL(17, int16);
+        uint32_t int32;
+        buf.getn((char*)&int32, sizeof(int32)).wait();
+        VERIFY_ARE_EQUAL(4711, int32);
+    }
+
+    TEST(write_rand)
+    {
+        producer_consumer_buffer<char> buf;
+
+        auto dw = ref new Windows::Storage::Streams::DataWriter(winrt_stream::create_random_access_stream(buf));
+        dw->ByteOrder = Windows::Storage::Streams::ByteOrder::LittleEndian;
+
+        auto value = ref new ::Platform::String(U("10 4711 -10.0 hello!"));
+        dw->WriteString(value);
+        dw->WriteByte(11); // Take care to make this a non-character!
+        dw->WriteUInt16(17);
+        dw->WriteUInt32(4711);
+        VERIFY_IS_TRUE(StoreAndFlush(dw).get());
+        buf.close(std::ios_base::out);
+
+        auto istream = buf.create_istream();
+        VERIFY_ARE_EQUAL(10, istream.extract<unsigned int>().get());
+        VERIFY_ARE_EQUAL(4711, istream.extract<int>().get());
+        VERIFY_ARE_EQUAL(-10.0, istream.extract<double>().get());
+        VERIFY_ARE_EQUAL(utility::string_t(U("hello!")), istream.extract<utility::string_t>().get());
+        VERIFY_ARE_EQUAL(11, istream.read().get());
+        uint16_t int16;
+        buf.getn((char*)&int16, sizeof(int16)).wait();
+        VERIFY_ARE_EQUAL(17, int16);
+        uint32_t int32;
+        buf.getn((char*)&int32, sizeof(int32)).wait();
+        VERIFY_ARE_EQUAL(4711, int32);
+    }
+
+    TEST(read_write_attributes)
+    {
+        {
+            container_buffer<std::string> buf("test data");
+            auto rastr = winrt_stream::create_random_access_stream(buf);
+            VERIFY_IS_TRUE(rastr->CanRead);
+            VERIFY_IS_FALSE(rastr->CanWrite);
+            VERIFY_ARE_EQUAL(rastr->Position, 0);
+
+            VERIFY_ARE_EQUAL(rastr->Size, 9);
+            rastr->Size = 1024U;
+            VERIFY_ARE_EQUAL(rastr->Size, 9);
+        }
+        {
+            container_buffer<std::string> buf;
+            auto rastr = winrt_stream::create_random_access_stream(buf);
+            VERIFY_IS_FALSE(rastr->CanRead);
+            VERIFY_IS_TRUE(rastr->CanWrite);
+            VERIFY_ARE_EQUAL(rastr->Position, 0);
+
+            VERIFY_ARE_EQUAL(rastr->Size, 0);
+            rastr->Size = 1024U;
+            VERIFY_ARE_EQUAL(rastr->Size, 1024U);
+            VERIFY_ARE_EQUAL(buf.collection().size(), 1024U);
+        }
+        {
+            producer_consumer_buffer<uint8_t> buf;
+            auto rastr = winrt_stream::create_random_access_stream(buf);
+            VERIFY_IS_TRUE(rastr->CanRead);
+            VERIFY_IS_TRUE(rastr->CanWrite);
+            VERIFY_ARE_EQUAL(rastr->Position, 0);
+
+            VERIFY_ARE_EQUAL(rastr->Size, 0);
+            rastr->Size = 1024U;
+            VERIFY_ARE_EQUAL(rastr->Size, 1024U);
+        }
+    }
+
+    TEST(cant_write)
+    {
+        container_buffer<std::string> buf("test data");
+
+        auto ostr = winrt_stream::create_output_stream(buf);
+        auto dw = ref new Windows::Storage::Streams::DataWriter(ostr);
+        dw->ByteOrder = Windows::Storage::Streams::ByteOrder::LittleEndian;
+
+        auto value = ref new ::Platform::String(U("10 4711 -10.0 hello!"));
+        dw->WriteString(value);
+
+        VERIFY_IS_FALSE(StoreAndFlush(dw).get());
+    }
+
+    TEST(cant_read)
+    {
+        container_buffer<std::string> buf;
+        auto ostream = buf.create_ostream();
+        ostream.print<int>(10);
+
+        auto istr = winrt_stream::create_input_stream(buf);
+        auto dr = ref new Windows::Storage::Streams::DataReader(istr);
+        dr->ByteOrder = Windows::Storage::Streams::ByteOrder::LittleEndian;
+
+        VERIFY_ARE_EQUAL(0, pplx::create_task(dr->LoadAsync(2)).get());
+    }
+
+} // SUITE
+
+} // namespace streams
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/CMakeLists.txt b/Release/tests/functional/uri/CMakeLists.txt
new file mode 100644 (file)
index 0000000..298ec21
--- /dev/null
@@ -0,0 +1,16 @@
+set(SOURCES
+  accessor_tests.cpp
+  combining_tests.cpp
+  constructor_tests.cpp
+  conversions_tests.cpp
+  diagnostic_tests.cpp
+  encoding_tests.cpp
+  operator_tests.cpp
+  splitting_tests.cpp
+  uri_builder_tests.cpp
+  resolve_uri_tests.cpp
+)
+
+add_casablanca_test(uri_test SOURCES)
+
+configure_pch(uri_test stdafx.h stdafx.cpp)
diff --git a/Release/tests/functional/uri/accessor_tests.cpp b/Release/tests/functional/uri/accessor_tests.cpp
new file mode 100644 (file)
index 0000000..e540fb3
--- /dev/null
@@ -0,0 +1,55 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * constructor_string_tests.cpp
+ *
+ * Tests for constructors of the uri class
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+SUITE(accessor_tests)
+{
+    TEST(authority_string)
+    {
+        uri u(U("http://testname.com:81/path?baz"));
+        uri a = u.authority();
+
+        VERIFY_ARE_EQUAL(U("/path"), u.path());
+        VERIFY_ARE_EQUAL(U("http"), a.scheme());
+        VERIFY_ARE_EQUAL(U("testname.com"), a.host());
+        VERIFY_ARE_EQUAL(81, a.port());
+        VERIFY_ARE_EQUAL(uri(U("http://testname.com:81")), a);
+    }
+
+    TEST(authority_wstring)
+    {
+        uri u(U("http://testname.com:81/path?baz"));
+        uri a = u.authority();
+
+        VERIFY_ARE_EQUAL(U("/path"), u.path());
+        VERIFY_ARE_EQUAL(U("http"), a.scheme());
+        VERIFY_ARE_EQUAL(U("testname.com"), a.host());
+        VERIFY_ARE_EQUAL(81, a.port());
+        VERIFY_ARE_EQUAL(uri(U("http://testname.com:81")), a);
+    }
+
+} // SUITE(accessor_tests)
+
+} // namespace uri_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/combining_tests.cpp b/Release/tests/functional/uri/combining_tests.cpp
new file mode 100644 (file)
index 0000000..0860052
--- /dev/null
@@ -0,0 +1,89 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * combining_tests.cpp
+ *
+ * Tests for appending/combining features of the http::uri class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+SUITE(combining_tests)
+{
+    TEST(append_path)
+    {
+        utility::string_t uri_str = U("http://testname.com/path?baz");
+        uri_builder ub(uri_str);
+        uri combined = ub.append_path(U("/baz")).to_uri();
+
+        VERIFY_ARE_EQUAL(uri(U("http://testname.com/path/baz?baz")), combined);
+    }
+
+    TEST(append_empty_path)
+    {
+        utility::string_t uri_str(U("http://fakeuri.net"));
+        uri u = uri_str;
+        uri_builder ub(u);
+        uri combined = ub.append_path(U("")).to_uri();
+
+        VERIFY_ARE_EQUAL(u, combined);
+    }
+
+    TEST(append_query)
+    {
+        utility::string_t uri_str(U("http://testname.com/path1?key1=value2"));
+        uri_builder ub(uri_str);
+        uri combined = ub.append_query(uri(U("http://testname2.com/path2?key2=value3")).query()).to_uri();
+
+        VERIFY_ARE_EQUAL(U("http://testname.com/path1?key1=value2&key2=value3"), combined.to_string());
+    }
+
+    TEST(append_empty_query)
+    {
+        utility::string_t uri_str(U("http://fakeuri.org/?key=value"));
+        uri u(uri_str);
+        uri_builder ub(u);
+        uri combined = ub.append_query(U("")).to_uri();
+
+        VERIFY_ARE_EQUAL(u, combined);
+    }
+
+    TEST(append)
+    {
+        utility::string_t uri_str(U("http://testname.com/path1?key1=value2"));
+        uri_builder ub(uri_str);
+        uri combined = ub.append(U("http://testname2.com/path2?key2=value3")).to_uri();
+
+        VERIFY_ARE_EQUAL(U("http://testname.com/path1/path2?key1=value2&key2=value3"), combined.to_string());
+        VERIFY_ARE_EQUAL(U("/path1/path2?key1=value2&key2=value3"), combined.resource().to_string());
+    }
+
+    TEST(append_empty)
+    {
+        utility::string_t uri_str(U("http://myhost.com"));
+        uri u(uri_str);
+        uri_builder ub(u);
+        uri combined = ub.append(U("")).to_uri();
+
+        VERIFY_ARE_EQUAL(u, combined);
+    }
+
+} // SUITE(combining_tests)
+
+} // namespace uri_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/constructor_tests.cpp b/Release/tests/functional/uri/constructor_tests.cpp
new file mode 100644 (file)
index 0000000..ea6041c
--- /dev/null
@@ -0,0 +1,262 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * constructor_tests.cpp
+ *
+ * Tests for constructors of the uri class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+SUITE(constructor_tests)
+{
+    TEST(parsing_constructor_char)
+    {
+        uri u(uri::encode_uri(U("net.tcp://steve:@testname.com:81/bleh%?qstring#goo")));
+
+        VERIFY_ARE_EQUAL(U("net.tcp"), u.scheme());
+        VERIFY_ARE_EQUAL(U("steve:"), u.user_info());
+        VERIFY_ARE_EQUAL(U("testname.com"), u.host());
+        VERIFY_ARE_EQUAL(81, u.port());
+        VERIFY_ARE_EQUAL(U("/bleh%25"), u.path());
+        VERIFY_ARE_EQUAL(U("qstring"), u.query());
+        VERIFY_ARE_EQUAL(U("goo"), u.fragment());
+    }
+
+    TEST(parsing_constructor_encoded_string)
+    {
+        uri u(uri::encode_uri(U("net.tcp://testname.com:81/bleh%?qstring#goo")));
+
+        VERIFY_ARE_EQUAL(U("net.tcp"), u.scheme());
+        VERIFY_ARE_EQUAL(U("testname.com"), u.host());
+        VERIFY_ARE_EQUAL(81, u.port());
+        VERIFY_ARE_EQUAL(U("/bleh%25"), u.path());
+        VERIFY_ARE_EQUAL(U("qstring"), u.query());
+        VERIFY_ARE_EQUAL(U("goo"), u.fragment());
+    }
+
+    TEST(parsing_constructor_string_string)
+    {
+        uri u(uri::encode_uri(U("net.tcp://testname.com:81/bleh%?qstring#goo")));
+
+        VERIFY_ARE_EQUAL(U("net.tcp"), u.scheme());
+        VERIFY_ARE_EQUAL(U("testname.com"), u.host());
+        VERIFY_ARE_EQUAL(81, u.port());
+        VERIFY_ARE_EQUAL(U("/bleh%25"), u.path());
+        VERIFY_ARE_EQUAL(U("qstring"), u.query());
+        VERIFY_ARE_EQUAL(U("goo"), u.fragment());
+    }
+
+    TEST(empty_strings)
+    {
+        VERIFY_IS_TRUE(uri(U("")).is_empty());
+        VERIFY_IS_TRUE(uri(U("")).is_empty());
+        VERIFY_IS_TRUE(uri(uri::encode_uri(U(""))).is_empty());
+    }
+
+    TEST(default_constructor) { VERIFY_IS_TRUE(uri().is_empty()); }
+
+    TEST(relative_ref_string)
+    {
+        uri u(uri::encode_uri(U("first/second#boff")));
+
+        VERIFY_ARE_EQUAL(U(""), u.scheme());
+        VERIFY_ARE_EQUAL(U(""), u.host());
+        VERIFY_ARE_EQUAL(0, u.port());
+        VERIFY_ARE_EQUAL(U("first/second"), u.path());
+        VERIFY_ARE_EQUAL(U(""), u.query());
+        VERIFY_ARE_EQUAL(U("boff"), u.fragment());
+    }
+
+    TEST(absolute_ref_string)
+    {
+        uri u(uri::encode_uri(U("/first/second#boff")));
+
+        VERIFY_ARE_EQUAL(U(""), u.scheme());
+        VERIFY_ARE_EQUAL(U(""), u.host());
+        VERIFY_ARE_EQUAL(0, u.port());
+        VERIFY_ARE_EQUAL(U("/first/second"), u.path());
+        VERIFY_ARE_EQUAL(U(""), u.query());
+        VERIFY_ARE_EQUAL(U("boff"), u.fragment());
+    }
+
+    TEST(copy_constructor)
+    {
+        uri original(U("http://st:pass@localhost:456/path1?qstring#goo"));
+        uri new_uri(original);
+
+        VERIFY_ARE_EQUAL(original, new_uri);
+    }
+
+    TEST(move_constructor)
+    {
+        const utility::string_t uri_str(U("http://localhost:456/path1?qstring#goo"));
+        uri original(uri_str);
+        uri new_uri = std::move(original);
+
+        VERIFY_ARE_EQUAL(uri_str, new_uri.to_string());
+        VERIFY_ARE_EQUAL(uri(uri_str), new_uri);
+    }
+
+    TEST(assignment_operator)
+    {
+        uri original(U("http://localhost:456/path?qstring#goo"));
+        uri new_uri = original;
+
+        VERIFY_ARE_EQUAL(original, new_uri);
+    }
+
+    // Tests invalid URI being passed in constructor.
+    TEST(parsing_constructor_invalid)
+    {
+        VERIFY_THROWS(uri(U("123http://localhost:345/")), uri_exception);
+        VERIFY_THROWS(uri(U("h*ttp://localhost:345/")), uri_exception);
+        VERIFY_THROWS(uri(U("http://localhost:345/\"")), uri_exception);
+        VERIFY_THROWS(uri(U("http://localhost:345/path?\"")), uri_exception);
+        VERIFY_THROWS(uri(U("http://local\"host:345/")), uri_exception);
+    }
+
+    // Tests a variety of different URIs using the examples in RFC 2732
+    TEST(RFC_2732_examples_string)
+    {
+        // The URI parser will make characters lower case
+        uri http1(U("http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html"));
+        VERIFY_ARE_EQUAL(U("http"), http1.scheme());
+        VERIFY_ARE_EQUAL(U("[fedc:ba98:7654:3210:fedc:ba98:7654:3210]"), http1.host());
+        VERIFY_ARE_EQUAL(80, http1.port());
+        VERIFY_ARE_EQUAL(U("/index.html"), http1.path());
+        VERIFY_ARE_EQUAL(U(""), http1.query());
+
+        uri http2(U("http://[1080:0:0:0:8:800:200C:417A]/index.html"));
+        VERIFY_ARE_EQUAL(U("http"), http2.scheme());
+        VERIFY_ARE_EQUAL(U("[1080:0:0:0:8:800:200c:417a]"), http2.host());
+        VERIFY_ARE_EQUAL(0, http2.port());
+        VERIFY_ARE_EQUAL(U("/index.html"), http2.path());
+        VERIFY_ARE_EQUAL(U(""), http2.query());
+
+        uri http3(U("https://[3ffe:2a00:100:7031::1]"));
+        VERIFY_ARE_EQUAL(U("https"), http3.scheme());
+        VERIFY_ARE_EQUAL(U("[3ffe:2a00:100:7031::1]"), http3.host());
+        VERIFY_ARE_EQUAL(0, http3.port());
+        VERIFY_ARE_EQUAL(U("/"), http3.path());
+        VERIFY_ARE_EQUAL(U(""), http3.query());
+
+        uri http4(U("http://[::192.9.5.5]/ipng"));
+        VERIFY_ARE_EQUAL(U("http"), http4.scheme());
+        VERIFY_ARE_EQUAL(U("[::192.9.5.5]"), http4.host());
+        VERIFY_ARE_EQUAL(0, http4.port());
+        VERIFY_ARE_EQUAL(U("/ipng"), http4.path());
+        VERIFY_ARE_EQUAL(U(""), http4.query());
+
+        uri http5(U("http://[1080::8:800:200C:417A]/foo"));
+        VERIFY_ARE_EQUAL(U("http"), http5.scheme());
+        VERIFY_ARE_EQUAL(U("[1080::8:800:200c:417a]"), http5.host());
+        VERIFY_ARE_EQUAL(0, http5.port());
+        VERIFY_ARE_EQUAL(U("/foo"), http5.path());
+        VERIFY_ARE_EQUAL(U(""), http5.query());
+
+        uri http6(U("http://[::FFFF:129.144.52.38]:80/index.html"));
+        VERIFY_ARE_EQUAL(U("http"), http6.scheme());
+        VERIFY_ARE_EQUAL(U("[::ffff:129.144.52.38]"), http6.host());
+        VERIFY_ARE_EQUAL(80, http6.port());
+        VERIFY_ARE_EQUAL(U("/index.html"), http6.path());
+        VERIFY_ARE_EQUAL(U(""), http6.query());
+
+        uri http7(U("http://[2010:836B:4179::836B:4179]"));
+        VERIFY_ARE_EQUAL(U("http"), http7.scheme());
+        VERIFY_ARE_EQUAL(U("[2010:836b:4179::836b:4179]"), http7.host());
+        VERIFY_ARE_EQUAL(0, http7.port());
+        VERIFY_ARE_EQUAL(U("/"), http7.path());
+        VERIFY_ARE_EQUAL(U(""), http7.query());
+    }
+
+    // Tests a variety of different URIs using the examples in RFC 3986.
+    TEST(RFC_3968_examples_string)
+    {
+        uri ftp(U("ftp://ftp.is.co.za/rfc/rfc1808.txt"));
+        VERIFY_ARE_EQUAL(U("ftp"), ftp.scheme());
+        VERIFY_ARE_EQUAL(U(""), ftp.user_info());
+        VERIFY_ARE_EQUAL(U("ftp.is.co.za"), ftp.host());
+        VERIFY_ARE_EQUAL(0, ftp.port());
+        VERIFY_ARE_EQUAL(U("/rfc/rfc1808.txt"), ftp.path());
+        VERIFY_ARE_EQUAL(U(""), ftp.query());
+        VERIFY_ARE_EQUAL(U(""), ftp.fragment());
+
+        // TFS #371892
+        // uri ldap(U("ldap://[2001:db8::7]/?c=GB#objectClass?one"));
+        // VERIFY_ARE_EQUAL(U("ldap"), ldap.scheme());
+        // VERIFY_ARE_EQUAL(U(""), ldap.user_info());
+        // VERIFY_ARE_EQUAL(U("2001:db8::7"), ldap.host());
+        // VERIFY_ARE_EQUAL(0, ldap.port());
+        // VERIFY_ARE_EQUAL(U("/"), ldap.path());
+        // VERIFY_ARE_EQUAL(U("c=GB"), ldap.query());
+        // VERIFY_ARE_EQUAL(U("objectClass?one"), ldap.fragment());
+
+        // We don't support anything scheme specific like in C# so
+        // these common ones don't have a great experience yet.
+        uri mailto(U("mailto:John.Doe@example.com"));
+        VERIFY_ARE_EQUAL(U("mailto"), mailto.scheme());
+        VERIFY_ARE_EQUAL(U(""), mailto.user_info());
+        VERIFY_ARE_EQUAL(U(""), mailto.host());
+        VERIFY_ARE_EQUAL(0, mailto.port());
+        VERIFY_ARE_EQUAL(U("John.Doe@example.com"), mailto.path());
+        VERIFY_ARE_EQUAL(U(""), mailto.query());
+        VERIFY_ARE_EQUAL(U(""), mailto.fragment());
+
+        uri tel(U("tel:+1-816-555-1212"));
+        VERIFY_ARE_EQUAL(U("tel"), tel.scheme());
+        VERIFY_ARE_EQUAL(U(""), tel.user_info());
+        VERIFY_ARE_EQUAL(U(""), tel.host());
+        VERIFY_ARE_EQUAL(0, tel.port());
+        VERIFY_ARE_EQUAL(U("+1-816-555-1212"), tel.path());
+        VERIFY_ARE_EQUAL(U(""), tel.query());
+        VERIFY_ARE_EQUAL(U(""), tel.fragment());
+
+        uri telnet(U("telnet://192.0.2.16:80/"));
+        VERIFY_ARE_EQUAL(U("telnet"), telnet.scheme());
+        VERIFY_ARE_EQUAL(U(""), telnet.user_info());
+        VERIFY_ARE_EQUAL(U("192.0.2.16"), telnet.host());
+        VERIFY_ARE_EQUAL(80, telnet.port());
+        VERIFY_ARE_EQUAL(U("/"), telnet.path());
+        VERIFY_ARE_EQUAL(U(""), telnet.query());
+        VERIFY_ARE_EQUAL(U(""), telnet.fragment());
+    }
+
+    TEST(user_info_string)
+    {
+        uri ftp(U("ftp://johndoe:testname@ftp.is.co.za/rfc/rfc1808.txt"));
+        VERIFY_ARE_EQUAL(U("ftp"), ftp.scheme());
+        VERIFY_ARE_EQUAL(U("johndoe:testname"), ftp.user_info());
+        VERIFY_ARE_EQUAL(U("ftp.is.co.za"), ftp.host());
+        VERIFY_ARE_EQUAL(0, ftp.port());
+        VERIFY_ARE_EQUAL(U("/rfc/rfc1808.txt"), ftp.path());
+        VERIFY_ARE_EQUAL(U(""), ftp.query());
+        VERIFY_ARE_EQUAL(U(""), ftp.fragment());
+    }
+
+    // Test query component can be separated with '&' or ';'.
+    TEST(query_seperated_with_semi_colon)
+    {
+        uri u(U("http://localhost/path1?key1=val1;key2=val2"));
+        VERIFY_ARE_EQUAL(U("key1=val1;key2=val2"), u.query());
+    }
+
+} // SUITE(constructor_tests)
+
+} // namespace uri_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/conversions_tests.cpp b/Release/tests/functional/uri/conversions_tests.cpp
new file mode 100644 (file)
index 0000000..ba99c74
--- /dev/null
@@ -0,0 +1,53 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * conversion_tests.cpp
+ *
+ * Tests to string functions and implicit conversions of the http::uri class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+SUITE(conversions_tests)
+{
+    TEST(to_string_conversion)
+    {
+        utility::string_t encoded = uri::encode_uri(U("http://testname.com/%%?qstring"));
+        uri u1(U("http://testname.com/%25%25?qstring"));
+
+        VERIFY_ARE_EQUAL(uri::decode(encoded), uri::decode(u1.to_string()));
+    }
+
+    TEST(to_encoded_string)
+    {
+        utility::string_t encoded = uri::encode_uri(U("http://testname.com/%%?qstring"));
+        uri u(U("http://testname.com/%25%25?qstring"));
+
+        VERIFY_ARE_EQUAL(encoded, u.to_string());
+    }
+
+    TEST(empty_to_string)
+    {
+        uri u;
+        VERIFY_ARE_EQUAL(U("/"), u.to_string());
+    }
+
+} // SUITE(conversions_tests)
+
+} // namespace uri_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/diagnostic_tests.cpp b/Release/tests/functional/uri/diagnostic_tests.cpp
new file mode 100644 (file)
index 0000000..d8fb45d
--- /dev/null
@@ -0,0 +1,117 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * diagnostic_tests.cpp
+ *
+ * Tests for diagnostic functions like is_host_loopback of the uri class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+SUITE(diagnostic_tests)
+{
+    TEST(empty_components)
+    {
+        VERIFY_IS_TRUE(uri().is_empty());
+
+        VERIFY_IS_FALSE(uri().is_authority());
+
+        VERIFY_IS_FALSE(uri().is_host_loopback());
+        VERIFY_IS_FALSE(uri().is_host_wildcard());
+        VERIFY_IS_FALSE(uri().is_host_portable());
+
+        VERIFY_IS_FALSE(uri().is_port_default());
+    }
+
+    TEST(is_authority)
+    {
+        VERIFY_IS_TRUE(uri(U("http://first.second/")).is_authority());
+        VERIFY_IS_TRUE(uri(U("http://first.second")).is_authority());
+
+        VERIFY_IS_FALSE(uri(U("http://first.second/b")).is_authority());
+        VERIFY_IS_FALSE(uri(U("http://first.second?qstring")).is_authority());
+        VERIFY_IS_FALSE(uri(U("http://first.second#third")).is_authority());
+    }
+
+    TEST(has_same_authority)
+    {
+        VERIFY_IS_TRUE(uri(U("http://first.second/")).has_same_authority(uri(U("http://first.second/path"))));
+        VERIFY_IS_TRUE(uri(U("http://first.second:83/")).has_same_authority(uri(U("http://first.second:83/path:83"))));
+
+        VERIFY_IS_FALSE(uri(U("http://first.second:82/")).has_same_authority(uri(U("http://first.second/path"))));
+        VERIFY_IS_FALSE(uri(U("tcp://first.second:82/")).has_same_authority(uri(U("http://first.second/path"))));
+        VERIFY_IS_FALSE(uri(U("http://path.:82/")).has_same_authority(uri(U("http://first.second/path"))));
+    }
+
+    TEST(has_same_authority_empty)
+    {
+        VERIFY_IS_FALSE(uri().has_same_authority(uri()));
+        VERIFY_IS_FALSE(uri(U("http://first.second/")).has_same_authority(uri()));
+        VERIFY_IS_FALSE(uri().has_same_authority(uri(U("http://first.second/"))));
+    }
+
+    TEST(is_host_wildcard)
+    {
+        VERIFY_IS_TRUE(uri(U("http://*/")).is_host_wildcard());
+        VERIFY_IS_TRUE(uri(U("http://+/?qstring")).is_host_wildcard());
+
+        VERIFY_IS_FALSE(uri(U("http://bleh/?qstring")).is_host_wildcard());
+        VERIFY_IS_FALSE(uri(U("http://+*/?qstring")).is_host_wildcard());
+    }
+
+    TEST(is_host_loopback)
+    {
+        VERIFY_IS_TRUE(uri(U("http://localhost/")).is_host_loopback());
+        VERIFY_IS_TRUE(uri(U("http://LoCALHoST/")).is_host_loopback());
+
+        VERIFY_IS_FALSE(uri(U("http://127")).is_host_loopback());
+        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://128.0.0.1/")).is_host_loopback());
+    }
+
+    TEST(is_host_portable)
+    {
+        VERIFY_IS_TRUE(uri(U("http://bleh/?qstring")).is_host_portable());
+
+        VERIFY_IS_FALSE(uri(U("http://localhost/")).is_host_portable());
+        VERIFY_IS_FALSE(uri(U("http://+/?qstring")).is_host_portable());
+    }
+
+    TEST(is_port_default)
+    {
+        VERIFY_IS_TRUE(uri(U("http://bleh/?qstring")).is_port_default());
+        VERIFY_IS_TRUE(uri(U("http://localhost:0/")).is_port_default());
+
+        VERIFY_IS_FALSE(uri(U("http://+:85/?qstring")).is_port_default());
+    }
+
+    TEST(is_path_empty)
+    {
+        VERIFY_IS_TRUE(uri(U("http://bleh/?qstring")).is_path_empty());
+        VERIFY_IS_TRUE(uri(U("http://localhost:0")).is_path_empty());
+
+        VERIFY_IS_FALSE(uri(U("http://+:85/path/?qstring")).is_path_empty());
+    }
+
+} // SUITE(diagnostic_tests)
+
+} // namespace uri_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/encoding_tests.cpp b/Release/tests/functional/uri/encoding_tests.cpp
new file mode 100644 (file)
index 0000000..535bc2a
--- /dev/null
@@ -0,0 +1,134 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Tests for encoding features of the uri class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+SUITE(encoding_tests)
+{
+#ifdef _WIN32
+#pragma warning(push)
+#pragma warning(disable : 4428)
+    TEST(encode_string)
+    {
+        utility::string_t result = uri::encode_uri(L"first%second\u4e2d\u56fd");
+        VERIFY_ARE_EQUAL(U("first%25second%E4%B8%AD%E5%9B%BD"), result);
+
+        result = uri::encode_uri(U("first%second"));
+        VERIFY_ARE_EQUAL(U("first%25second"), result);
+    }
+
+    TEST(decode_string)
+    {
+        utility::string_t result = uri::decode(U("first%25second%E4%B8%AD%E5%9B%BD"));
+        VERIFY_ARE_EQUAL(L"first%second\u4e2d\u56fd", result);
+
+        result = uri::decode(U("first%25second"));
+        VERIFY_ARE_EQUAL(U("first%second"), result);
+    }
+#pragma warning(pop)
+#endif
+
+    TEST(encode_characters_in_resource)
+    {
+        utility::string_t result = uri::encode_uri(U("http://path%name/%#!%"));
+        VERIFY_ARE_EQUAL(U("http://path%25name/%25#!%25"), result);
+    }
+
+    // Tests trying to encode empty strings.
+    TEST(encode_decode_empty_strings)
+    {
+        // utility::string_t
+        utility::string_t result = uri::encode_uri(U(""));
+        VERIFY_ARE_EQUAL(U(""), result);
+        utility::string_t str = uri::decode(result);
+        VERIFY_ARE_EQUAL(U(""), str);
+
+        // std::wstring
+        result = uri::encode_uri(U(""));
+        VERIFY_ARE_EQUAL(U(""), result);
+        auto wstr = uri::decode(result);
+        VERIFY_ARE_EQUAL(U(""), wstr);
+    }
+
+    // Tests encoding in various components of the URI.
+    TEST(encode_uri_multiple_components)
+    {
+        // only encodes characters that aren't in the unreserved and reserved set.
+
+        // utility::string_t
+        utility::string_t str(U("htt p://^localhost:80/path ?^one=two# frag"));
+        utility::string_t result = uri::encode_uri(str);
+        VERIFY_ARE_EQUAL(U("htt%20p://%5Elocalhost:80/path%20?%5Eone=two#%20frag"), result);
+        VERIFY_ARE_EQUAL(str, uri::decode(result));
+    }
+
+    // Tests encoding individual components of a URI.
+    TEST(encode_uri_component)
+    {
+        // encodes all characters not in the unreserved set.
+
+        // utility::string_t
+        utility::string_t str(U("path with^spaced"));
+        utility::string_t result = uri::encode_uri(str);
+        VERIFY_ARE_EQUAL(U("path%20with%5Espaced"), result);
+        VERIFY_ARE_EQUAL(str, uri::decode(result));
+    }
+
+    // Tests trying to decode a string that doesn't have 2 hex digits after %
+    TEST(decode_invalid_hex)
+    {
+        VERIFY_THROWS(uri::decode(U("hehe%")), uri_exception);
+        VERIFY_THROWS(uri::decode(U("hehe%2")), uri_exception);
+        VERIFY_THROWS(uri::decode(U("hehe%4H")), uri_exception);
+        VERIFY_THROWS(uri::decode(U("he%kkhe")), uri_exception);
+    }
+
+    // Tests making sure '+' is encoded even though nonstandard, so it doesn't
+    // get mistaken later by some implementations as a space.
+    TEST(encode_plus_char)
+    {
+        const utility::string_t encodedPlus(U("%2B"));
+
+        uri_builder builder;
+        builder.set_user_info(U("+"), true);
+        builder.set_path(U("+"), true);
+        builder.set_query(U("+"), true);
+        builder.set_fragment(U("+"), true);
+
+        VERIFY_ARE_EQUAL(builder.user_info(), encodedPlus);
+        VERIFY_ARE_EQUAL(builder.path(), encodedPlus);
+        VERIFY_ARE_EQUAL(builder.query(), encodedPlus);
+        VERIFY_ARE_EQUAL(builder.fragment(), encodedPlus);
+    }
+
+    TEST(bug_417601)
+    {
+        utility::ostringstream_t ss1;
+        auto enc1 = uri::encode_data_string(U("!"));
+        ss1 << enc1;
+
+        VERIFY_ARE_EQUAL(U("%21"), ss1.str());
+    }
+
+} // SUITE(encoding_tests)
+
+} // namespace uri_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/operator_tests.cpp b/Release/tests/functional/uri/operator_tests.cpp
new file mode 100644 (file)
index 0000000..b76475e
--- /dev/null
@@ -0,0 +1,80 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * operator_tests.cpp
+ *
+ * Tests for operators of the uri class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+SUITE(operator_tests)
+{
+    TEST(uri_basic_equality)
+    {
+        VERIFY_ARE_EQUAL(uri(U("")), uri(U("")));
+
+        uri u1(U("http://localhost:80/path1"));
+        uri u2(U("http://localhost:80/path1"));
+        VERIFY_ARE_EQUAL(u1, u2);
+    }
+
+    TEST(uri_decoded_equality)
+    {
+        uri_builder u3b(U("http://localhost:80"));
+        u3b.set_path(U("path 1"), true);
+
+        uri u3 = u3b.to_uri();
+        uri u4(U("http://localhost:80/path%201"));
+        VERIFY_ARE_EQUAL(u3, u4);
+
+        uri u5(U("http://localhost:80/pat%68a1"));
+        uri u6(U("http://localhost:80/patha1"));
+        VERIFY_ARE_EQUAL(u5, u6);
+
+        uri u9(U("http://localhost:80/patha1?name=first#t%65st"));
+        uri u10(U("http://localhost:80/patha1?name=first#test"));
+        VERIFY_ARE_EQUAL(u9, u10);
+    }
+
+    TEST(uri_basic_inequality)
+    {
+        VERIFY_ARE_NOT_EQUAL(uri(U("http://localhost:80/path1")), uri(U("https://localhost:80/path1")));
+        VERIFY_ARE_NOT_EQUAL(uri(U("http://localhost:80/path1")), uri(U("http://localhost2:80/path1")));
+        VERIFY_ARE_NOT_EQUAL(uri(U("http://localhost:80/path1")), uri(U("http://localhost:81/path1")));
+        VERIFY_ARE_NOT_EQUAL(uri(U("http://localhost:80/path1")), uri(U("http://localhost:80/path2")));
+        VERIFY_ARE_NOT_EQUAL(uri(U("http://localhost:80/path1?key=value")),
+                             uri(U("http://localhost:80/path1?key=value2")));
+        VERIFY_ARE_NOT_EQUAL(uri(U("http://localhost:80/path1?key=value#nose")),
+                             uri(U("http://localhost:80/path1?key=value#nose1")));
+    }
+
+    TEST(test_empty)
+    {
+        VERIFY_ARE_EQUAL(uri(), uri());
+        VERIFY_ARE_EQUAL(uri(U("htTp://Path")), uri(U("hTtp://pAth")));
+
+        VERIFY_ARE_NOT_EQUAL(uri(U("http://path")), uri());
+        VERIFY_ARE_NOT_EQUAL(uri(), uri(U("http://path")));
+        VERIFY_ARE_NOT_EQUAL(uri(U("http://path1")), uri(U("http://path2")));
+    }
+
+} // SUITE(operator_tests)
+
+} // namespace uri_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/resolve_uri_tests.cpp b/Release/tests/functional/uri/resolve_uri_tests.cpp
new file mode 100644 (file)
index 0000000..537b5a4
--- /dev/null
@@ -0,0 +1,74 @@
+#include "stdafx.h"
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+// testing resolution against examples from Section 5.4 https://tools.ietf.org/html/rfc3986#section-5.4
+SUITE(resolve_uri_tests)
+{
+    // 5.4.1. Normal Examples https://tools.ietf.org/html/rfc3986#section-5.4.1
+    TEST(resolve_uri_normal)
+    {
+        const uri baseUri = U("http://a/b/c/d;p?q");
+
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g:h")), U("g:h"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g")), U("http://a/b/c/g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./g")), U("http://a/b/c/g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/")), U("http://a/b/c/g/"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/g")), U("http://a/g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("//g")), U("http://g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("?y")), U("http://a/b/c/d;p?y"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y")), U("http://a/b/c/g?y"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("#s")), U("http://a/b/c/d;p?q#s"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s")), U("http://a/b/c/g#s"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y#s")), U("http://a/b/c/g?y#s"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(";x")), U("http://a/b/c/;x"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x")), U("http://a/b/c/g;x"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x?y#s")), U("http://a/b/c/g;x?y#s"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("")), U("http://a/b/c/d;p?q"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(".")), U("http://a/b/c/"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./")), U("http://a/b/c/"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("..")), U("http://a/b/"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../")), U("http://a/b/"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../g")), U("http://a/b/g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../..")), U("http://a/"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../")), U("http://a/"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../g")), U("http://a/g"));
+    }
+    // 5.4.2. Abnormal Examples https://tools.ietf.org/html/rfc3986#section-5.4.2
+    TEST(resolve_uri_abnormal)
+    {
+        const uri baseUri = U("http://a/b/c/d;p?q");
+
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../../g")), U("http://a/g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../../../g")), U("http://a/g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/./g")), U("http://a/g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/../g")), U("http://a/g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g.")), U("http://a/b/c/g."));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(".g")), U("http://a/b/c/.g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g..")), U("http://a/b/c/g.."));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("..g")), U("http://a/b/c/..g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./../g")), U("http://a/b/g"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./g/.")), U("http://a/b/c/g/"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/./h")), U("http://a/b/c/g/h"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/../h")), U("http://a/b/c/h"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x=1/./y")), U("http://a/b/c/g;x=1/y"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x=1/../y")), U("http://a/b/c/y"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y/./x")), U("http://a/b/c/g?y/./x"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y/../x")), U("http://a/b/c/g?y/../x"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s/./x")), U("http://a/b/c/g#s/./x"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s/../x")), U("http://a/b/c/g#s/../x"));
+        VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("http:g")), U("http:g"));
+    }
+
+} // SUITE(resolve_uri_tests)
+
+} // namespace uri_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/splitting_tests.cpp b/Release/tests/functional/uri/splitting_tests.cpp
new file mode 100644 (file)
index 0000000..918e966
--- /dev/null
@@ -0,0 +1,181 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * splitting_tests.cpp
+ *
+ * Tests for path and query splitting features of the uri class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+using namespace web;
+using namespace utility;
+
+SUITE(splitting_tests)
+{
+    TEST(split_string)
+    {
+        std::vector<utility::string_t> s = uri::split_path(U("/first/second/third"));
+        VERIFY_ARE_EQUAL(3u, s.size());
+        VERIFY_ARE_EQUAL(U("first"), s[0]);
+        VERIFY_ARE_EQUAL(U("second"), s[1]);
+        VERIFY_ARE_EQUAL(U("third"), s[2]);
+    }
+
+    TEST(split_encoded_string)
+    {
+        std::vector<utility::string_t> s = uri::split_path(utility::string_t(U("heh%2Ffirst/second/third")));
+        VERIFY_ARE_EQUAL(3u, s.size());
+        VERIFY_ARE_EQUAL(U("heh%2Ffirst"), s[0]);
+        VERIFY_ARE_EQUAL(U("second"), s[1]);
+        VERIFY_ARE_EQUAL(U("third"), s[2]);
+    }
+
+    TEST(split_no_slash)
+    {
+        std::vector<utility::string_t> s = uri::split_path(utility::string_t(U("noslash")));
+        VERIFY_ARE_EQUAL(1u, s.size());
+        VERIFY_ARE_EQUAL(U("noslash"), s[0]);
+    }
+
+    TEST(split_query_basic)
+    {
+        {
+            // Separating with '&'
+            std::map<utility::string_t, utility::string_t> keyMap =
+                uri::split_query(U("key1=value1&key2=value2&key3=value3"));
+            VERIFY_ARE_EQUAL(3u, keyMap.size());
+            auto iter = keyMap.begin();
+            VERIFY_ARE_EQUAL(U("key1"), iter->first);
+            VERIFY_ARE_EQUAL(U("value1"), iter->second);
+            ++iter;
+            VERIFY_ARE_EQUAL(U("key2"), iter->first);
+            VERIFY_ARE_EQUAL(U("value2"), iter->second);
+            ++iter;
+            VERIFY_ARE_EQUAL(U("key3"), iter->first);
+            VERIFY_ARE_EQUAL(U("value3"), iter->second);
+        }
+        {
+            // Separating with ';'
+            std::map<utility::string_t, utility::string_t> keyMap =
+                uri::split_query(U("key1=value1;key2=value2;key3=value3"));
+            VERIFY_ARE_EQUAL(3u, keyMap.size());
+            auto iter = keyMap.begin();
+            VERIFY_ARE_EQUAL(U("key1"), iter->first);
+            VERIFY_ARE_EQUAL(U("value1"), iter->second);
+            ++iter;
+            VERIFY_ARE_EQUAL(U("key2"), iter->first);
+            VERIFY_ARE_EQUAL(U("value2"), iter->second);
+            ++iter;
+            VERIFY_ARE_EQUAL(U("key3"), iter->first);
+            VERIFY_ARE_EQUAL(U("value3"), iter->second);
+        }
+    }
+
+    TEST(split_encoded_query)
+    {
+        {
+            // Separating with '&'
+            std::map<utility::string_t, utility::string_t> keyMap =
+                uri::split_query(utility::string_t(U("key=value%26key1%20=value1&key2=%5Evalue2&key3=value3%20")));
+            VERIFY_ARE_EQUAL(3u, keyMap.size());
+            auto iter = keyMap.begin();
+            VERIFY_ARE_EQUAL(U("key"), iter->first);
+            VERIFY_ARE_EQUAL(U("value%26key1%20=value1"), iter->second);
+            ++iter;
+            VERIFY_ARE_EQUAL(U("key2"), iter->first);
+            VERIFY_ARE_EQUAL(U("%5Evalue2"), iter->second);
+            ++iter;
+            VERIFY_ARE_EQUAL(U("key3"), iter->first);
+            VERIFY_ARE_EQUAL(U("value3%20"), iter->second);
+        }
+        {
+            // Separating with ';'
+            std::map<utility::string_t, utility::string_t> keyMap =
+                uri::split_query(utility::string_t(U("key=value%26key1%20=value1;key2=%5Evalue2;key3=value3%20")));
+            VERIFY_ARE_EQUAL(3u, keyMap.size());
+            auto iter = keyMap.begin();
+            VERIFY_ARE_EQUAL(U("key"), iter->first);
+            VERIFY_ARE_EQUAL(U("value%26key1%20=value1"), iter->second);
+            ++iter;
+            VERIFY_ARE_EQUAL(U("key2"), iter->first);
+            VERIFY_ARE_EQUAL(U("%5Evalue2"), iter->second);
+            ++iter;
+            VERIFY_ARE_EQUAL(U("key3"), iter->first);
+            VERIFY_ARE_EQUAL(U("value3%20"), iter->second);
+        }
+    }
+
+    TEST(split_query_empty)
+    {
+        std::map<utility::string_t, utility::string_t> keyMap = uri::split_query(U(""));
+        VERIFY_ARE_EQUAL(0u, keyMap.size());
+    }
+
+    TEST(split_query_single)
+    {
+        std::map<utility::string_t, utility::string_t> keyMap = uri::split_query(U("key1=44"));
+        VERIFY_ARE_EQUAL(1u, keyMap.size());
+        auto iter = keyMap.begin();
+        VERIFY_ARE_EQUAL(U("key1"), iter->first);
+        VERIFY_ARE_EQUAL(U("44"), iter->second);
+    }
+
+    TEST(split_query_no_value)
+    {
+        std::map<utility::string_t, utility::string_t> keyMap = uri::split_query(U("key1"));
+        VERIFY_ARE_EQUAL(0u, keyMap.size());
+        keyMap = uri::split_query(U("key1="));
+        VERIFY_ARE_EQUAL(1u, keyMap.size());
+        auto iter = keyMap.begin();
+        VERIFY_ARE_EQUAL(U("key1"), iter->first);
+        VERIFY_ARE_EQUAL(U(""), iter->second);
+        keyMap = uri::split_query(U("key1&"));
+        VERIFY_ARE_EQUAL(0u, keyMap.size());
+    }
+
+    TEST(split_query_no_key)
+    {
+        std::map<utility::string_t, utility::string_t> keyMap = uri::split_query(U("=value1"));
+        VERIFY_ARE_EQUAL(1u, keyMap.size());
+        auto iter = keyMap.begin();
+        VERIFY_ARE_EQUAL(U(""), iter->first);
+        VERIFY_ARE_EQUAL(U("value1"), iter->second);
+    }
+
+    TEST(split_query_end_with_amp)
+    {
+        {
+            // Separating with '&'
+            std::map<utility::string_t, utility::string_t> keyMap = uri::split_query(U("key1=44&"));
+            VERIFY_ARE_EQUAL(1u, keyMap.size());
+            auto iter = keyMap.begin();
+            VERIFY_ARE_EQUAL(U("key1"), iter->first);
+            VERIFY_ARE_EQUAL(U("44"), iter->second);
+        }
+        {
+            // Separating with ';'
+            std::map<utility::string_t, utility::string_t> keyMap = uri::split_query(U("key1=44;"));
+            VERIFY_ARE_EQUAL(1u, keyMap.size());
+            auto iter = keyMap.begin();
+            VERIFY_ARE_EQUAL(U("key1"), iter->first);
+            VERIFY_ARE_EQUAL(U("44"), iter->second);
+        }
+    }
+
+} // SUITE(splitting_tests)
+
+} // namespace uri_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/stdafx.cpp b/Release/tests/functional/uri/stdafx.cpp
new file mode 100644 (file)
index 0000000..3ca0605
--- /dev/null
@@ -0,0 +1,15 @@
+/***
+ * 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 uri_test_generate_lib = 0;
+#endif
diff --git a/Release/tests/functional/uri/stdafx.h b/Release/tests/functional/uri/stdafx.h
new file mode 100644 (file)
index 0000000..4086ad4
--- /dev/null
@@ -0,0 +1,19 @@
+/***
+ * 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 "cpprest/asyncrt_utils.h"
+#include "cpprest/uri.h"
+#include "unittestpp.h"
+#include "uri_tests.h"
diff --git a/Release/tests/functional/uri/uri_builder_tests.cpp b/Release/tests/functional/uri/uri_builder_tests.cpp
new file mode 100644 (file)
index 0000000..5a869a0
--- /dev/null
@@ -0,0 +1,610 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Tests for the URI builder class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <locale_guard.h>
+
+using namespace web;
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+// Helper functions to verify components of a builder.
+static void VERIFY_URI_BUILDER(uri_builder& builder,
+                               const utility::string_t& scheme,
+                               const utility::string_t& user_info,
+                               const utility::string_t& host,
+                               const int port,
+                               const utility::string_t& path,
+                               const utility::string_t& query,
+                               const utility::string_t& fragment)
+{
+    VERIFY_ARE_EQUAL(scheme, builder.scheme());
+    VERIFY_ARE_EQUAL(host, builder.host());
+    VERIFY_ARE_EQUAL(user_info, builder.user_info());
+    VERIFY_ARE_EQUAL(port, builder.port());
+    VERIFY_ARE_EQUAL(path, builder.path());
+    VERIFY_ARE_EQUAL(query, builder.query());
+    VERIFY_ARE_EQUAL(fragment, builder.fragment());
+}
+static void VERIFY_URI_BUILDER(uri_builder& builder,
+                               const utility::string_t& scheme,
+                               const utility::string_t& host,
+                               const int port)
+{
+    VERIFY_URI_BUILDER(builder,
+                       scheme,
+                       utility::string_t(),
+                       host,
+                       port,
+                       utility::string_t(U("/")),
+                       utility::string_t(),
+                       utility::string_t());
+}
+static void VERIFY_URI_BUILDER_IS_EMPTY(uri_builder& builder)
+{
+    VERIFY_URI_BUILDER(builder,
+                       utility::string_t(),
+                       utility::string_t(),
+                       utility::string_t(),
+                       -1,
+                       utility::string_t(U("/")),
+                       utility::string_t(),
+                       utility::string_t());
+}
+
+SUITE(uri_builder_tests)
+{
+    TEST(constructor_tests)
+    {
+        // Default constructor
+        uri_builder builder;
+        VERIFY_URI_BUILDER_IS_EMPTY(builder);
+        // scheme, user_info, host, port
+        utility::string_t scheme(U("ftp"));
+        utility::string_t user_info(U("steve:pass"));
+        utility::string_t host(U("localhost"));
+        int port = 44;
+        utility::string_t path(U("/Yeshere888"));
+        utility::string_t uri_str(U("ftp://steve:pass@localhost:44/Yeshere888"));
+
+        // utility::string_t
+        utility::string_t uri_wstr(U("ftp://steve:pass@localhost:44/Yeshere888?abc:123&abc2:456#nose"));
+        builder = uri_builder(uri_wstr);
+        VERIFY_URI_BUILDER(builder,
+                           scheme,
+                           user_info,
+                           host,
+                           port,
+                           path,
+                           utility::string_t(U("abc:123&abc2:456")),
+                           utility::string_t(U("nose")));
+
+        // copy constructor
+        uri_builder other(builder);
+        builder = uri_builder(uri_str);
+        VERIFY_URI_BUILDER(other,
+                           scheme,
+                           user_info,
+                           host,
+                           port,
+                           path,
+                           utility::string_t(U("abc:123&abc2:456")),
+                           utility::string_t(U("nose")));
+        VERIFY_URI_BUILDER(builder, scheme, user_info, host, port, path, U(""), U(""));
+
+        // move constructor
+        uri_builder move_other = std::move(builder);
+        VERIFY_URI_BUILDER(move_other, scheme, user_info, host, port, path, U(""), U(""));
+    }
+
+    TEST(assignment_operators)
+    {
+        // assignment operator
+        const utility::string_t scheme = U("http"), host = U("localhost");
+        const int port = 44;
+        uri_builder original;
+        original.set_scheme(scheme).set_host(host).set_port(port);
+        uri_builder assign;
+        assign = original;
+        VERIFY_URI_BUILDER(assign, scheme, utility::string_t(host), port);
+
+        // move assignment operator
+        uri_builder move_assign;
+        move_assign = std::move(original);
+        VERIFY_URI_BUILDER(assign, scheme, utility::string_t(host), port);
+    }
+
+    TEST(set_port_as_string)
+    {
+        uri_builder builder;
+
+        VERIFY_THROWS(builder.set_port(U("")), std::invalid_argument);
+        VERIFY_ARE_EQUAL(-1, builder.port());
+
+        builder.set_port(U("987"));
+        VERIFY_ARE_EQUAL(987, builder.port());
+
+        VERIFY_THROWS(builder.set_port(U("abc")), std::invalid_argument);
+        VERIFY_ARE_EQUAL(987, builder.port());
+
+        builder.set_port(U(" 44 "));
+        VERIFY_ARE_EQUAL(44, builder.port());
+
+        builder.set_port(U("99"));
+        VERIFY_ARE_EQUAL(99, builder.port());
+    }
+
+    TEST(component_assignment)
+    {
+        uri_builder builder;
+        const utility::string_t scheme(U("myscheme"));
+        const utility::string_t uinfo(U("johndoe:test"));
+        const utility::string_t host(U("localhost"));
+        const int port = 88;
+        const utility::string_t path(U("jklajsd"));
+        const utility::string_t query(U("key1=val1"));
+        const utility::string_t fragment(U("last"));
+
+        builder.set_scheme(scheme);
+        builder.set_user_info(uinfo);
+        builder.set_host(host);
+        builder.set_port(port);
+        builder.set_path(path);
+        builder.set_query(query);
+        builder.set_fragment(fragment);
+
+        VERIFY_URI_BUILDER(builder, scheme, uinfo, host, port, path, query, fragment);
+    }
+
+    TEST(component_assignment_encode)
+    {
+        {
+            uri_builder builder;
+            const utility::string_t scheme(U("myscheme"));
+            const utility::string_t uinfo(U("johndoe:test"));
+            const utility::string_t host(U("localhost"));
+            const int port = 88;
+            const utility::string_t path(U("jklajsd/yes no"));
+            const utility::string_t query(U("key1=va%l1"));
+            const utility::string_t fragment(U("las t"));
+
+            builder.set_scheme(scheme);
+            builder.set_user_info(uinfo, true);
+            builder.set_host(host, true);
+            builder.set_port(port);
+            builder.set_path(path, true);
+            builder.set_query(query, true);
+            builder.set_fragment(fragment, true);
+
+            VERIFY_URI_BUILDER(builder,
+                               scheme,
+                               utility::string_t(U("johndoe:test")),
+                               utility::string_t(U("localhost")),
+                               port,
+                               utility::string_t(U("jklajsd/yes%20no")),
+                               utility::string_t(U("key1=va%25l1")),
+                               utility::string_t(U("las%20t")));
+        }
+        {
+            uri_builder builder;
+            const utility::string_t scheme(U("myscheme"));
+            const utility::string_t uinfo(U("johndoe:test"));
+            const utility::string_t host(U("localhost"));
+            const int port = 88;
+            const utility::string_t path(U("jklajsd/yes no"));
+            const utility::string_t query(U("key1=va%l1"));
+            const utility::string_t fragment(U("las t"));
+
+            builder.set_scheme(scheme);
+            builder.set_user_info(uinfo, true);
+            builder.set_host(host, true);
+            builder.set_port(port);
+            builder.set_path(path, true);
+            builder.set_query(query, true);
+            builder.set_fragment(fragment, true);
+
+            VERIFY_URI_BUILDER(builder,
+                               scheme,
+                               utility::string_t(U("johndoe:test")),
+                               utility::string_t(U("localhost")),
+                               port,
+                               utility::string_t(U("jklajsd/yes%20no")),
+                               utility::string_t(U("key1=va%25l1")),
+                               utility::string_t(U("las%20t")));
+        }
+    }
+
+    TEST(validation)
+    {
+        {
+            // true
+            uri_builder builder(U("http://localhost:4567/"));
+            VERIFY_IS_TRUE(builder.is_valid());
+
+            // false
+            builder = uri_builder();
+            builder.set_scheme(U("123"));
+            VERIFY_IS_FALSE(builder.is_valid());
+        }
+        {
+            // true
+            uri_builder builder(U("http://localhost:4567/"));
+            VERIFY_IS_TRUE(builder.is_valid());
+
+            // false
+            builder = uri_builder();
+            builder.set_scheme(U("123"));
+            VERIFY_IS_FALSE(builder.is_valid());
+        }
+    }
+
+    TEST(uri_creation_string)
+    {
+        utility::string_t uri_str(U("http://steve:temp@localhost:4556/"));
+
+        // to_string
+        uri_builder builder(uri_str);
+        VERIFY_ARE_EQUAL(uri_str, builder.to_string());
+
+        // to_string
+        VERIFY_ARE_EQUAL(uri_str, builder.to_string());
+
+        // to uri
+        VERIFY_ARE_EQUAL(uri_str, builder.to_uri().to_string());
+
+        // to encoded string
+        uri_builder with_space(builder);
+        with_space.set_path(utility::string_t(U("path%20with%20space")));
+        VERIFY_ARE_EQUAL(U("http://steve:temp@localhost:4556/path%20with%20space"), with_space.to_string());
+    }
+
+    TEST(append_path_string)
+    {
+        // empty uri builder path
+        uri_builder builder;
+        builder.append_path(U("/path1"));
+        VERIFY_ARE_EQUAL(U("/path1"), builder.path());
+
+        // empty append path
+        builder.append_path(U(""));
+        VERIFY_ARE_EQUAL(U("/path1"), builder.path());
+
+        // uri builder with slash
+        builder.append_path(U("/"));
+        builder.append_path(U("path2"));
+        VERIFY_ARE_EQUAL(U("/path1/path2"), builder.path());
+
+        // both with slash
+        builder.append_path(U("/"));
+        builder.append_path(U("/path3"));
+        VERIFY_ARE_EQUAL(U("/path1/path2/path3"), builder.path());
+
+        // both without slash
+        builder.append_path(U("path4"));
+        VERIFY_ARE_EQUAL(U("/path1/path2/path3/path4"), builder.path());
+
+        // encoding
+        builder.clear();
+        builder.append_path(U("encode%things"));
+        VERIFY_ARE_EQUAL(U("/encode%things"), builder.path());
+
+        builder.clear();
+        builder.append_path(U("encode%things"), false);
+        VERIFY_ARE_EQUAL(U("/encode%things"), builder.path());
+
+        builder.clear();
+        builder.append_path(U("encode%things"), true);
+        VERIFY_ARE_EQUAL(U("/encode%25things"), builder.path());
+
+        // self references
+        builder.set_path(U("example"));
+        builder.append_path(builder.path());
+        VERIFY_ARE_EQUAL(U("example/example"), builder.path());
+
+        builder.set_path(U("/example"));
+        builder.append_path(builder.path());
+        VERIFY_ARE_EQUAL(U("/example/example"), builder.path());
+
+        builder.set_path(U("/example/"));
+        builder.append_path(builder.path());
+        VERIFY_ARE_EQUAL(U("/example/example/"), builder.path());
+    }
+
+    TEST(append_path_raw_string)
+    {
+        // empty uri builder path
+        uri_builder builder;
+        builder.append_path_raw(U("path1"));
+        VERIFY_ARE_EQUAL(U("/path1"), builder.path());
+
+        // empty append path
+        builder.append_path_raw(U(""));
+        VERIFY_ARE_EQUAL(U("/path1"), builder.path());
+
+        // uri builder with slash
+        builder.append_path_raw(U("/"));
+        builder.append_path_raw(U("path2"));
+        VERIFY_ARE_EQUAL(U("/path1///path2"), builder.path());
+
+        // leading slash (should result in "//")
+        builder.append_path_raw(U("/path3"));
+        VERIFY_ARE_EQUAL(U("/path1///path2//path3"), builder.path());
+
+        // trailing slash
+        builder.append_path_raw(U("path4/"));
+        builder.append_path_raw(U("path5"));
+        VERIFY_ARE_EQUAL(U("/path1///path2//path3/path4//path5"), builder.path());
+
+        // encoding
+        builder.clear();
+        builder.append_path_raw(U("encode%things"));
+        VERIFY_ARE_EQUAL(U("/encode%things"), builder.path());
+
+        builder.clear();
+        builder.append_path_raw(U("encode%things"), false);
+        VERIFY_ARE_EQUAL(U("/encode%things"), builder.path());
+
+        builder.clear();
+        builder.append_path_raw(U("encode%things"), true);
+        VERIFY_ARE_EQUAL(U("/encode%25things"), builder.path());
+
+        // self references
+        builder.set_path(U("example"));
+        builder.append_path_raw(builder.path());
+        VERIFY_ARE_EQUAL(U("example/example"), builder.path());
+
+        builder.set_path(U("/example"));
+        builder.append_path_raw(builder.path());
+        VERIFY_ARE_EQUAL(U("/example//example"), builder.path());
+
+        builder.set_path(U("/example/"));
+        builder.append_path_raw(builder.path());
+        VERIFY_ARE_EQUAL(U("/example///example/"), builder.path());
+    }
+
+    TEST(append_query_string)
+    {
+        // empty uri builder query
+        uri_builder builder;
+        builder.append_query(U("key1=value1"));
+        VERIFY_ARE_EQUAL(U("key1=value1"), builder.query());
+
+        // empty append query
+        builder.append_query(U(""));
+        VERIFY_ARE_EQUAL(U("key1=value1"), builder.query());
+
+        // uri builder with ampersand
+        builder.append_query(U("&"));
+        builder.append_query(U("key2=value2"));
+        VERIFY_ARE_EQUAL(U("key1=value1&key2=value2"), builder.query());
+
+        // both with ampersand
+        builder.append_query(U("&"));
+        builder.append_query(U("&key3=value3"));
+        VERIFY_ARE_EQUAL(U("key1=value1&key2=value2&key3=value3"), builder.query());
+
+        // both without ampersand
+        builder.append_query(U("key4=value4"));
+        VERIFY_ARE_EQUAL(U("key1=value1&key2=value2&key3=value3&key4=value4"), builder.query());
+
+        // number query
+        builder.append_query(U("key5"), 1);
+        VERIFY_ARE_EQUAL(U("key1=value1&key2=value2&key3=value3&key4=value4&key5=1"), builder.query());
+
+        // string query
+        builder.append_query(U("key6"), U("val6"));
+        VERIFY_ARE_EQUAL(U("key1=value1&key2=value2&key3=value3&key4=value4&key5=1&key6=val6"), builder.query());
+
+        // key and value separate with '=', '&', and ';'
+        builder.append_query(U("key=&;"), U("=&;value"));
+        VERIFY_ARE_EQUAL(
+            U("key1=value1&key2=value2&key3=value3&key4=value4&key5=1&key6=val6&key%3D%26%3B=%3D%26%3Bvalue"),
+            builder.query());
+
+        // self references
+        builder.set_query(U("example"));
+        builder.append_query(builder.query());
+        VERIFY_ARE_EQUAL(U("example&example"), builder.query());
+
+        builder.set_query(U("&example"));
+        builder.append_query(builder.query());
+        VERIFY_ARE_EQUAL(U("&example&example"), builder.query());
+
+        builder.set_query(U("&example&"));
+        builder.append_query(builder.query());
+        VERIFY_ARE_EQUAL(U("&example&example&"), builder.query());
+    }
+
+    TEST(append_query_string_no_encode)
+    {
+        uri_builder builder;
+        builder.append_query(U("key=&;"), U("=&;value"), false);
+        VERIFY_ARE_EQUAL(U("key=&;==&;value"), builder.query());
+    }
+
+    TEST(append_string)
+    {
+        // with just path
+        uri_builder builder;
+        builder.append(U("/path1"));
+        VERIFY_ARE_EQUAL(U("/path1"), builder.path());
+
+        // with just query
+        builder.append(U("?key1=value1"));
+        VERIFY_ARE_EQUAL(U("/path1"), builder.path());
+        VERIFY_ARE_EQUAL(U("key1=value1"), builder.query());
+        VERIFY_ARE_EQUAL(U("/path1?key1=value1"), builder.to_string());
+
+        // with just fragment
+        builder.append(U("#fragment"));
+        VERIFY_ARE_EQUAL(U("/path1"), builder.path());
+        VERIFY_ARE_EQUAL(U("key1=value1"), builder.query());
+        VERIFY_ARE_EQUAL(U("fragment"), builder.fragment());
+        VERIFY_ARE_EQUAL(U("/path1?key1=value1#fragment"), builder.to_string());
+
+        // with all
+        builder.append(U("/path2?key2=value2#frag2"));
+        VERIFY_ARE_EQUAL(U("/path1/path2"), builder.path());
+        VERIFY_ARE_EQUAL(U("key1=value1&key2=value2"), builder.query());
+        VERIFY_ARE_EQUAL(U("fragmentfrag2"), builder.fragment());
+        VERIFY_ARE_EQUAL(U("/path1/path2?key1=value1&key2=value2#fragmentfrag2"), builder.to_string());
+    }
+
+    TEST(append_empty_string)
+    {
+        utility::string_t uri_str(U("http://uribuilder.com/"));
+        uri_builder builder(uri_str);
+        builder.append(U(""));
+
+        VERIFY_ARE_EQUAL(builder.to_string(), uri_str);
+    }
+
+    TEST(append_path_encoding)
+    {
+        uri_builder builder;
+        builder.append_path(U("/path space"), true);
+        VERIFY_ARE_EQUAL(U("/path%20space"), builder.path());
+
+        builder.append_path(U("path2"));
+        VERIFY_ARE_EQUAL(U("/path%20space/path2"), builder.path());
+    }
+
+    TEST(append_query_encoding)
+    {
+        uri_builder builder;
+        builder.append_query(U("key1 =value2"), true);
+        VERIFY_ARE_EQUAL(U("key1%20=value2"), builder.query());
+
+        builder.append_query(U("key2=value3"));
+        VERIFY_ARE_EQUAL(U("key1%20=value2&key2=value3"), builder.query());
+    }
+
+    TEST(append_encoding)
+    {
+        uri_builder builder;
+        builder.append(uri::encode_uri(U("path space?key =space#frag space")));
+        VERIFY_ARE_EQUAL(U("/path%20space"), builder.path());
+        VERIFY_ARE_EQUAL(U("key%20=space"), builder.query());
+        VERIFY_ARE_EQUAL(U("frag%20space"), builder.fragment());
+        VERIFY_ARE_EQUAL(U("/path%20space?key%20=space#frag%20space"), builder.to_string());
+
+        // try with encoded_string
+        builder = uri_builder();
+        builder.append(U("/path2?key2=value2#frag2"));
+        VERIFY_ARE_EQUAL(U("/path2"), builder.path());
+        VERIFY_ARE_EQUAL(U("key2=value2"), builder.query());
+        VERIFY_ARE_EQUAL(U("frag2"), builder.fragment());
+        VERIFY_ARE_EQUAL(U("/path2?key2=value2#frag2"), builder.to_string());
+    }
+
+    TEST(host_encoding)
+    {
+        // Check that ASCII characters that are invalid in a host name
+        // do not get percent-encoded.
+
+        uri_builder ub1;
+        ub1.set_scheme(U("http")).set_host(U("????dfasddsf!@#$%^&*()_+")).set_port(80);
+
+        VERIFY_IS_FALSE(ub1.is_valid());
+    }
+
+    TEST(clear)
+    {
+        uri_builder ub;
+        ub.clear();
+        CHECK(ub.scheme() == U(""));
+        CHECK(ub.user_info() == U(""));
+        CHECK(ub.host() == U(""));
+        CHECK(ub.port() == -1);
+        CHECK(ub.path() == U("/"));
+        CHECK(ub.query() == U(""));
+        CHECK(ub.fragment() == U(""));
+
+        ub = uri_builder(U("http://myhost.com/path1"));
+        ub.append_path(U("path2"));
+        uri u = ub.to_uri();
+        ub.clear();
+        CHECK(ub.scheme() == U(""));
+        CHECK(ub.user_info() == U(""));
+        CHECK(ub.host() == U(""));
+        CHECK(ub.port() == -1);
+        CHECK(ub.path() == U("/"));
+        CHECK(ub.query() == U(""));
+        CHECK(ub.fragment() == U(""));
+        CHECK(u.to_string() == U("http://myhost.com/path1/path2"));
+
+        ub.append_path(U("path3"));
+        ub.set_host(U("hahah"));
+        ub.set_fragment(U("No"));
+        ub.clear();
+        CHECK(ub.scheme() == U(""));
+        CHECK(ub.user_info() == U(""));
+        CHECK(ub.host() == U(""));
+        CHECK(ub.port() == -1);
+        CHECK(ub.path() == U("/"));
+        CHECK(ub.query() == U(""));
+        CHECK(ub.fragment() == U(""));
+    }
+
+    TEST(to_string_invalid_uri)
+    {
+        uri_builder builder(U("http://invaliduri.com"));
+        builder.set_scheme(U("1http"));
+        VERIFY_THROWS(builder.to_string(), uri_exception);
+        VERIFY_THROWS(builder.to_uri(), uri_exception);
+
+        builder.set_scheme(U("ht*ip"));
+        VERIFY_THROWS(builder.to_string(), uri_exception);
+
+        builder.set_scheme(U("htt%20p"));
+        VERIFY_THROWS(builder.to_string(), uri_exception);
+    }
+
+    TEST(append_query_locale, "Ignore:Android", "Locale unsupported on Android")
+    {
+        std::locale changedLocale;
+        try
+        {
+#ifdef _WIN32
+            changedLocale = std::locale("fr-FR");
+#else
+            changedLocale = std::locale("fr_FR.UTF-8");
+#endif
+        }
+        catch (const std::exception&)
+        {
+            // Silently pass if locale isn't installed on machine.
+            return;
+        }
+
+        tests::common::utilities::locale_guard loc(changedLocale);
+
+        uri_builder builder;
+        auto const& key = U("key1000");
+        builder.append_query(key, 1000);
+        ::utility::string_t expected(key);
+        expected.append(U("=1000"));
+        VERIFY_ARE_EQUAL(expected, builder.query());
+    }
+
+    TEST(github_crash_994) { web::uri uri(U("http://127.0.0.1:34568/")); }
+
+} // SUITE(uri_builder_tests)
+
+} // namespace uri_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/uri/uri_tests.h b/Release/tests/functional/uri/uri_tests.h
new file mode 100644 (file)
index 0000000..72271db
--- /dev/null
@@ -0,0 +1,25 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * uri_tests.h
+ *
+ * Common utilities and helper functions for URI tests.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "cpprest/uri.h"
+#include "unittestpp.h"
+
+namespace tests
+{
+namespace functional
+{
+namespace uri_tests
+{
+}
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/utils/CMakeLists.txt b/Release/tests/functional/utils/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a8f157e
--- /dev/null
@@ -0,0 +1,16 @@
+set(SOURCES
+  datetime.cpp
+  base64.cpp
+  strings.cpp
+  macro_test.cpp
+  nonce_generator_tests.cpp
+  win32_encryption_tests.cpp
+)
+
+add_casablanca_test(utils_test SOURCES)
+
+if(CMAKE_COMPILER_IS_GNUCXX)
+  target_compile_options(utils_test PRIVATE "-Wno-deprecated-declarations")
+endif()
+
+configure_pch(utils_test stdafx.h stdafx.cpp)
diff --git a/Release/tests/functional/utils/base64.cpp b/Release/tests/functional/utils/base64.cpp
new file mode 100644 (file)
index 0000000..30ec199
--- /dev/null
@@ -0,0 +1,260 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * base64.cpp
+ *
+ * Tests for base64-related utility functions and classes.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace utils_tests
+{
+SUITE(base64)
+{
+    // Note: base64 works by encoding any 3 bytes as a four-byte string. Each triple is encoded independently of
+    // previous and subsequent triples. If, for a given set of input bytes, the number is not an even multiple of 3,
+    // the remaining 1 or two bytes are encoded and padded using '=' characters at the end. The encoding format is
+    // defined by IETF RFC 4648. Such padding is only allowed at the end of a encoded string, which makes it impossible
+    // to generally concatenate encoded strings and wind up with a string that is a valid base64 encoding.
+    //
+    // Since each triple of bytes is independent of others, we don't have to test particularly large sets if input data,
+    // validating that the algorithm can process at least two triples should be sufficient.
+    //
+    TEST(rfc_4648_tests_encode)
+    {
+        // These tests are what base64 RFC 4648 proposes.
+        {
+            std::vector<unsigned char> str1;
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("")), utility::conversions::to_base64(str1));
+        }
+        {
+            std::vector<unsigned char> str1;
+            str1.push_back('f');
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zg==")), utility::conversions::to_base64(str1));
+        }
+        {
+            std::vector<unsigned char> str1;
+            str1.push_back('f');
+            str1.push_back('o');
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm8=")), utility::conversions::to_base64(str1));
+        }
+        {
+            std::vector<unsigned char> str1;
+            str1.push_back('f');
+            str1.push_back('o');
+            str1.push_back('o');
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9v")), utility::conversions::to_base64(str1));
+        }
+        {
+            std::vector<unsigned char> str1;
+            str1.push_back('f');
+            str1.push_back('o');
+            str1.push_back('o');
+            str1.push_back('b');
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9vYg==")), utility::conversions::to_base64(str1));
+        }
+        {
+            std::vector<unsigned char> str1;
+            str1.push_back('f');
+            str1.push_back('o');
+            str1.push_back('o');
+            str1.push_back('b');
+            str1.push_back('a');
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9vYmE=")), utility::conversions::to_base64(str1));
+        }
+        {
+            std::vector<unsigned char> str1;
+            str1.push_back('f');
+            str1.push_back('o');
+            str1.push_back('o');
+            str1.push_back('b');
+            str1.push_back('a');
+            str1.push_back('r');
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9vYmFy")), utility::conversions::to_base64(str1));
+        }
+    }
+
+    TEST(rfc_4648_tests_decode)
+    {
+        // These tests are what base64 RFC 4648 proposes.
+        {
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR(""));
+            VERIFY_ARE_EQUAL(0u, str1.size());
+        }
+        {
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zg=="));
+            VERIFY_ARE_EQUAL(1u, str1.size());
+            VERIFY_ARE_EQUAL('f', str1[0]);
+        }
+        {
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm8="));
+            VERIFY_ARE_EQUAL(2u, str1.size());
+            VERIFY_ARE_EQUAL('f', str1[0]);
+            VERIFY_ARE_EQUAL('o', str1[1]);
+        }
+        {
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm9v"));
+            VERIFY_ARE_EQUAL(3u, str1.size());
+            VERIFY_ARE_EQUAL('f', str1[0]);
+            VERIFY_ARE_EQUAL('o', str1[1]);
+            VERIFY_ARE_EQUAL('o', str1[2]);
+        }
+        {
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm9vYg=="));
+            VERIFY_ARE_EQUAL(4u, str1.size());
+            VERIFY_ARE_EQUAL('f', str1[0]);
+            VERIFY_ARE_EQUAL('o', str1[1]);
+            VERIFY_ARE_EQUAL('o', str1[2]);
+            VERIFY_ARE_EQUAL('b', str1[3]);
+        }
+        {
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm9vYmE="));
+            VERIFY_ARE_EQUAL(5u, str1.size());
+            VERIFY_ARE_EQUAL('f', str1[0]);
+            VERIFY_ARE_EQUAL('o', str1[1]);
+            VERIFY_ARE_EQUAL('o', str1[2]);
+            VERIFY_ARE_EQUAL('b', str1[3]);
+            VERIFY_ARE_EQUAL('a', str1[4]);
+        }
+        {
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm9vYmFy"));
+            VERIFY_ARE_EQUAL(6u, str1.size());
+            VERIFY_ARE_EQUAL('f', str1[0]);
+            VERIFY_ARE_EQUAL('o', str1[1]);
+            VERIFY_ARE_EQUAL('o', str1[2]);
+            VERIFY_ARE_EQUAL('b', str1[3]);
+            VERIFY_ARE_EQUAL('a', str1[4]);
+            VERIFY_ARE_EQUAL('r', str1[5]);
+        }
+    }
+
+    TEST(additional_encode)
+    {
+        {
+            // Check '/' encoding
+            std::vector<unsigned char> str1;
+            str1.push_back(254);
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("/g==")), utility::conversions::to_base64(str1));
+        }
+        {
+            // Check '+' encoding
+            std::vector<unsigned char> str1;
+            str1.push_back(250);
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("+g==")), utility::conversions::to_base64(str1));
+        }
+        {
+            std::vector<unsigned char> str1;
+            str1.push_back('f');
+            str1.push_back('o');
+            str1.push_back(239);
+            str1.push_back('b');
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm/vYg==")), utility::conversions::to_base64(str1));
+        }
+        {
+            std::vector<unsigned char> str1;
+            str1.push_back('g');
+            str1.push_back(239);
+            str1.push_back('o');
+            str1.push_back('b');
+            VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Z+9vYg==")), utility::conversions::to_base64(str1));
+        }
+    }
+
+    TEST(additional_decode)
+    {
+        // Tests beyond what the RFC recommends.
+        {
+            // Check '/' decoding
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("/g=="));
+            VERIFY_ARE_EQUAL(1u, str1.size());
+            VERIFY_ARE_EQUAL(254u, str1[0]);
+        }
+        {
+            // Check '+' decoding
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("+g=="));
+            VERIFY_ARE_EQUAL(1u, str1.size());
+            VERIFY_ARE_EQUAL(250u, str1[0]);
+        }
+        {
+            // Check '/' decoding
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm/vYg=="));
+            VERIFY_ARE_EQUAL(4u, str1.size());
+            VERIFY_ARE_EQUAL('f', str1[0]);
+            VERIFY_ARE_EQUAL('o', str1[1]);
+            VERIFY_ARE_EQUAL(239, str1[2]);
+            VERIFY_ARE_EQUAL('b', str1[3]);
+        }
+        {
+            // Check '+' decoding
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Z+9vYg=="));
+            VERIFY_ARE_EQUAL(4u, str1.size());
+            VERIFY_ARE_EQUAL('g', str1[0]);
+            VERIFY_ARE_EQUAL(239, str1[1]);
+            VERIFY_ARE_EQUAL('o', str1[2]);
+            VERIFY_ARE_EQUAL('b', str1[3]);
+        }
+        {
+            // Check the whole base64 alphabet
+            std::vector<unsigned char> str1 = utility::conversions::from_base64(
+                _XPLATSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"));
+            VERIFY_ARE_EQUAL(48u, str1.size());
+        }
+    }
+
+    TEST(bad_decode)
+    {
+        // These tests are for input that should be disallowed by a very strict decoder, but
+        // the available APIs on Windows accept them, as does glib, which is used on Linux.
+
+        // Invalid character before padding, unused ones.
+        VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("/q==")), std::runtime_error);
+        VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("Zm9vYmD=")), std::runtime_error);
+
+        // CRLF in the middle.
+        VERIFY_THROWS(utility::conversions::from_base64(
+                          _XPLATSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\nabcdefghijklmnopqrstuvwxyz\r\n0123456789+/")),
+                      std::runtime_error);
+
+        // Not the right length.
+        VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("/q")), std::runtime_error);
+        // Characters not in the alphabet
+        VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("$%#@")), std::runtime_error);
+        // Too much padding at the end.
+        VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("/q=========")), std::runtime_error);
+        // Valid strings, concatenated
+        VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("Z+9vYg==Z+9vYg==")), std::runtime_error);
+    }
+
+    TEST(large_string)
+    {
+        const size_t size = 64 * 1024;
+
+        std::vector<unsigned char> data(size);
+        for (auto i = 0u; i < size; ++i)
+        {
+            data[i] = (unsigned char)(rand() & 0xFF);
+        }
+
+        auto string = utility::conversions::to_base64(data);
+        auto data2 = utility::conversions::from_base64(string);
+
+        VERIFY_ARE_EQUAL(data, data2);
+    }
+
+} // SUITE(base64)
+
+} // namespace utils_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/utils/datetime.cpp b/Release/tests/functional/utils/datetime.cpp
new file mode 100644 (file)
index 0000000..22954ca
--- /dev/null
@@ -0,0 +1,556 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Tests for datetime-related utility functions and classes.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#include <stdint.h>
+#include <string>
+
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace utils_tests
+{
+SUITE(datetime)
+{
+    // This is by no means a comprehensive test suite for the datetime functionality.
+    // It's a response to a particular bug and should be amended over time.
+
+    TEST(parsing_dateandtime_basic)
+    {
+        // ISO 8601
+        // RFC 1123
+
+        auto dt1 = utility::datetime::from_string(_XPLATSTR("20130517T00:00:00Z"), utility::datetime::ISO_8601);
+        VERIFY_ARE_NOT_EQUAL(0u, dt1.to_interval());
+
+        auto dt2 =
+            utility::datetime::from_string(_XPLATSTR("Fri, 17 May 2013 00:00:00 GMT"), utility::datetime::RFC_1123);
+        VERIFY_ARE_NOT_EQUAL(0u, dt2.to_interval());
+
+        VERIFY_ARE_EQUAL(dt1.to_interval(), dt2.to_interval());
+    }
+
+    TEST(parsing_dateandtime_extended)
+    {
+        // ISO 8601
+        // RFC 1123
+
+        auto dt1 = utility::datetime::from_string(_XPLATSTR("2013-05-17T00:00:00Z"), utility::datetime::ISO_8601);
+        VERIFY_ARE_NOT_EQUAL(0u, dt1.to_interval());
+
+        auto dt2 =
+            utility::datetime::from_string(_XPLATSTR("Fri, 17 May 2013 00:00:00 GMT"), utility::datetime::RFC_1123);
+        VERIFY_ARE_NOT_EQUAL(0u, dt2.to_interval());
+
+        VERIFY_ARE_EQUAL(dt1.to_interval(), dt2.to_interval());
+    }
+
+    TEST(parsing_date_basic)
+    {
+        // ISO 8601
+        {
+            auto dt = utility::datetime::from_string(_XPLATSTR("20130517"), utility::datetime::ISO_8601);
+
+            VERIFY_ARE_NOT_EQUAL(0u, dt.to_interval());
+        }
+    }
+
+    TEST(parsing_date_extended)
+    {
+        // ISO 8601
+        {
+            auto dt = utility::datetime::from_string(_XPLATSTR("2013-05-17"), utility::datetime::ISO_8601);
+
+            VERIFY_ARE_NOT_EQUAL(0u, dt.to_interval());
+        }
+    }
+
+    void TestDateTimeRoundtrip(utility::string_t str, utility::string_t strExpected)
+    {
+        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); }
+
+    TEST(parsing_time_roundtrip_datetime1)
+    {
+        // Preserve all 7 digits after the comma:
+        TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.1234567Z"));
+    }
+
+    TEST(parsing_time_roundtrip_datetime2)
+    {
+        // lose the last '000'
+        TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.1234567000Z"), _XPLATSTR("2013-11-19T14:30:59.1234567Z"));
+        // lose the last '999' without rounding up
+        TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.1234567999Z"), _XPLATSTR("2013-11-19T14:30:59.1234567Z"));
+    }
+
+    TEST(parsing_time_roundtrip_datetime3)
+    {
+        // leading 0-s after the comma, tricky to parse correctly
+        TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.00123Z"));
+    }
+
+    TEST(parsing_time_roundtrip_datetime4)
+    {
+        // another leading 0 test
+        TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.0000001Z"));
+    }
+
+    TEST(parsing_time_roundtrip_datetime5)
+    {
+        // this is going to be truncated
+        TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.00000001Z"), _XPLATSTR("2013-11-19T14:30:59Z"));
+    }
+
+    TEST(parsing_time_roundtrip_datetime6)
+    {
+        // Only one digit after the dot
+        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_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(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"));
+        VERIFY_ARE_EQUAL(actual.substr(0, 3), expected);
+    }
+
+    void TestRfc1123IsTimeT(const utility::char_t* str, uint64_t t)
+    {
+        datetime dt = datetime::from_string(str, utility::datetime::RFC_1123);
+        uint64_t interval = dt.to_interval();
+        VERIFY_ARE_EQUAL(0, interval % 10000000);
+        interval /= 10000000;
+        interval -= 11644473600; // NT epoch adjustment
+        VERIFY_ARE_EQUAL(static_cast<uint64_t>(t), interval);
+    }
+
+    TEST(parsing_time_rfc1123_accepts_each_day)
+    {
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:00:00 GMT"), 0);
+        TestRfc1123IsTimeT(_XPLATSTR("Fri, 02 Jan 1970 00:00:00 GMT"), 86400 * 1);
+        TestRfc1123IsTimeT(_XPLATSTR("Sat, 03 Jan 1970 00:00:00 GMT"), 86400 * 2);
+        TestRfc1123IsTimeT(_XPLATSTR("Sun, 04 Jan 1970 00:00:00 GMT"), 86400 * 3);
+        TestRfc1123IsTimeT(_XPLATSTR("Mon, 05 Jan 1970 00:00:00 GMT"), 86400 * 4);
+        TestRfc1123IsTimeT(_XPLATSTR("Tue, 06 Jan 1970 00:00:00 GMT"), 86400 * 5);
+        TestRfc1123IsTimeT(_XPLATSTR("Wed, 07 Jan 1970 00:00:00 GMT"), 86400 * 6);
+    }
+
+    TEST(parsing_time_rfc1123_boundary_cases)
+    {
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:00:00 GMT"), 0);
+        TestRfc1123IsTimeT(_XPLATSTR("19 Jan 2038 03:14:06 GMT"), INT_MAX - 1);
+        TestRfc1123IsTimeT(_XPLATSTR("19 Jan 2038 03:13:07 -0001"), INT_MAX);
+        TestRfc1123IsTimeT(_XPLATSTR("19 Jan 2038 03:14:07 -0000"), INT_MAX);
+        TestRfc1123IsTimeT(_XPLATSTR("14 Jan 2019 23:16:21 +0000"), 1547507781);
+        TestRfc1123IsTimeT(_XPLATSTR("14 Jan 2019 23:16:21 -0001"), 1547507841);
+        TestRfc1123IsTimeT(_XPLATSTR("14 Jan 2019 23:16:21 +0001"), 1547507721);
+        TestRfc1123IsTimeT(_XPLATSTR("14 Jan 2019 23:16:21 -0100"), 1547511381);
+        TestRfc1123IsTimeT(_XPLATSTR("14 Jan 2019 23:16:21 +0100"), 1547504181);
+    }
+
+    TEST(parsing_time_rfc1123_uses_each_field)
+    {
+        TestRfc1123IsTimeT(_XPLATSTR("02 Jan 1970 00:00:00 GMT"), 86400);
+        TestRfc1123IsTimeT(_XPLATSTR("12 Jan 1970 00:00:00 GMT"), 950400);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Feb 1970 00:00:00 GMT"), 2678400);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 2000 00:00:00 GMT"), 946684800);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 2100 00:00:00 GMT"), 4102444800);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1990 00:00:00 GMT"), 631152000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1971 00:00:00 GMT"), 31536000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 10:00:00 GMT"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 01:00:00 GMT"), 3600);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:10:00 GMT"), 600);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:01:00 GMT"), 60);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:00:10 GMT"), 10);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:00:01 GMT"), 1);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 10:00:00 GMT"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 02:00:00 PST"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 03:00:00 PDT"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 03:00:00 MST"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 04:00:00 MDT"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 04:00:00 CST"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 05:00:00 CDT"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 05:00:00 EST"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 06:00:00 EDT"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 06:00:00 -0400"), 36000);
+        TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 05:59:00 -0401"), 36000);
+    }
+
+    TEST(parsing_time_rfc1123_max_days)
+    {
+        TestRfc1123IsTimeT(_XPLATSTR("31 Jan 1970 00:00:00 GMT"), 2592000);
+        TestRfc1123IsTimeT(_XPLATSTR("28 Feb 2019 00:00:00 GMT"), 1551312000); // non leap year allows feb 28
+        TestRfc1123IsTimeT(_XPLATSTR("29 Feb 2020 00:00:00 GMT"), 1582934400); // leap year allows feb 29
+        TestRfc1123IsTimeT(_XPLATSTR("31 Mar 1970 00:00:00 GMT"), 7689600);
+        TestRfc1123IsTimeT(_XPLATSTR("30 Apr 1970 00:00:00 GMT"), 10281600);
+        TestRfc1123IsTimeT(_XPLATSTR("31 May 1970 00:00:00 GMT"), 12960000);
+        TestRfc1123IsTimeT(_XPLATSTR("30 Jun 1970 00:00:00 GMT"), 15552000);
+        TestRfc1123IsTimeT(_XPLATSTR("31 Jul 1970 00:00:00 GMT"), 18230400);
+        TestRfc1123IsTimeT(_XPLATSTR("31 Aug 1970 00:00:00 GMT"), 20908800);
+        TestRfc1123IsTimeT(_XPLATSTR("30 Sep 1970 00:00:00 GMT"), 23500800);
+        TestRfc1123IsTimeT(_XPLATSTR("31 Oct 1970 00:00:00 GMT"), 26179200);
+        TestRfc1123IsTimeT(_XPLATSTR("30 Nov 1970 00:00:00 GMT"), 28771200);
+        TestRfc1123IsTimeT(_XPLATSTR("31 Dec 1970 00:00:00 GMT"), 31449600);
+    }
+
+    TEST(parsing_time_rfc1123_invalid_cases)
+    {
+        const utility::string_t bad_strings[] = {
+            _XPLATSTR("Ahu, 01 Jan 1970 00:00:00 GMT"), // bad letters in each place
+            _XPLATSTR("TAu, 01 Jan 1970 00:00:00 GMT"),
+            _XPLATSTR("ThA, 01 Jan 1970 00:00:00 GMT"),
+            _XPLATSTR("ThuA 01 Jan 1970 00:00:00 GMT"),
+            _XPLATSTR("Thu,A01 Jan 1970 00:00:00 GMT"),
+            _XPLATSTR("Thu, A1 Jan 1970 00:00:00 GMT"),
+            _XPLATSTR("Thu, 0A Jan 1970 00:00:00 GMT"),
+            _XPLATSTR("Thu, 01AJan 1970 00:00:00 GMT"),
+            _XPLATSTR("Thu, 01 Aan 1970 00:00:00 GMT"),
+            _XPLATSTR("Thu, 01 JAn 1970 00:00:00 GMT"),
+            _XPLATSTR("Thu, 01 JaA 1970 00:00:00 GMT"),
+            _XPLATSTR("Thu, 01 JanA1970 00:00:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan A970 00:00:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 1A70 00:00:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 19A0 00:00:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 197A 00:00:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 1970A00:00:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 A0:00:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 0A:00:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 00A00:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:A0:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:0A:00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00A00 GMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00:A0 GMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00:0A GMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00:00AGMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00:00 AMT"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00:00 GAT"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00:00 GMA"),
+            _XPLATSTR(""), // truncation
+            _XPLATSTR("T"),
+            _XPLATSTR("Th"),
+            _XPLATSTR("Thu"),
+            _XPLATSTR("Thu,"),
+            _XPLATSTR("Thu, "),
+            _XPLATSTR("Thu, 0"),
+            _XPLATSTR("Thu, 01"),
+            _XPLATSTR("Thu, 01 "),
+            _XPLATSTR("Thu, 01 J"),
+            _XPLATSTR("Thu, 01 Ja"),
+            _XPLATSTR("Thu, 01 Jan"),
+            _XPLATSTR("Thu, 01 Jan "),
+            _XPLATSTR("Thu, 01 Jan 1"),
+            _XPLATSTR("Thu, 01 Jan 19"),
+            _XPLATSTR("Thu, 01 Jan 197"),
+            _XPLATSTR("Thu, 01 Jan 1970"),
+            _XPLATSTR("Thu, 01 Jan 1970 "),
+            _XPLATSTR("Thu, 01 Jan 1970 0"),
+            _XPLATSTR("Thu, 01 Jan 1970 00"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:0"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00:"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00:0"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00:00"),
+            _XPLATSTR("Thu, 01 Jan 1970 00:00:00 "),
+            _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 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"),
+            _XPLATSTR("32 Jul 1971 00:00:00 GMT"),
+            _XPLATSTR("32 Aug 1971 00:00:00 GMT"),
+            _XPLATSTR("31 Sep 1971 00:00:00 GMT"),
+            _XPLATSTR("32 Oct 1971 00:00:00 GMT"),
+            _XPLATSTR("31 Nov 1971 00:00:00 GMT"),
+            _XPLATSTR("32 Dec 1971 00:00:00 GMT"),
+            _XPLATSTR("01 Jan 1971 70:00:00 GMT"), // hour too big
+            _XPLATSTR("01 Jan 1971 24:00:00 GMT"),
+            _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 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"),
+            _XPLATSTR("01 Jan 1970 00:00:00 -2160"),
+            _XPLATSTR("00 Jan 1971 00:00:00 GMT"), // zero month day
+        };
+
+        for (const auto& str : bad_strings)
+        {
+            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);
+        }
+    }
+
+    TEST(parsing_time_iso8601_boundary_cases)
+    {
+        // boundary cases:
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:00:00Z"));                                         // epoch
+        TestDateTimeRoundtrip(_XPLATSTR("2038-01-19T03:14:06+00:00"), _XPLATSTR("2038-01-19T03:14:06Z")); // INT_MAX - 1
+        TestDateTimeRoundtrip(_XPLATSTR("2038-01-19T03:13:07-00:01"),
+                              _XPLATSTR("2038-01-19T03:14:07Z")); // INT_MAX after subtacting 1
+        TestDateTimeRoundtrip(_XPLATSTR("2038-01-19T03:14:07-00:00"), _XPLATSTR("2038-01-19T03:14:07Z"));
+    }
+
+    TEST(parsing_time_iso8601_uses_each_timezone_digit)
+    {
+        TestDateTimeRoundtrip(_XPLATSTR("2019-01-14T23:16:21+00:00"), _XPLATSTR("2019-01-14T23:16:21Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("2019-01-14T23:16:21-00:01"), _XPLATSTR("2019-01-14T23:17:21Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("2019-01-14T23:16:21+00:01"), _XPLATSTR("2019-01-14T23:15:21Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("2019-01-14T23:16:21-01:00"), _XPLATSTR("2019-01-15T00:16:21Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("2019-01-14T23:16:21+01:00"), _XPLATSTR("2019-01-14T22:16:21Z"));
+    }
+
+    TEST(parsing_time_iso8601_uses_each_digit)
+    {
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:00:01Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:01:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T01:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-02T00:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-02-01T00:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1971-01-01T00:00:00Z"));
+
+        TestDateTimeRoundtrip(_XPLATSTR("1999-01-01T00:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-12-01T00:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-09-01T00:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-30T00:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-31T00:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T23:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T19:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:59:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:00:59Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:00:60Z"), _XPLATSTR("1970-01-01T00:01:00Z")); // leap seconds
+    }
+
+    TEST(parsing_time_iso8601_accepts_month_max_days)
+    {
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-31T00:00:00Z")); // jan
+        TestDateTimeRoundtrip(_XPLATSTR("2019-02-28T00:00:00Z")); // non leap year allows feb 28
+        TestDateTimeRoundtrip(_XPLATSTR("2020-02-29T00:00:00Z")); // leap year allows feb 29
+        TestDateTimeRoundtrip(_XPLATSTR("1970-03-31T00:00:00Z")); // mar
+        TestDateTimeRoundtrip(_XPLATSTR("1970-04-30T00:00:00Z")); // apr
+        TestDateTimeRoundtrip(_XPLATSTR("1970-05-31T00:00:00Z")); // may
+        TestDateTimeRoundtrip(_XPLATSTR("1970-06-30T00:00:00Z")); // jun
+        TestDateTimeRoundtrip(_XPLATSTR("1970-07-31T00:00:00Z")); // jul
+        TestDateTimeRoundtrip(_XPLATSTR("1970-08-31T00:00:00Z")); // aug
+        TestDateTimeRoundtrip(_XPLATSTR("1970-09-30T00:00:00Z")); // sep
+        TestDateTimeRoundtrip(_XPLATSTR("1970-10-31T00:00:00Z")); // oct
+        TestDateTimeRoundtrip(_XPLATSTR("1970-11-30T00:00:00Z")); // nov
+        TestDateTimeRoundtrip(_XPLATSTR("1970-12-31T00:00:00Z")); // dec
+    }
+
+    TEST(parsing_time_iso8601_accepts_lowercase_t_z)
+    {
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01t00:00:00Z"), _XPLATSTR("1970-01-01T00:00:00Z"));
+        TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:00:00z"), _XPLATSTR("1970-01-01T00:00:00Z"));
+    }
+
+    TEST(parsing_time_roundtrip_datetime_accepts_invalid_no_trailing_timezone)
+    {
+        // No digits after the dot, or non-digits. This is not a valid input, but we should not choke on it,
+        // Simply ignore the bad fraction
+        const utility::string_t bad_strings[] = {_XPLATSTR("2013-11-19T14:30:59.Z"),
+                                                 _XPLATSTR("2013-11-19T14:30:59.a12Z")};
+        utility::string_t str_corrected = _XPLATSTR("2013-11-19T14:30:59Z");
+
+        for (const auto& str : bad_strings)
+        {
+            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, str_corrected);
+        }
+    }
+
+    TEST(parsing_time_roundtrip_datetime_invalid2)
+    {
+        // Various unsupported cases. In all cases, we have produce an empty date time
+        const utility::string_t bad_strings[] = {
+            _XPLATSTR(""),                     // empty
+            _XPLATSTR(".Z"),                   // too short
+            _XPLATSTR(".Zx"),                  // no trailing Z
+            _XPLATSTR("3.14Z")                 // not a valid date
+            _XPLATSTR("a971-01-01T00:00:00Z"), // any non digits or valid separators
+            _XPLATSTR("1a71-01-01T00:00:00Z"),
+            _XPLATSTR("19a1-01-01T00:00:00Z"),
+            _XPLATSTR("197a-01-01T00:00:00Z"),
+            _XPLATSTR("1971a01-01T00:00:00Z"),
+            _XPLATSTR("1971-a1-01T00:00:00Z"),
+            _XPLATSTR("1971-0a-01T00:00:00Z"),
+            _XPLATSTR("1971-01a01T00:00:00Z"),
+            _XPLATSTR("1971-01-a1T00:00:00Z"),
+            _XPLATSTR("1971-01-0aT00:00:00Z"),
+            // _XPLATSTR("1971-01-01a00:00:00Z"), parsed as complete date
+            _XPLATSTR("1971-01-01Ta0:00:00Z"),
+            _XPLATSTR("1971-01-01T0a:00:00Z"),
+            _XPLATSTR("1971-01-01T00a00:00Z"),
+            _XPLATSTR("1971-01-01T00:a0:00Z"),
+            _XPLATSTR("1971-01-01T00:0a:00Z"),
+            _XPLATSTR("1971-01-01T00:00a00Z"),
+            _XPLATSTR("1971-01-01T00:00:a0Z"),
+            _XPLATSTR("1971-01-01T00:00:0aZ"),
+            // "1971-01-01T00:00:00a", accepted as per invalid_no_trailing_timezone above
+            _XPLATSTR("1"), // truncation
+            _XPLATSTR("19"),
+            _XPLATSTR("197"),
+            _XPLATSTR("1970"),
+            _XPLATSTR("1970-"),
+            _XPLATSTR("1970-0"),
+            _XPLATSTR("1970-01"),
+            _XPLATSTR("1970-01-"),
+            _XPLATSTR("1970-01-0"),
+            // _XPLATSTR("1970-01-01"), complete date
+            _XPLATSTR("1970-01-01T"),
+            _XPLATSTR("1970-01-01T0"),
+            _XPLATSTR("1970-01-01T00"),
+            _XPLATSTR("1970-01-01T00:"),
+            _XPLATSTR("1970-01-01T00:0"),
+            _XPLATSTR("1970-01-01T00:00"),
+            _XPLATSTR("1970-01-01T00:00:"),
+            _XPLATSTR("1970-01-01T00:00:0"),
+            // _XPLATSTR("1970-01-01T00:00:00"), // accepted as invalid timezone above
+            _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"),
+            _XPLATSTR("1971-01-00T00:00:00Z"), // day too small
+            _XPLATSTR("1971-01-32T00:00:00Z"), // day too big
+            _XPLATSTR("1971-02-30T00:00:00Z"), // day too big for feb
+            _XPLATSTR("1971-02-30T00:00:00Z"), // day too big for feb (non-leap year)
+            _XPLATSTR("1971-03-32T00:00:00Z"), // other months
+            _XPLATSTR("1971-04-31T00:00:00Z"),
+            _XPLATSTR("1971-05-32T00:00:00Z"),
+            _XPLATSTR("1971-06-31T00:00:00Z"),
+            _XPLATSTR("1971-07-32T00:00:00Z"),
+            _XPLATSTR("1971-08-32T00:00:00Z"),
+            _XPLATSTR("1971-09-31T00:00:00Z"),
+            _XPLATSTR("1971-10-32T00:00:00Z"),
+            _XPLATSTR("1971-11-31T00:00:00Z"),
+            _XPLATSTR("1971-12-32T00:00:00Z"),
+            _XPLATSTR("1971-01-01T70:00:00Z"), // hour too big
+            _XPLATSTR("1971-01-01T24:00:00Z"),
+            _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("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"),
+            _XPLATSTR("1970-01-01T00:00:00+21:60"),
+            _XPLATSTR("1970-01-01T00:00:00-24:00"),
+            _XPLATSTR("1970-01-01T00:00:00-21:60"),
+            _XPLATSTR("1971-01-00"), // zero month day
+        };
+
+        for (const auto& str : bad_strings)
+        {
+            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
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/utils/macro_test.cpp b/Release/tests/functional/utils/macro_test.cpp
new file mode 100644 (file)
index 0000000..f45bc9f
--- /dev/null
@@ -0,0 +1,38 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * macro_test.cpp
+ *
+ * Tests cases for macro name conflicts.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "cpprest/http_client.h"
+#include "cpprest/http_msg.h"
+#include "cpprest/json.h"
+#include "cpprest/uri_builder.h"
+
+namespace tests
+{
+namespace functional
+{
+namespace utils_tests
+{
+template<typename U>
+void macro_U_Test()
+{
+    (void)U();
+}
+
+SUITE(macro_test)
+{
+    TEST(U_test) { macro_U_Test<int>(); }
+}
+} // namespace utils_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/utils/nonce_generator_tests.cpp b/Release/tests/functional/utils/nonce_generator_tests.cpp
new file mode 100644 (file)
index 0000000..92c4ea0
--- /dev/null
@@ -0,0 +1,60 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * nonce_generator_tests.cpp
+ *
+ * Tests for nonce_generator class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace utils_tests
+{
+SUITE(nonce_generator_tests)
+{
+    TEST(nonce_generator_set_length)
+    {
+        utility::nonce_generator gen;
+        VERIFY_ARE_EQUAL(utility::nonce_generator::default_length, gen.generate().length());
+
+        gen.set_length(1);
+        VERIFY_ARE_EQUAL(1, gen.generate().length());
+
+        gen.set_length(0);
+        VERIFY_ARE_EQUAL(0, gen.generate().length());
+
+        gen.set_length(500);
+        VERIFY_ARE_EQUAL(500, gen.generate().length());
+    }
+
+    TEST(nonce_generator_unique_strings)
+    {
+        // Generate 100 nonces and check each is unique.
+        std::vector<utility::string_t> nonces(100);
+        utility::nonce_generator gen;
+        for (auto&& v : nonces)
+        {
+            v = gen.generate();
+        }
+        for (auto v : nonces)
+        {
+            VERIFY_ARE_EQUAL(1, std::count(nonces.begin(), nonces.end(), v));
+        }
+    }
+
+} // SUITE(nonce_generator_tests)
+
+} // namespace utils_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/utils/stdafx.cpp b/Release/tests/functional/utils/stdafx.cpp
new file mode 100644 (file)
index 0000000..c9db6a6
--- /dev/null
@@ -0,0 +1,15 @@
+/***
+ * 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 utils_test_generate_lib = 0;
+#endif
diff --git a/Release/tests/functional/utils/stdafx.h b/Release/tests/functional/utils/stdafx.h
new file mode 100644 (file)
index 0000000..40eb75f
--- /dev/null
@@ -0,0 +1,21 @@
+/***
+ * 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
+#define _TURN_OFF_PLATFORM_STRING
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/details/web_utilities.h"
+#include "cpprest/uri.h"
+#include "unittestpp.h"
+#include "utils_tests.h"
diff --git a/Release/tests/functional/utils/strings.cpp b/Release/tests/functional/utils/strings.cpp
new file mode 100644 (file)
index 0000000..2617143
--- /dev/null
@@ -0,0 +1,409 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * base64.cpp
+ *
+ * Tests for base64-related utility functions and classes.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if !defined(__GLIBCXX__)
+#include <codecvt>
+#endif
+
+#include <locale_guard.h>
+
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace utils_tests
+{
+SUITE(strings)
+{
+    TEST(usascii_to_utf16)
+    {
+        std::string str_ascii("This is a test");
+        utf16string str_utf16 = utility::conversions::usascii_to_utf16(str_ascii);
+
+        for (size_t i = 0; i < str_ascii.size(); ++i)
+        {
+            VERIFY_ARE_EQUAL((utf16char)str_ascii[i], str_utf16[i]);
+        }
+    }
+
+#ifdef _WIN32
+#define UTF16(x) L##x
+#else
+#define UTF16(x) u##x
+#endif
+
+    TEST(utf16_to_utf8)
+    {
+#if !defined(__GLIBCXX__)
+        std::wstring_convert<std::codecvt_utf8_utf16<utf16char>, utf16char> conversion;
+#endif
+
+        // encodes to single byte character
+        VERIFY_ARE_EQUAL("ABC987", utility::conversions::utf16_to_utf8(UTF16("ABC987")));
+        utf16string input;
+        input.push_back(0x7F); // last ASCII character
+        auto result = utility::conversions::utf16_to_utf8(input);
+        VERIFY_ARE_EQUAL(0x7F, result[0]);
+
+        // encodes to 2 byte character
+        input.clear();
+        input.push_back(0x80);
+        input.push_back(0x14D);
+        input.push_back(0x7FF);
+        result = utility::conversions::utf16_to_utf8(input);
+#if defined(__GLIBCXX__)
+        VERIFY_ARE_EQUAL(194u, static_cast<unsigned char>(result[0]));
+        VERIFY_ARE_EQUAL(128u, static_cast<unsigned char>(result[1]));
+        VERIFY_ARE_EQUAL(197u, static_cast<unsigned char>(result[2]));
+        VERIFY_ARE_EQUAL(141u, static_cast<unsigned char>(result[3]));
+        VERIFY_ARE_EQUAL(223u, static_cast<unsigned char>(result[4]));
+        VERIFY_ARE_EQUAL(191u, static_cast<unsigned char>(result[5]));
+#else
+        VERIFY_ARE_EQUAL(conversion.to_bytes(input), result);
+#endif
+
+        // encodes to 3 byte character
+        input.clear();
+        input.push_back(0x800);
+        input.push_back(0x14AB);
+        input.push_back(0xFFFF);
+        result = utility::conversions::utf16_to_utf8(input);
+#if defined(__GLIBCXX__)
+        VERIFY_ARE_EQUAL(224u, static_cast<unsigned char>(result[0]));
+        VERIFY_ARE_EQUAL(160u, static_cast<unsigned char>(result[1]));
+        VERIFY_ARE_EQUAL(128u, static_cast<unsigned char>(result[2]));
+        VERIFY_ARE_EQUAL(225u, static_cast<unsigned char>(result[3]));
+        VERIFY_ARE_EQUAL(146u, static_cast<unsigned char>(result[4]));
+        VERIFY_ARE_EQUAL(171u, static_cast<unsigned char>(result[5]));
+        VERIFY_ARE_EQUAL(239u, static_cast<unsigned char>(result[6]));
+        VERIFY_ARE_EQUAL(191u, static_cast<unsigned char>(result[7]));
+        VERIFY_ARE_EQUAL(191u, static_cast<unsigned char>(result[8]));
+#else
+        VERIFY_ARE_EQUAL(conversion.to_bytes(input), result);
+#endif
+
+        // surrogate pair - encodes to 4 byte character
+        input.clear();
+        // U+10000
+        input.push_back(0xD800);
+        input.push_back(0xDC00);
+        // U+12345
+        input.push_back(0xD802);
+        input.push_back(0xDD29);
+        // U+10FFFF
+        input.push_back(0xDA3F);
+        input.push_back(0xDFFF);
+        result = utility::conversions::utf16_to_utf8(input);
+#if defined(__GLIBCXX__)
+        VERIFY_ARE_EQUAL(240u, static_cast<unsigned char>(result[0]));
+        VERIFY_ARE_EQUAL(144u, static_cast<unsigned char>(result[1]));
+        VERIFY_ARE_EQUAL(128u, static_cast<unsigned char>(result[2]));
+        VERIFY_ARE_EQUAL(128u, static_cast<unsigned char>(result[3]));
+        VERIFY_ARE_EQUAL(240u, static_cast<unsigned char>(result[4]));
+        VERIFY_ARE_EQUAL(144u, static_cast<unsigned char>(result[5]));
+        VERIFY_ARE_EQUAL(164u, static_cast<unsigned char>(result[6]));
+        VERIFY_ARE_EQUAL(169u, static_cast<unsigned char>(result[7]));
+        VERIFY_ARE_EQUAL(242u, static_cast<unsigned char>(result[8]));
+        VERIFY_ARE_EQUAL(159u, static_cast<unsigned char>(result[9]));
+        VERIFY_ARE_EQUAL(191u, static_cast<unsigned char>(result[10]));
+        VERIFY_ARE_EQUAL(191u, static_cast<unsigned char>(result[11]));
+#else
+        VERIFY_ARE_EQUAL(conversion.to_bytes(input), result);
+#endif
+
+        // surrogate pair - covering regression bug where 0x10000 was accidentally bitwise OR'ed instead of added.
+        input.clear();
+        input.push_back(0xD840);
+        input.push_back(0xDC00);
+        result = utility::conversions::utf16_to_utf8(input);
+#if defined(__GLIBCXX__)
+        VERIFY_ARE_EQUAL(240u, static_cast<unsigned char>(result[0]));
+        VERIFY_ARE_EQUAL(160u, static_cast<unsigned char>(result[1]));
+        VERIFY_ARE_EQUAL(128u, static_cast<unsigned char>(result[2]));
+        VERIFY_ARE_EQUAL(128u, static_cast<unsigned char>(result[3]));
+#else
+        VERIFY_ARE_EQUAL(conversion.to_bytes(input), result);
+#endif
+    }
+
+    TEST(utf8_to_utf16)
+    {
+#if !defined(__GLIBCXX__)
+        std::wstring_convert<std::codecvt_utf8_utf16<utf16char>, utf16char> conversion;
+#endif
+
+        // single byte character
+        VERIFY_ARE_EQUAL(UTF16("ABC123"), utility::conversions::utf8_to_utf16("ABC123"));
+        std::string input;
+        input.push_back(0x7F); // last ASCII character
+        auto result = utility::conversions::utf8_to_utf16(input);
+        VERIFY_ARE_EQUAL(0x7F, result[0]);
+
+        // null byte
+        input.clear();
+        input.push_back(0);
+        input.push_back(0);
+        result = utility::conversions::utf8_to_utf16(input);
+        VERIFY_ARE_EQUAL(0, result[0]);
+        VERIFY_ARE_EQUAL(0, result[1]);
+
+        // 2 byte character
+        input.clear();
+        // U+80
+        input.push_back(208u); // 11010000
+        input.push_back(128u); // 10000000
+        // U+7FF
+        input.push_back(223u); // 11011111
+        input.push_back(191u); // 10111111
+        result = utility::conversions::utf8_to_utf16(input);
+#if defined(__GLIBCXX__)
+        VERIFY_ARE_EQUAL(1024, result[0]);
+        VERIFY_ARE_EQUAL(2047, result[1]);
+#else
+        VERIFY_ARE_EQUAL(conversion.from_bytes(input), result);
+#endif
+
+        // 3 byte character
+        input.clear();
+        // U+800
+        input.push_back(232u); // 11101000
+        input.push_back(128u); // 10000000
+        input.push_back(128u); // 10000000
+        // U+FFFF
+        input.push_back(239u); // 11101111
+        input.push_back(191u); // 10111111
+        input.push_back(191u); // 10111111
+        result = utility::conversions::utf8_to_utf16(input);
+#if defined(__GLIBCXX__)
+        VERIFY_ARE_EQUAL(32768, result[0]);
+        VERIFY_ARE_EQUAL(65535, result[1]);
+#else
+        VERIFY_ARE_EQUAL(conversion.from_bytes(input), result);
+#endif
+
+        // 4 byte character
+        input.clear();
+        // U+10000
+        input.push_back(244u); // 11110100
+        input.push_back(128u); // 10000000
+        input.push_back(128u); // 10000000
+        input.push_back(128u); // 10000000
+        // U+10FFFF
+        input.push_back(244u); // 11110100
+        input.push_back(143u); // 10001111
+        input.push_back(191u); // 10111111
+        input.push_back(191u); // 10111111
+        result = utility::conversions::utf8_to_utf16(input);
+#if defined(__GLIBCXX__)
+        VERIFY_ARE_EQUAL(56256, result[0]);
+        VERIFY_ARE_EQUAL(56320, result[1]);
+        VERIFY_ARE_EQUAL(56319, result[2]);
+        VERIFY_ARE_EQUAL(57343, result[3]);
+#else
+        VERIFY_ARE_EQUAL(conversion.from_bytes(input), result);
+#endif
+
+        // 1 byte character followed by 4 byte character
+        input.clear();
+        input.push_back(51u); // 00110011
+        // U+10000
+        input.push_back(244u); // 11110100
+        input.push_back(128u); // 10000000
+        input.push_back(128u); // 10000000
+        input.push_back(128u); // 10000000
+        // U+10FFFF
+        input.push_back(244u); // 11110100
+        input.push_back(143u); // 10001111
+        input.push_back(191u); // 10111111
+        input.push_back(191u); // 10111111
+        result = utility::conversions::utf8_to_utf16(input);
+#if defined(__GLIBCXX__)
+        VERIFY_ARE_EQUAL(51, result[0]);
+        VERIFY_ARE_EQUAL(56256, result[1]);
+        VERIFY_ARE_EQUAL(56320, result[2]);
+        VERIFY_ARE_EQUAL(56319, result[3]);
+        VERIFY_ARE_EQUAL(57343, result[4]);
+#else
+        VERIFY_ARE_EQUAL(conversion.from_bytes(input), result);
+#endif
+    }
+
+    TEST(utf16_to_utf8_errors)
+    {
+        VERIFY_ARE_EQUAL("ABC987", utility::conversions::utf16_to_utf8(UTF16("ABC987")));
+        utf16string input;
+
+        // high surrogate with missing low surrogate.
+        input.push_back(0xD800);
+        input.push_back(0x0);
+        VERIFY_THROWS(utility::conversions::utf16_to_utf8(input), std::range_error);
+
+        // high surrogate with no more characters
+        input.clear();
+        input.push_back(0xD800);
+        VERIFY_THROWS(utility::conversions::utf16_to_utf8(input), std::range_error);
+    }
+
+    TEST(utf8_to_utf16_errors)
+    {
+        // missing second continuation byte
+        std::string input;
+        input.push_back(207u); // 11001111
+        VERIFY_THROWS(utility::conversions::utf8_to_utf16(input), std::range_error);
+
+        // missing third continuation byte
+        input.clear();
+        input.push_back(230u); // 11100110
+        input.push_back(141u); // 10001101
+        VERIFY_THROWS(utility::conversions::utf8_to_utf16(input), std::range_error);
+
+        // missing fourth continuation byte
+        input.clear();
+        input.push_back(240u); // 11110000
+        input.push_back(173u); // 10101101
+        input.push_back(157u); // 10011101
+        VERIFY_THROWS(utility::conversions::utf8_to_utf16(input), std::range_error);
+
+        // continuation byte missing leading 10xxxxxx
+        input.clear();
+        input.push_back(230u); // 11100110
+        input.push_back(141u); // 00001101
+        VERIFY_THROWS(utility::conversions::utf8_to_utf16(input), std::range_error);
+        input.clear();
+        input.push_back(230u); // 11100110
+        input.push_back(141u); // 11001101
+        VERIFY_THROWS(utility::conversions::utf8_to_utf16(input), std::range_error);
+
+        // invalid for a first character to start with 1xxxxxxx
+        input.clear();
+        input.push_back(128u); // 10000000
+        input.push_back(128u); // 10000000
+        VERIFY_THROWS(utility::conversions::utf8_to_utf16(input), std::range_error);
+        input.clear();
+        input.push_back(191u); // 10111111
+        input.push_back(128u); // 10000000
+        VERIFY_THROWS(utility::conversions::utf8_to_utf16(input), std::range_error);
+    }
+
+    TEST(latin1_to_utf16)
+    {
+        char in[256] = {0};
+        char16_t expectedResult[256] = {0};
+        for (size_t i = 0; i < 256; ++i)
+        {
+            in[i] = static_cast<char>(i);
+            expectedResult[i] = static_cast<char16_t>(i);
+        }
+
+        std::string str_latin1(in, 256);
+
+        auto actualResult = utility::conversions::latin1_to_utf16(str_latin1);
+
+        VERIFY_ARE_EQUAL(str_latin1.size(), actualResult.size());
+        for (size_t i = 0; i < actualResult.size(); ++i)
+        {
+            VERIFY_ARE_EQUAL(expectedResult[i], actualResult[i]);
+        }
+    }
+
+#if defined(_MSC_VER)
+#pragma warning(disable : 4996)
+#elif defined(__clang__)
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+    TEST(print_string_locale, "Ignore:Android", "Locale unsupported on Android")
+    {
+        std::locale changedLocale;
+        try
+        {
+#ifdef _WIN32
+            changedLocale = std::locale("fr-FR");
+#else
+            changedLocale = std::locale("fr_FR.UTF-8");
+#endif
+        }
+        catch (const std::exception&)
+        {
+            // Silently pass if locale isn't installed on machine.
+            return;
+        }
+
+        tests::common::utilities::locale_guard loc(changedLocale);
+
+        utility::ostringstream_t oss;
+        oss << 1000;
+        VERIFY_ARE_EQUAL(oss.str(), utility::conversions::print_string(1000));
+        VERIFY_ARE_EQUAL(_XPLATSTR("1000"), utility::conversions::print_string(1000, std::locale::classic()));
+    }
+
+    TEST(scan_string_locale, "Ignore:Android", "Locale unsupported on Android")
+    {
+        std::locale changedLocale;
+        try
+        {
+#ifdef _WIN32
+            changedLocale = std::locale("fr-FR");
+#else
+            changedLocale = std::locale("fr_FR.UTF-8");
+#endif
+        }
+        catch (const std::exception&)
+        {
+            // Silently pass if locale isn't installed on machine.
+            return;
+        }
+
+        VERIFY_ARE_EQUAL(_XPLATSTR("1000"),
+                         utility::conversions::scan_string<utility::string_t>(utility::string_t(_XPLATSTR("1000"))));
+        VERIFY_ARE_EQUAL(_XPLATSTR("1,000"),
+                         utility::conversions::scan_string<utility::string_t>(utility::string_t(_XPLATSTR("1,000"))));
+
+        VERIFY_ARE_EQUAL(
+            _XPLATSTR("1000"),
+            utility::conversions::scan_string<utility::string_t>(utility::string_t(_XPLATSTR("1000")), changedLocale));
+        VERIFY_ARE_EQUAL(
+            _XPLATSTR("1,000"),
+            utility::conversions::scan_string<utility::string_t>(utility::string_t(_XPLATSTR("1,000")), changedLocale));
+
+        {
+            tests::common::utilities::locale_guard loc(changedLocale);
+            VERIFY_ARE_EQUAL(_XPLATSTR("1000"),
+                             utility::conversions::scan_string<utility::string_t>(utility::string_t(_XPLATSTR("1000")),
+                                                                                  std::locale::classic()));
+            VERIFY_ARE_EQUAL(_XPLATSTR("1,000"),
+                             utility::conversions::scan_string<utility::string_t>(utility::string_t(_XPLATSTR("1,000")),
+                                                                                  std::locale::classic()));
+        }
+    }
+
+#ifdef _WIN32
+    TEST(windows_category_message)
+    {
+        // Ensure the error message string returned by windows_category doesn't contain trailing zeros.
+        std::string error_message = utility::details::windows_category().message(0);
+        std::string zero_terminated_copy = error_message.c_str();
+        VERIFY_ARE_EQUAL(zero_terminated_copy, error_message);
+    }
+#endif // _WIN32
+}
+
+} // namespace utils_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/utils/utils_tests.h b/Release/tests/functional/utils/utils_tests.h
new file mode 100644 (file)
index 0000000..827664f
--- /dev/null
@@ -0,0 +1,24 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * utils_tests.h
+ *
+ * Common utilities and helper functions for utility tests
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "unittestpp.h"
+
+namespace tests
+{
+namespace functional
+{
+namespace utils_tests
+{
+}
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/utils/win32_encryption_tests.cpp b/Release/tests/functional/utils/win32_encryption_tests.cpp
new file mode 100644 (file)
index 0000000..a2be7cd
--- /dev/null
@@ -0,0 +1,49 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * win32_encryption_tests.cpp
+ *
+ * Tests for win32_encryption class.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+using namespace utility;
+
+namespace tests
+{
+namespace functional
+{
+namespace utils_tests
+{
+#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)
+SUITE(win32_encryption)
+{
+    TEST(win32_encryption_random_string)
+    {
+        utility::string_t rndStr = utility::conversions::to_string_t("random string");
+        web::details::win32_encryption enc(rndStr);
+
+        VERIFY_ARE_EQUAL(*enc.decrypt(), rndStr);
+    }
+
+    TEST(win32_encryption_empty_string)
+    {
+        utility::string_t emptyStr = utility::conversions::to_string_t("");
+        web::details::win32_encryption enc(emptyStr);
+
+        VERIFY_ARE_EQUAL(*enc.decrypt(), emptyStr);
+    }
+
+} // SUITE(win32_encryption)
+
+#endif // defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)
+
+} // namespace utils_tests
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/websockets/CMakeLists.txt b/Release/tests/functional/websockets/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c8efb77
--- /dev/null
@@ -0,0 +1,35 @@
+if (NOT CPPREST_EXCLUDE_WEBSOCKETS)
+  add_library(websockettest_utilities utilities/test_websocket_server.cpp)
+  target_include_directories(websockettest_utilities PUBLIC utilities)
+  target_compile_definitions(websockettest_utilities PRIVATE -DWEBSOCKETTESTUTILITY_EXPORTS)
+  if(NOT WIN32)
+    target_compile_definitions(websockettest_utilities PRIVATE "-DWEBSOCKET_UTILITY_API=__attribute__ ((visibility (\"default\")))")
+    target_compile_definitions(websockettest_utilities INTERFACE "-DWEBSOCKET_UTILITY_API=")
+  endif()
+
+  cpprest_find_websocketpp()
+  target_link_libraries(websockettest_utilities
+    PRIVATE
+      cpprest
+      unittestpp
+      common_utilities
+      cpprestsdk_websocketpp_internal
+  )
+
+  # websocketsclient_test
+  set(SOURCES
+    client/authentication_tests.cpp
+    client/client_construction.cpp
+    client/close_tests.cpp
+    client/error_tests.cpp
+    client/receive_msg_tests.cpp
+    client/send_msg_tests.cpp
+    client/stdafx.cpp
+  )
+
+  add_casablanca_test(websocketsclient_test SOURCES)
+  if(NOT TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT")
+    target_link_libraries(websocketsclient_test PRIVATE websockettest_utilities)
+  endif()
+  target_include_directories(websocketsclient_test PRIVATE utilities)
+endif()
diff --git a/Release/tests/functional/websockets/client/authentication_tests.cpp b/Release/tests/functional/websockets/client/authentication_tests.cpp
new file mode 100644 (file)
index 0000000..a35949c
--- /dev/null
@@ -0,0 +1,164 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * authentication_tests.cpp
+ *
+ * Tests cases for covering authentication using websocket_client
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if defined(__cplusplus_winrt) || !defined(_M_ARM)
+
+using namespace web::websockets;
+using namespace web::websockets::client;
+
+using namespace tests::functional::websocket::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace websocket
+{
+namespace client
+{
+SUITE(authentication_tests)
+{
+// Authorization not implemented in non WinRT websocket_client yet - CodePlex 254
+#if defined(__cplusplus_winrt)
+    void auth_helper(test_websocket_server & server,
+                     const utility::string_t& username = U(""),
+                     const utility::string_t& password = U(""))
+    {
+        server.set_http_handler([username, password](test_http_request request) {
+            test_http_response resp;
+            if (request->username().empty()) // No credentials -> challenge the request
+            {
+                resp.set_status_code(401); // Unauthorized.
+                resp.set_realm("My Realm");
+            }
+            else if (request->username().compare(utility::conversions::to_utf8string(username)) ||
+                     request->password().compare(utility::conversions::to_utf8string(password)))
+            {
+                resp.set_status_code(403); // User name/password did not match: Forbidden - auth failure.
+            }
+            else
+            {
+                resp.set_status_code(200); // User name and passwords match. Successful auth.
+            }
+            return resp;
+        });
+    }
+
+    // connect without credentials, when the server expects credentials
+    TEST_FIXTURE(uri_address, auth_no_credentials, "Ignore", "245")
+    {
+        test_websocket_server server;
+        websocket_client client;
+        auth_helper(server);
+        VERIFY_THROWS(client.connect(m_uri).wait(), websocket_exception);
+    }
+
+    // Connect with credentials
+    TEST_FIXTURE(uri_address, auth_with_credentials, "Ignore", "245")
+    {
+        test_websocket_server server;
+        websocket_client_config config;
+        web::credentials cred(U("user"), U("password"));
+        config.set_credentials(cred);
+        websocket_client client(config);
+
+        auth_helper(server, cred.username(), U("password"));
+        client.connect(m_uri).wait();
+        client.close().wait();
+    }
+#endif
+
+    // helper function to check if failure is due to timeout.
+    bool is_timeout(const std::string& msg)
+    {
+        if (msg.find("set_fail_handler") != std::string::npos)
+        {
+            if (msg.find("handshake timed out") != std::string::npos || msg.find("Timer Expired") != std::string::npos)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    TEST(ssl_test)
+    {
+        websocket_client client;
+        std::string body_str("hello");
+
+        try
+        {
+            client.connect(U("wss://echo.websocket.org/")).wait();
+            auto receive_task = client.receive().then([body_str](websocket_incoming_message ret_msg) {
+                VERIFY_ARE_EQUAL(ret_msg.length(), body_str.length());
+                auto ret_str = ret_msg.extract_string().get();
+
+                VERIFY_ARE_EQUAL(body_str.compare(ret_str), 0);
+                VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::text_message);
+            });
+
+            websocket_outgoing_message msg;
+            msg.set_utf8_message(body_str);
+            client.send(msg).wait();
+
+            receive_task.wait();
+            client.close().wait();
+        }
+        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;
+            }
+            throw;
+        }
+    }
+
+    void handshake_error_test_impl(const ::utility::string_t& host)
+    {
+        websocket_client client;
+        try
+        {
+            client.connect(host).wait();
+            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;
+            }
+            VERIFY_ARE_EQUAL("TLS handshake failed", e.error_code().message());
+        }
+    }
+
+    TEST(self_signed_cert) { handshake_error_test_impl(U("wss://self-signed.badssl.com/")); }
+
+    TEST(hostname_mismatch) { handshake_error_test_impl(U("wss://wrong.host.badssl.com/")); }
+
+    TEST(cert_expired) { handshake_error_test_impl(U("wss://expired.badssl.com/")); }
+
+} // SUITE(authentication_tests)
+
+} // namespace client
+} // namespace websocket
+} // namespace functional
+} // namespace tests
+
+#endif
diff --git a/Release/tests/functional/websockets/client/client_construction.cpp b/Release/tests/functional/websockets/client/client_construction.cpp
new file mode 100644 (file)
index 0000000..cffe647
--- /dev/null
@@ -0,0 +1,255 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * client_construction.cpp
+ *
+ * Tests cases for covering creating websocket_clients.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if defined(__cplusplus_winrt) || !defined(_M_ARM)
+
+using namespace concurrency::streams;
+
+using namespace web;
+using namespace web::websockets;
+using namespace web::websockets::client;
+
+using namespace tests::functional::websocket::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace websocket
+{
+namespace client
+{
+SUITE(client_construction)
+{
+    // Helper function verifies that when constructing a websocket_client with invalid
+    // URI std::invalid_argument is thrown.
+    static void verify_client_invalid_argument(const uri& address)
+    {
+        try
+        {
+            websocket_client client;
+            client.connect(address).wait();
+            VERIFY_IS_TRUE(false);
+        }
+        catch (std::invalid_argument&)
+        {
+            // expected
+        }
+    }
+
+    TEST_FIXTURE(uri_address, client_construction_error_cases)
+    {
+        uri address(U("notws://localhost:34567/"));
+
+        // Invalid scheme.
+        verify_client_invalid_argument(address);
+
+        // empty host.
+        address = uri(U("ws://:34567/"));
+        verify_client_invalid_argument(address);
+    }
+
+    // Verify that we can read the config from the websocket_client
+    TEST_FIXTURE(uri_address, get_client_config)
+    {
+        websocket_client_config config;
+
+        web::credentials cred(U("username"), U("password"));
+        config.set_credentials(cred);
+        websocket_client client(config);
+
+        const websocket_client_config& config2 = client.config();
+        VERIFY_ARE_EQUAL(config2.credentials().username(), cred.username());
+    }
+
+    // Verify that we can read the config from the websocket_callback_client
+    TEST_FIXTURE(uri_address, get_client_config_callback_client)
+    {
+        websocket_client_config config;
+
+        web::credentials cred(U("username"), U("password"));
+        config.set_credentials(cred);
+        websocket_callback_client client(config);
+
+        const websocket_client_config& config2 = client.config();
+        VERIFY_ARE_EQUAL(config2.credentials().username(), cred.username());
+    }
+
+    // Verify that we can get the baseuri from websocket_client connect.
+    TEST_FIXTURE(uri_address, uri_test)
+    {
+        websocket_client client1;
+        VERIFY_ARE_EQUAL(client1.uri(), U("/"));
+
+        test_websocket_server server;
+        client1.connect(m_uri).wait();
+        VERIFY_ARE_EQUAL(client1.uri(), m_uri);
+        client1.close().wait();
+
+        websocket_client_config config;
+        websocket_client client2(config);
+        VERIFY_ARE_EQUAL(client2.uri(), U("/"));
+
+        client2.connect(m_uri).wait();
+        VERIFY_ARE_EQUAL(client2.uri(), m_uri);
+        client2.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, move_operations)
+    {
+        std::string body("hello");
+        std::vector<unsigned char> body_vec(body.begin(), body.end());
+
+        test_websocket_server server;
+        websocket_client client;
+
+        client.connect(m_uri).wait();
+
+        // Move constructor
+        websocket_client client2 = std::move(client);
+
+        server.next_message([&](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_UTF8_MESSAGE_TYPE);
+                            });
+
+        websocket_outgoing_message msg;
+        msg.set_utf8_message(body);
+        client2.send(std::move(msg)).wait();
+
+        auto t = client2.receive().then([&](websocket_incoming_message ret_msg) {
+            VERIFY_ARE_EQUAL(ret_msg.length(), body.length());
+            auto ret_str = ret_msg.extract_string().get();
+
+            VERIFY_ARE_EQUAL(body.compare(ret_str), 0);
+            VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::text_message);
+        });
+
+        test_websocket_msg rmsg;
+        rmsg.set_data(body_vec);
+        rmsg.set_msg_type(test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        server.send_msg(rmsg);
+        t.wait();
+
+        // Move assignment
+        client = std::move(client2);
+        server.next_message([&](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_UTF8_MESSAGE_TYPE);
+                            });
+
+        websocket_outgoing_message msg1;
+        msg1.set_utf8_message(body);
+        client.send(std::move(msg1)).wait();
+
+        test_websocket_msg rmsg1;
+        rmsg1.set_data(body_vec);
+        rmsg1.set_msg_type(test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        server.send_msg(rmsg1);
+        auto t1 = client.receive().then([&](websocket_incoming_message ret_msg) {
+            VERIFY_ARE_EQUAL(ret_msg.length(), body.length());
+            auto ret_str = ret_msg.extract_string().get();
+
+            VERIFY_ARE_EQUAL(body.compare(ret_str), 0);
+            VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::text_message);
+        });
+        t1.wait();
+        client.close().wait();
+    }
+
+    void header_test_impl(const uri& address,
+                          const utility::string_t& headerName,
+                          const utility::string_t& headerValue,
+                          const utility::string_t& expectedHeaderValue = U(""))
+    {
+        test_websocket_server server;
+        websocket_client_config config;
+        utility::string_t expectedValue = headerValue;
+        if (!expectedHeaderValue.empty())
+        {
+            expectedValue = expectedHeaderValue;
+        }
+        config.headers().add(headerName, headerValue);
+        websocket_client client(config);
+
+        server.set_http_handler([&](test_http_request request) {
+            test_http_response resp;
+            if (request->get_header_val(utility::conversions::to_utf8string(headerName))
+                    .compare(utility::conversions::to_utf8string(expectedValue)) == 0)
+                resp.set_status_code(200); // Handshake request will be completed only if header match succeeds.
+            else
+                resp.set_status_code(400); // Else fail the handshake, websocket client connect will fail in this case.
+            return resp;
+        });
+        client.connect(address).wait();
+        client.close().wait();
+    }
+
+    TEST_FIXTURE(uri_address, connect_with_headers)
+    {
+        header_test_impl(m_uri, U("HeaderTest"), U("ConnectSuccessfully"));
+    }
+
+    TEST_FIXTURE(uri_address, manually_set_protocol_header)
+    {
+        utility::string_t headerName(U("Sec-WebSocket-Protocol"));
+        header_test_impl(m_uri, headerName, U("myprotocol"));
+        header_test_impl(m_uri, headerName, U("myprotocol2,"), U("myprotocol2"));
+        header_test_impl(m_uri, headerName, U("myprotocol2,protocol3"), U("myprotocol2, protocol3"));
+        header_test_impl(
+            m_uri, headerName, U("myprotocol2, protocol3, protocol6,,"), U("myprotocol2, protocol3, protocol6"));
+    }
+
+    TEST_FIXTURE(uri_address, set_subprotocol)
+    {
+        test_websocket_server server;
+        websocket_client_config config;
+
+        utility::string_t expected1(U("pro1"));
+        config.add_subprotocol(expected1);
+        VERIFY_ARE_EQUAL(1, config.subprotocols().size());
+        VERIFY_ARE_EQUAL(expected1, config.subprotocols()[0]);
+
+        utility::string_t expected2(U("second"));
+        config.add_subprotocol(expected2);
+        VERIFY_ARE_EQUAL(2, config.subprotocols().size());
+        VERIFY_ARE_EQUAL(expected1, config.subprotocols()[0]);
+        VERIFY_ARE_EQUAL(expected2, config.subprotocols()[1]);
+
+        websocket_client client(config);
+        server.set_http_handler([&](test_http_request request) {
+            test_http_response resp;
+            if (request->get_header_val(utility::conversions::to_utf8string(U("Sec-WebSocket-Protocol")))
+                    .compare(utility::conversions::to_utf8string(expected1 + U(", ") + expected2)) == 0)
+                resp.set_status_code(200); // Handshake request will be completed only if header match succeeds.
+            else
+                resp.set_status_code(400); // Else fail the handshake, websocket client connect will fail in this case.
+            return resp;
+        });
+
+        client.connect(m_uri).wait();
+        client.close().wait();
+    }
+
+} // SUITE(client_construction)
+
+} // namespace client
+} // namespace websocket
+} // namespace functional
+} // namespace tests
+
+#endif
diff --git a/Release/tests/functional/websockets/client/close_tests.cpp b/Release/tests/functional/websockets/client/close_tests.cpp
new file mode 100644 (file)
index 0000000..58a6e7c
--- /dev/null
@@ -0,0 +1,167 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * close_tests.cpp
+ *
+ * Tests cases for closing websocket_client objects.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if defined(__cplusplus_winrt) || !defined(_M_ARM)
+
+using namespace concurrency::streams;
+
+using namespace web::websockets;
+using namespace web::websockets::client;
+
+using namespace tests::functional::websocket::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace websocket
+{
+namespace client
+{
+SUITE(close_tests)
+{
+    // Test close websocket connection: client sends an empty close and server responds with close frame
+    TEST_FIXTURE(uri_address, close_client_websocket)
+    {
+        test_websocket_server server;
+
+        websocket_client client;
+
+        client.connect(m_uri).wait();
+
+        client.close().wait();
+    }
+
+    // Test close websocket connection: client sends a close with reason and server responds with close frame
+    TEST_FIXTURE(uri_address, close_with_reason)
+    {
+        test_websocket_server server;
+
+        websocket_client client;
+
+        client.connect(m_uri).wait();
+
+        client.close(websocket_close_status::going_away, U("Client disconnecting")).wait();
+    }
+
+    // Server sends a close frame (server initiated close)
+    TEST_FIXTURE(uri_address, close_from_server)
+    {
+        std::string body("hello");
+        test_websocket_server server;
+
+        websocket_client client;
+
+        client.connect(m_uri).wait();
+
+        // Send close frame from server
+        test_websocket_msg msg;
+        msg.set_msg_type(test_websocket_message_type::WEB_SOCKET_CLOSE_TYPE);
+        server.send_msg(msg);
+
+        client.close().wait();
+    }
+
+    // Test close websocket connection with callback client: client sends an empty close and server responds with close
+    // frame
+    TEST_FIXTURE(uri_address, close_callback_client_websocket, "Ignore", "319")
+    {
+        test_websocket_server server;
+        const utility::string_t close_reason = U("Too large");
+
+        // verify it is ok not to set close handler
+        websocket_callback_client client;
+
+        client.connect(m_uri).wait();
+
+        client.close().wait();
+
+        websocket_callback_client client1;
+
+        client1.set_close_handler([&close_reason](websocket_close_status status,
+                                                  const utility::string_t& reason,
+                                                  const std::error_code& code) {
+            VERIFY_ARE_EQUAL(status, websocket_close_status::too_large);
+            VERIFY_ARE_EQUAL(reason, close_reason);
+            VERIFY_ARE_EQUAL(code.value(), 0);
+        });
+
+        client1.connect(m_uri).wait();
+
+        client1.close(websocket_close_status::too_large, close_reason).wait();
+    }
+
+    // Test close websocket connection: client sends a close with reason and server responds with close frame
+    TEST_FIXTURE(uri_address, close_callback_client_with_reason, "Ignore", "319")
+    {
+        const utility::string_t close_reason = U("Client disconnecting");
+        test_websocket_server server;
+
+        websocket_callback_client client;
+
+        client.set_close_handler([close_reason](websocket_close_status status,
+                                                const utility::string_t& reason,
+                                                const std::error_code& code) {
+            VERIFY_ARE_EQUAL(status, websocket_close_status::normal);
+            VERIFY_ARE_EQUAL(reason, close_reason);
+            VERIFY_ARE_EQUAL(code.value(), 0);
+        });
+
+        client.connect(m_uri).wait();
+
+        client.close(websocket_close_status::normal, close_reason).wait();
+    }
+
+    // Server sends a close frame (server initiated close)
+    TEST_FIXTURE(uri_address, close_callback_client_from_server, "Ignore", "319")
+    {
+        std::string body("hello");
+        test_websocket_server server;
+
+        websocket_callback_client client;
+
+        int hitCount = 0;
+        pplx::task_completion_event<void> closeEvent;
+        client.set_close_handler([&hitCount, closeEvent](websocket_close_status status,
+                                                         const utility::string_t& reason,
+                                                         const std::error_code& code) {
+            VERIFY_ARE_EQUAL(status, websocket_close_status::going_away);
+            VERIFY_ARE_EQUAL(reason, U(""));
+            VERIFY_ARE_EQUAL(code.value(), 0);
+
+            hitCount++;
+            closeEvent.set();
+        });
+
+        client.connect(m_uri).wait();
+
+        // Send close frame from server
+        test_websocket_msg msg;
+        msg.set_msg_type(test_websocket_message_type::WEB_SOCKET_CLOSE_TYPE);
+        server.send_msg(msg);
+
+        // make sure it only called once.
+        pplx::create_task(closeEvent).wait();
+        VERIFY_ARE_EQUAL(hitCount, 1);
+    }
+
+} // SUITE(close_tests)
+
+} // namespace client
+} // namespace websocket
+} // namespace functional
+} // namespace tests
+
+#endif
diff --git a/Release/tests/functional/websockets/client/error_tests.cpp b/Release/tests/functional/websockets/client/error_tests.cpp
new file mode 100644 (file)
index 0000000..7e24ec5
--- /dev/null
@@ -0,0 +1,172 @@
+/***
+ * 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 error connection cases with websocket_client.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if defined(__cplusplus_winrt) || !defined(_M_ARM)
+
+using namespace concurrency::streams;
+
+using namespace web::websockets;
+using namespace web::websockets::client;
+
+using namespace tests::functional::websocket::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace websocket
+{
+namespace client
+{
+SUITE(error_tests)
+{
+    // Send before connecting
+    TEST_FIXTURE(uri_address, send_before_connect)
+    {
+        websocket_client client;
+
+        websocket_outgoing_message msg;
+        msg.set_utf8_message("xyz");
+
+        VERIFY_THROWS(client.send(msg).wait(), websocket_exception);
+    }
+
+    // Server does not exist
+    TEST_FIXTURE(uri_address, server_doesnt_exist)
+    {
+        websocket_client client;
+        VERIFY_THROWS(client.connect(m_uri).get(), websocket_exception);
+    }
+
+// Send after close
+// CodePlex 319 fails on VS2013.
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+    TEST_FIXTURE(uri_address, send_after_close)
+    {
+        std::string body("hello");
+        test_websocket_server server;
+
+        server.next_message([&](test_websocket_msg msg) {
+            websocket_asserts::assert_message_equals(
+                msg, body, test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        });
+        websocket_client client;
+
+        client.connect(m_uri).wait();
+        client.close().wait();
+
+        websocket_outgoing_message msg;
+        msg.set_utf8_message(body);
+        VERIFY_THROWS(client.send(msg).wait(), websocket_exception);
+    }
+#endif
+
+    // Send after close for callback client
+    TEST_FIXTURE(uri_address, send_after_close_callback_client, "Ignore", "319")
+    {
+        std::string body("hello");
+        test_websocket_server server;
+
+        server.next_message([&](test_websocket_msg msg) {
+            websocket_asserts::assert_message_equals(
+                msg, body, test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        });
+        websocket_callback_client client;
+
+        client.connect(m_uri).wait();
+        client.close().wait();
+
+        websocket_outgoing_message msg;
+        msg.set_utf8_message(body);
+        VERIFY_THROWS(client.send(msg).wait(), websocket_exception);
+    }
+
+    // Receive after close
+    TEST_FIXTURE(uri_address, receive_after_close)
+    {
+        test_websocket_server server;
+        websocket_client client;
+        client.connect(m_uri).wait();
+        auto t = client.receive();
+        client.close().wait();
+        VERIFY_THROWS(t.wait(), websocket_exception);
+    }
+
+    // Start receive task after client has closed
+    TEST_FIXTURE(uri_address, try_receive_after_close)
+    {
+        test_websocket_server server;
+        websocket_client client;
+        client.connect(m_uri).wait();
+        client.close().wait();
+        auto t = client.receive();
+        VERIFY_THROWS(t.wait(), websocket_exception);
+    }
+
+    // Start the receive task after server has sent a close frame
+    TEST_FIXTURE(uri_address, try_receive_after_server_initiated_close)
+    {
+        test_websocket_server server;
+        websocket_client client;
+        client.connect(m_uri).wait();
+
+        // Send close frame from server
+        test_websocket_msg msg;
+        msg.set_msg_type(test_websocket_message_type::WEB_SOCKET_CLOSE_TYPE);
+        server.send_msg(msg);
+
+        // 100 ms should be plenty for local loopback
+        std::chrono::milliseconds dura(100);
+        std::this_thread::sleep_for(dura);
+
+        auto t = client.receive();
+        VERIFY_THROWS(t.wait(), websocket_exception);
+
+        client.close().wait();
+    }
+
+    // Destroy the client without closing it explicitly
+    TEST_FIXTURE(uri_address, destroy_without_close)
+    {
+        test_websocket_server server;
+        websocket_client client;
+        client.connect(m_uri).wait();
+    }
+
+    // Destroy the callback client without closing it explicitly
+    TEST_FIXTURE(uri_address, destroy_without_close_callback_client)
+    {
+        // test won't finish if we can't release client properly
+        test_websocket_server server;
+        websocket_callback_client client;
+        client.connect(m_uri).wait();
+    }
+
+    // connect fails while user is waiting on receive
+    TEST_FIXTURE(uri_address, connect_fail_with_receive)
+    {
+        websocket_client client;
+        auto t = client.receive();
+
+        VERIFY_THROWS(client.connect(U("ws://localhost:9981/ws")).get(), websocket_exception);
+        VERIFY_THROWS(t.get(), websocket_exception);
+    }
+
+} // SUITE(error_tests)
+
+} // namespace client
+} // namespace websocket
+} // namespace functional
+} // namespace tests
+
+#endif
diff --git a/Release/tests/functional/websockets/client/proxy_tests.cpp b/Release/tests/functional/websockets/client/proxy_tests.cpp
new file mode 100644 (file)
index 0000000..16df878
--- /dev/null
@@ -0,0 +1,92 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * proxy_tests.cpp
+ *
+ * Tests cases for covering proxies using websocket_client
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if defined(__cplusplus_winrt) || !defined(_M_ARM)
+
+using namespace web::websockets;
+using namespace web::websockets::client;
+
+using namespace tests::functional::websocket::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace websocket
+{
+namespace client
+{
+SUITE(proxy_tests)
+{
+#ifdef __cplusplus_winrt
+    TEST_FIXTURE(uri_address, no_proxy_options_on_winrt)
+    {
+        websocket_client_config config;
+        config.set_proxy(web::web_proxy::use_auto_discovery);
+        websocket_client client(config);
+        VERIFY_THROWS(client.connect(m_uri).wait(), websocket_exception);
+    }
+#endif
+
+#ifndef __cplusplus_winrt
+    // Can't specify a proxy with WinRT implementation.
+    TEST_FIXTURE(uri_address, proxy_with_credentials, "Ignore:Android", "390")
+    {
+        web::web_proxy proxy(U("http://netproxy.redmond.corp.microsoft.com"));
+        web::credentials cred(U("artur"), U("fred")); // relax, this is not my real password
+        proxy.set_credentials(cred);
+        websocket_client_config config;
+        config.set_proxy(proxy);
+
+        websocket_client client(config);
+
+        try
+        {
+            client.connect(U("wss://echo.websocket.org/")).wait();
+            const auto text = std::string("hello");
+            websocket_outgoing_message msg;
+            msg.set_utf8_message(text);
+            client.send(msg).wait();
+            auto response = client.receive().get();
+            VERIFY_ARE_EQUAL(text, response.extract_string().get());
+            client.close().wait();
+        }
+        catch (websocket_exception const& e)
+        {
+            if (e.error_code().value() == 12007)
+            {
+                // The above "netproxy.redmond.corp.microsoft.com" is an internal site not generally accessible.
+                // This will cause a failure to resolve the URL.
+                // This is ok.
+                return;
+            }
+            else if (e.error_code().value() == 9 || e.error_code().value() == 5)
+            {
+                // Timer expired case, since this is an outside test don't fail due to timing out.
+                return;
+            }
+            throw;
+        }
+    }
+#endif
+
+} // SUITE(proxy_tests)
+
+} // namespace client
+} // namespace websocket
+} // namespace functional
+} // namespace tests
+
+#endif
diff --git a/Release/tests/functional/websockets/client/receive_msg_tests.cpp b/Release/tests/functional/websockets/client/receive_msg_tests.cpp
new file mode 100644 (file)
index 0000000..8100d51
--- /dev/null
@@ -0,0 +1,307 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * receive_msg_tests.cpp
+ *
+ * Test cases covering receiving messages from websocket server.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if defined(__cplusplus_winrt) || !defined(_M_ARM)
+
+using namespace concurrency;
+using namespace concurrency::streams;
+
+using namespace web::websockets;
+using namespace web::websockets::client;
+
+using namespace tests::functional::websocket::utilities;
+
+namespace tests
+{
+namespace functional
+{
+namespace websocket
+{
+namespace client
+{
+SUITE(receive_msg_tests)
+{
+    pplx::task<void> receive_text_msg_helper(websocket_client & client,
+                                             test_websocket_server & server,
+                                             web::uri uri,
+                                             const std::string& body_str,
+                                             bool connect_client = true)
+    {
+        std::vector<unsigned char> body(body_str.begin(), body_str.end());
+
+        if (connect_client) client.connect(uri).wait();
+
+        auto t = client.receive().then([body_str](websocket_incoming_message ret_msg) {
+            VERIFY_ARE_EQUAL(ret_msg.length(), body_str.length());
+            auto ret_str = ret_msg.extract_string().get();
+
+            VERIFY_ARE_EQUAL(body_str.compare(ret_str), 0);
+            VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::text_message);
+        });
+
+        test_websocket_msg msg;
+        msg.set_data(std::move(body));
+        msg.set_msg_type(test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        server.send_msg(msg);
+
+        return t;
+    }
+
+    pplx::task<void> receive_msg_stream_helper(websocket_client & client,
+                                               test_websocket_server & server,
+                                               web::uri uri,
+                                               const std::vector<unsigned char>& body,
+                                               test_websocket_message_type type,
+                                               bool connect_client = true)
+    {
+        if (connect_client) client.connect(uri).wait();
+
+        auto t = client.receive().then([body, type](websocket_incoming_message ret_msg) {
+            auto is = ret_msg.body();
+            streams::container_buffer<std::vector<uint8_t>> ret_data;
+            is.read_to_end(ret_data).wait();
+
+            VERIFY_ARE_EQUAL(ret_msg.length(), body.size());
+            VERIFY_ARE_EQUAL(body, ret_data.collection());
+            if (type == test_websocket_message_type::WEB_SOCKET_BINARY_MESSAGE_TYPE)
+                VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::binary_message);
+            else if (type == test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE)
+                VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::text_message);
+        });
+
+        test_websocket_msg msg;
+        msg.set_data(std::move(body));
+        msg.set_msg_type(type);
+        server.send_msg(msg);
+
+        return t;
+    }
+
+    // Receive text message (no fragmentation)
+    TEST_FIXTURE(uri_address, receive_text_msg)
+    {
+        test_websocket_server server;
+        websocket_client client;
+
+        receive_text_msg_helper(client, server, m_uri, "hello").wait();
+        client.close().wait();
+    }
+
+    // Receive text message (no fragmentation)
+    // Test the stream interface to read data
+    TEST_FIXTURE(uri_address, receive_text_msg_stream)
+    {
+        std::string body_str("hello");
+        std::vector<unsigned char> body(body_str.begin(), body_str.end());
+        test_websocket_server server;
+        websocket_client client;
+
+        receive_msg_stream_helper(
+            client, server, m_uri, body, test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE)
+            .wait();
+        client.close().wait();
+    }
+
+    // Receive binary message (no fragmentation)
+    TEST_FIXTURE(uri_address, receive_binary_msg)
+    {
+        std::vector<uint8_t> body;
+        body.resize(6);
+        memcpy(&body[0], "a\0b\0c\0", 6);
+
+        test_websocket_server server;
+
+        websocket_client client;
+
+        receive_msg_stream_helper(
+            client, server, m_uri, body, test_websocket_message_type::WEB_SOCKET_BINARY_MESSAGE_TYPE)
+            .wait();
+        client.close().wait();
+    }
+
+    // Server sends text message fragmented in 2 fragments
+    TEST_FIXTURE(uri_address, receive_text_msg_fragments, "Ignore", "898451")
+    {
+        std::string body_str("hello");
+        std::vector<unsigned char> body(body_str.begin(), body_str.end());
+        test_websocket_server server;
+
+        websocket_client client;
+
+        client.connect(m_uri).wait();
+
+        auto t = client.receive().then([&](websocket_incoming_message ret_msg) {
+            auto ret_str = ret_msg.extract_string().get();
+
+            VERIFY_ARE_EQUAL(body_str.compare(ret_str), 0);
+            VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::text_message);
+        });
+
+        test_websocket_msg msg1;
+        msg1.set_data(std::move(body));
+        msg1.set_msg_type(test_websocket_message_type::WEB_SOCKET_UTF8_FRAGMENT_TYPE);
+        server.send_msg(msg1);
+
+        test_websocket_msg msg2;
+        msg2.set_data(std::move(body));
+        msg2.set_msg_type(test_websocket_message_type::WEB_SOCKET_UTF8_FRAGMENT_TYPE);
+        server.send_msg(msg2);
+
+        t.wait();
+        client.close().wait();
+    }
+
+    // Server sends message of length 0
+    TEST_FIXTURE(uri_address, receive_zero_length_msg)
+    {
+        test_websocket_server server;
+        websocket_client client;
+
+        receive_text_msg_helper(client, server, m_uri, "").wait();
+
+        client.close().wait();
+    }
+
+    // Receive UTF-8 string with special characters
+    TEST_FIXTURE(uri_address, receive_multi_byte_utf8_msg)
+    {
+        std::string body_str = "\xC3\xA0\xC3\xB8";
+        test_websocket_server server;
+        websocket_client client;
+
+        receive_text_msg_helper(client, server, m_uri, body_str).wait();
+
+        client.close().wait();
+    }
+
+    // Receive multiple messages
+    TEST_FIXTURE(uri_address, receive_multiple_msges)
+    {
+        test_websocket_server server;
+        websocket_client client;
+
+        auto t1 = receive_text_msg_helper(client, server, m_uri, "hello1");
+        auto t2 = receive_text_msg_helper(client, server, m_uri, "hello2", false);
+
+        t1.wait();
+        t2.wait();
+
+        client.close().wait();
+    }
+
+    // Start the receive task after the server has sent a message
+    TEST_FIXTURE(uri_address, receive_after_server_send)
+    {
+        std::string body_str("hello");
+        std::vector<unsigned char> body(body_str.begin(), body_str.end());
+
+        test_websocket_server server;
+
+        websocket_client client;
+
+        client.connect(m_uri).wait();
+
+        test_websocket_msg msg;
+        msg.set_data(std::move(body));
+        msg.set_msg_type(test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        server.send_msg(msg);
+
+        // We dont have a way of knowing if the message has been received by our client.
+        // Hence Sleep for 100 msecs and then initiate the receive
+        std::chrono::milliseconds dura(100);
+        std::this_thread::sleep_for(dura);
+
+        client.receive()
+            .then([&](websocket_incoming_message ret_msg) {
+                auto ret_str = ret_msg.extract_string().get();
+                VERIFY_ARE_EQUAL(body_str.compare(ret_str), 0);
+                VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::text_message);
+            })
+            .wait();
+
+        client.close().wait();
+    }
+
+    // Start task to receive text message before connecting.
+    TEST_FIXTURE(uri_address, receive_before_connect)
+    {
+        test_websocket_server server;
+        websocket_client client;
+
+        std::string body_str("hello");
+        std::vector<unsigned char> body(body_str.begin(), body_str.end());
+
+        auto t = client.receive().then([body_str](websocket_incoming_message ret_msg) {
+            VERIFY_ARE_EQUAL(ret_msg.length(), body_str.length());
+            auto ret_str = ret_msg.extract_string().get();
+
+            VERIFY_ARE_EQUAL(body_str.compare(ret_str), 0);
+            VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::text_message);
+        });
+
+        // Connect after the client is waiting on a receive task.
+        client.connect(m_uri).wait();
+
+        // Now send the message from the server
+        test_websocket_msg msg;
+        msg.set_data(std::move(body));
+        msg.set_msg_type(test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        server.send_msg(msg);
+
+        t.wait();
+        client.close().wait();
+    }
+
+    // Receive message using callback APIs
+    TEST_FIXTURE(uri_address, receive_text_msg_callback_client)
+    {
+        test_websocket_server server;
+        websocket_callback_client client;
+
+        client.connect(m_uri).wait();
+        std::string body_str("hello");
+        std::vector<unsigned char> body(body_str.begin(), body_str.end());
+
+        pplx::task_completion_event<void> receiveEvent;
+        // make sure client works fine without setting receive handler
+        test_websocket_msg msg;
+        msg.set_data(std::move(body));
+        msg.set_msg_type(test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        server.send_msg(msg);
+
+        // set receive handler
+        client.set_message_handler([body_str, &receiveEvent](websocket_incoming_message ret_msg) {
+            VERIFY_ARE_EQUAL(ret_msg.length(), body_str.length());
+            auto ret_str = ret_msg.extract_string().get();
+
+            VERIFY_ARE_EQUAL(body_str.compare(ret_str), 0);
+            VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::text_message);
+
+            receiveEvent.set();
+        });
+
+        server.send_msg(msg);
+
+        pplx::create_task(receiveEvent).wait();
+        client.close().wait();
+    }
+} // SUITE(receive_msg_tests)
+
+} // namespace client
+} // namespace websocket
+} // namespace functional
+} // namespace tests
+
+#endif
diff --git a/Release/tests/functional/websockets/client/send_msg_tests.cpp b/Release/tests/functional/websockets/client/send_msg_tests.cpp
new file mode 100644 (file)
index 0000000..eed1ded
--- /dev/null
@@ -0,0 +1,565 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * send_msg_tests.cpp
+ *
+ * Tests cases for covering sending messages from websocket client.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#include "stdafx.h"
+
+#if defined(__cplusplus_winrt) || !defined(_M_ARM)
+
+using namespace concurrency;
+using namespace concurrency::streams;
+
+using namespace web::websockets;
+using namespace web::websockets::client;
+
+using namespace tests::functional::websocket::utilities;
+
+#if defined(__cplusplus_winrt)
+using namespace Windows::Storage;
+#endif
+
+namespace tests
+{
+namespace functional
+{
+namespace websocket
+{
+namespace client
+{
+SUITE(send_msg_tests)
+{
+    utility::string_t get_full_name(const utility::string_t& name)
+    {
+#if defined(__cplusplus_winrt)
+        // On WinRT, we must compensate for the fact that we will be accessing files in the
+        // Documents folder
+        auto file =
+            pplx::create_task(KnownFolders::DocumentsLibrary->CreateFileAsync(ref new Platform::String(name.c_str()),
+                                                                              CreationCollisionOption::ReplaceExisting))
+                .get();
+        return file->Path->Data();
+#else
+        return name;
+#endif
+    }
+
+    template<typename _CharType>
+    pplx::task<streams::streambuf<_CharType>> OPEN_R(const utility::string_t& name)
+    {
+#if !defined(__cplusplus_winrt)
+        return streams::file_buffer<_CharType>::open(name, std::ios_base::in);
+#else
+        auto file =
+            pplx::create_task(KnownFolders::DocumentsLibrary->GetFileAsync(ref new Platform::String(name.c_str())))
+                .get();
+
+        return streams::file_buffer<_CharType>::open(file, std::ios_base::in);
+#endif
+    }
+
+    // Used to prepare data for stream tests
+    void fill_file(const utility::string_t& name, const std::vector<uint8_t>& body, size_t repetitions = 1)
+    {
+        std::fstream stream(get_full_name(name), std::ios_base::out | std::ios_base::trunc);
+
+        for (size_t i = 0; i < repetitions; i++)
+            stream.write((char*)&body[0], body.size());
+        stream.close();
+    }
+
+    void fill_buffer(streams::streambuf<uint8_t> rbuf, const std::vector<uint8_t>& body, size_t repetitions = 1)
+    {
+        size_t len = body.size();
+        for (size_t i = 0; i < repetitions; i++)
+            rbuf.putn_nocopy((const uint8_t*)&body[0], len).wait();
+    }
+
+    template<class SocketClientClass>
+    pplx::task<void> send_text_msg_helper(SocketClientClass & client,
+                                          web::uri uri,
+                                          test_websocket_server & server,
+                                          const std::string& body,
+                                          bool connect_client = true)
+    {
+        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_UTF8_MESSAGE_TYPE);
+                            });
+
+        if (connect_client) client.connect(uri).wait();
+
+        websocket_outgoing_message msg;
+        msg.set_utf8_message(body);
+        return client.send(msg);
+    }
+
+    template<class SocketClientClass>
+    pplx::task<void> send_ping_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_PING_TYPE); });
+
+        client.connect(uri).wait();
+
+        websocket_outgoing_message msg;
+        msg.set_ping_message(body);
+        return client.send(msg);
+    }
+
+    template<class SocketClientClass>
+    pplx::task<void> 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);
+    }
+
+    pplx::task<void> send_msg_from_stream(websocket_client & client,
+                                          test_websocket_server & server,
+                                          web::uri uri,
+                                          const std::vector<uint8_t>& body,
+                                          streams::streambuf<uint8_t> buf,
+                                          test_websocket_message_type type,
+                                          bool fill_data,
+                                          bool connect_client = true)
+    {
+        server.next_message(
+            [body, type](test_websocket_msg msg) { websocket_asserts::assert_message_equals(msg, body, type); });
+
+        if (connect_client) client.connect(uri).wait();
+        if (fill_data) fill_buffer(buf, body);
+
+        websocket_outgoing_message msg;
+        if (type == test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE)
+            msg.set_utf8_message(streams::istream(buf), body.size());
+        else if (type == test_websocket_message_type::WEB_SOCKET_BINARY_MESSAGE_TYPE)
+            msg.set_binary_message(streams::istream(buf), body.size());
+
+        return client.send(msg);
+    }
+
+    // Send message from input stream -> data is already populated in the stream buffer
+    pplx::task<void> send_msg_from_istream_helper(websocket_client & client,
+                                                  test_websocket_server & server,
+                                                  web::uri uri,
+                                                  const std::vector<uint8_t>& body,
+                                                  streams::streambuf<uint8_t> rbuf,
+                                                  test_websocket_message_type type,
+                                                  bool connect_client = true)
+    {
+        return send_msg_from_stream(client, server, uri, body, rbuf, type, false, connect_client);
+    }
+
+    pplx::task<void> send_msg_from_stream_helper(websocket_client & client,
+                                                 test_websocket_server & server,
+                                                 web::uri uri,
+                                                 const std::vector<uint8_t>& body,
+                                                 streams::streambuf<uint8_t> rbuf,
+                                                 test_websocket_message_type type,
+                                                 bool connect_client = true)
+    {
+        return send_msg_from_stream(client, server, uri, body, rbuf, type, true, connect_client);
+    }
+
+    // Send text message (no fragmentation)
+    TEST_FIXTURE(uri_address, send_text_msg)
+    {
+        test_websocket_server server;
+        websocket_client client;
+        send_text_msg_helper(client, m_uri, server, "hello").wait();
+        client.close().wait();
+    }
+
+    // Send text message with websocket_callback_client
+    TEST_FIXTURE(uri_address, send_text_msg_callback_client)
+    {
+        test_websocket_server server;
+        websocket_callback_client client;
+        send_text_msg_helper(client, m_uri, server, "hello").wait();
+        client.close().wait();
+    }
+
+    // Send text message (no fragmentation)
+    // Test the stream interface to send data
+    TEST_FIXTURE(uri_address, send_text_msg_stream)
+    {
+        test_websocket_server server;
+        streams::producer_consumer_buffer<uint8_t> rbuf;
+        std::vector<uint8_t> body(26);
+        memcpy(&body[0], "abcdefghijklmnopqrstuvwxyz", 26);
+
+        websocket_client client;
+        send_msg_from_stream_helper(
+            client, server, m_uri, body, rbuf, test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE)
+            .wait();
+
+        rbuf.close(std::ios::out).wait();
+        client.close().wait();
+    }
+
+    // Send Binary message (no fragmentation)
+    TEST_FIXTURE(uri_address, send_binary_msg)
+    {
+        test_websocket_server server;
+        streams::producer_consumer_buffer<uint8_t> rbuf;
+        std::vector<uint8_t> body(6);
+        memcpy(&body[0], "a\0b\0c\0", 6);
+
+        websocket_client client;
+
+        send_msg_from_stream_helper(
+            client, server, m_uri, body, rbuf, test_websocket_message_type::WEB_SOCKET_BINARY_MESSAGE_TYPE)
+            .wait();
+        rbuf.close(std::ios::out);
+        client.close().wait();
+    }
+
+    // Send empty text message
+    // WinRT client does not handle empty messages. Verify websocket_exception is thrown.
+    TEST_FIXTURE(uri_address, send_empty_text_msg)
+    {
+        test_websocket_server server;
+        websocket_client client;
+
+        client.connect(m_uri).wait();
+
+        websocket_outgoing_message msg;
+        msg.set_utf8_message("");
+        VERIFY_THROWS(client.send(msg).wait(), websocket_exception);
+
+        client.close().wait();
+    }
+
+    // Send multiple text messages
+    TEST_FIXTURE(uri_address, send_multiple_text_msges)
+    {
+        test_websocket_server server;
+        websocket_client client;
+
+        send_text_msg_helper(client, m_uri, server, "hello1").wait();
+        send_text_msg_helper(client, m_uri, server, "hello2", false).wait();
+
+        client.close().wait();
+    }
+
+    // Send multiple text messages
+    TEST_FIXTURE(uri_address, send_multiple_text_msges_async)
+    {
+        test_websocket_server server;
+        websocket_client client;
+
+        auto t1 = send_text_msg_helper(client, m_uri, server, "hello1");
+        auto t2 = send_text_msg_helper(client, m_uri, server, "hello2", false);
+
+        t2.wait();
+        t1.wait();
+        client.close().wait();
+    }
+
+    // Send multiple text messages from a stream
+    TEST_FIXTURE(uri_address, send_multiple_text_msges_stream)
+    {
+        test_websocket_server server;
+        streams::producer_consumer_buffer<uint8_t> rbuf;
+        std::vector<uint8_t> body1(26);
+        memcpy(&body1[0], "abcdefghijklmnopqrstuvwxyz", 26);
+        std::vector<uint8_t> body2(26);
+        memcpy(&body2[0], "zyxwvutsrqponmlkjihgfedcba", 26);
+
+        websocket_client client;
+
+        auto t1 = send_msg_from_stream_helper(
+            client, server, m_uri, body1, rbuf, test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        auto t2 = send_msg_from_stream_helper(
+            client, server, m_uri, body2, rbuf, test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE, false);
+
+        t1.wait();
+        t2.wait();
+        client.close().wait();
+    }
+
+    // Send multiple text messages from a file stream
+    // send uses stream::acquire API, acquire will fail for file streams.
+    TEST_FIXTURE(uri_address, send_text_msges_fstream)
+    {
+        test_websocket_server server;
+        utility::string_t fname = U("send_multiple_text_msges_fstream.txt");
+        std::vector<uint8_t> body1(26);
+        memcpy(&body1[0], "abcdefghijklmnopqrstuvwxyz", 26);
+        fill_file(fname, body1, 2);
+        auto file_buf = OPEN_R<uint8_t>(fname).get();
+        websocket_client client;
+
+        auto t1 = send_msg_from_istream_helper(
+            client, server, m_uri, body1, file_buf, test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        auto t2 = send_msg_from_istream_helper(
+            client, server, m_uri, body1, file_buf, test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE, false);
+
+        t1.wait();
+        t2.wait();
+        client.close().wait();
+    }
+
+    // Send multiple text messages from a container stream, where container stream has more data than what we want to
+    // send in a single message
+    TEST_FIXTURE(uri_address, send_text_msges_cstream)
+    {
+        test_websocket_server server;
+        std::vector<uint8_t> body(26);
+        memcpy(&body[0], "abcdefghijklmnopqrstuvwxyz", 26);
+        auto cbuf = streams::container_stream<std::vector<uint8_t>>::open_istream(body).streambuf();
+
+        websocket_client client;
+
+        auto t1 = send_msg_from_istream_helper(client,
+                                               server,
+                                               m_uri,
+                                               std::vector<uint8_t>(body.begin(), body.begin() + body.size() / 2),
+                                               cbuf,
+                                               test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        auto t2 = send_msg_from_istream_helper(client,
+                                               server,
+                                               m_uri,
+                                               std::vector<uint8_t>(body.begin() + body.size() / 2, body.end()),
+                                               cbuf,
+                                               test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE,
+                                               false);
+
+        t1.wait();
+        t2.wait();
+        client.close().wait();
+    }
+
+    // Send multiple text messages from a producer consumer stream, where stream initially has less data than what we
+    // want to send in a single message Write data to the buffer after initiating the send, send should succeed.
+    TEST_FIXTURE(uri_address, send_text_msges_pcstream_lessdata)
+    {
+        test_websocket_server server;
+        streams::producer_consumer_buffer<uint8_t> rbuf;
+        std::vector<uint8_t> body(26);
+        memcpy(&body[0], "abcdefghijklmnopqrstuvwxyz", 26);
+        fill_buffer(rbuf, body);
+
+        server.next_message([](test_websocket_msg msg) {
+            websocket_asserts::assert_message_equals(
+                msg, "abcdefghijklmnopqrstuvwxyzabcd", test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        });
+
+        websocket_client client;
+        client.connect(m_uri).wait();
+        websocket_outgoing_message msg;
+        msg.set_utf8_message(rbuf.create_istream(), 30);
+        auto t1 = client.send(msg);
+
+        fill_buffer(rbuf, body);
+        t1.wait();
+        client.close().wait();
+    }
+
+    // Send multiple text messages from a container stream, where stream has less data than what we want to send in a
+    // single message Since container stream does not support in | out simultaneously, websocket send_msg will fail to
+    // read the required number of bytes and throws an exception.
+    TEST_FIXTURE(uri_address, send_text_msges_cstream_lessdata)
+    {
+        test_websocket_server server;
+        std::vector<uint8_t> body(26);
+        memcpy(&body[0], "abcdefghijklmnopqrstuvwxyz", 26);
+        auto cbuf = streams::container_stream<std::vector<uint8_t>>::open_istream(body).streambuf();
+
+        server.next_message([](test_websocket_msg msg) {
+            websocket_asserts::assert_message_equals(
+                msg, "abcdefghijklmnopqrstuvwxyzabcd", test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+        });
+
+        websocket_client client;
+        client.connect(m_uri).wait();
+        websocket_outgoing_message msg;
+        msg.set_utf8_message(cbuf.create_istream(), 30);
+
+        VERIFY_THROWS(client.send(msg).wait(), websocket_exception);
+        client.close().wait();
+    }
+
+    // Send multiple binary messages from the same stream
+    TEST_FIXTURE(uri_address, send_multiple_binary_msg_same_stream)
+    {
+        test_websocket_server server;
+        streams::producer_consumer_buffer<uint8_t> rbuf;
+        std::vector<uint8_t> body1(6);
+        memcpy(&body1[0], "a\0b\0c\0", 6);
+        std::vector<uint8_t> body2(6);
+        memcpy(&body2[0], "a\0b\0c\0", 6);
+
+        websocket_client client;
+
+        auto t1 = send_msg_from_stream_helper(
+            client, server, m_uri, body1, rbuf, test_websocket_message_type::WEB_SOCKET_BINARY_MESSAGE_TYPE);
+        auto t2 = send_msg_from_stream_helper(
+            client, server, m_uri, body2, rbuf, test_websocket_message_type::WEB_SOCKET_BINARY_MESSAGE_TYPE, false);
+
+        t1.wait();
+        t2.wait();
+        rbuf.close(std::ios_base::out);
+        client.close().wait();
+    }
+
+    // Send text message followed by binary message
+    TEST_FIXTURE(uri_address, send_text_and_binary)
+    {
+        test_websocket_server server;
+        streams::producer_consumer_buffer<uint8_t> rbuf;
+        std::vector<uint8_t> body2(6);
+        memcpy(&body2[0], "a\0b\0c\0", 6);
+
+        websocket_client client;
+
+        send_text_msg_helper(client, m_uri, server, "hello1").wait();
+        send_msg_from_stream_helper(
+            client, server, m_uri, body2, rbuf, test_websocket_message_type::WEB_SOCKET_BINARY_MESSAGE_TYPE, false)
+            .wait();
+
+        rbuf.close(std::ios::out).wait();
+        client.close().wait();
+    }
+
+    // Send a multi byte UTF-8 text message
+    TEST_FIXTURE(uri_address, send_multi_byte_utf8_msg)
+    {
+        test_websocket_server server;
+        std::string body = "\xC3\xA0\xC3\xB8";
+        websocket_client client;
+
+        send_text_msg_helper(client, m_uri, server, body).wait();
+        client.close().wait();
+    }
+
+    // Send a streamed text message without specifying length
+    TEST_FIXTURE(uri_address, send_stream_utf8_msg_no_length)
+    {
+        test_websocket_server server;
+
+        std::string body = "\xC3\xA0\xC3\xB8";
+        std::vector<uint8_t> msgbuf(body.begin(), body.end());
+
+        auto is = streams::container_stream<std::vector<uint8_t>>::open_istream(std::move(msgbuf));
+
+        websocket_client client;
+        {
+            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_UTF8_MESSAGE_TYPE);
+                                });
+
+            client.connect(m_uri).wait();
+
+            websocket_outgoing_message msg;
+            msg.set_utf8_message(is);
+            client.send(msg).wait();
+        }
+
+        client.close().wait();
+    }
+
+    // Send a streamed binary message without specifying length
+    TEST_FIXTURE(uri_address, send_stream_binary_msg_no_length)
+    {
+        test_websocket_server server;
+
+        std::string body = "\x00\x01\x02\x00";
+        std::vector<uint8_t> msgbuf(body.begin(), body.end());
+
+        auto is = streams::container_stream<std::vector<uint8_t>>::open_istream(std::move(msgbuf));
+
+        websocket_client client;
+        {
+            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_BINARY_MESSAGE_TYPE);
+                                });
+
+            client.connect(m_uri).wait();
+
+            websocket_outgoing_message msg;
+            msg.set_binary_message(is);
+            client.send(msg).wait();
+        }
+
+        client.close().wait();
+    }
+
+#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)
+    {
+        test_websocket_server server;
+        websocket_client client;
+        send_pong_msg_helper(client, m_uri, server).wait();
+        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)
+    {
+        test_websocket_server server;
+        websocket_callback_client client;
+        send_pong_msg_helper(client, m_uri, server).wait();
+        client.close().wait();
+    }
+#endif
+
+} // SUITE(send_msg_tests)
+
+} // namespace client
+} // namespace websocket
+} // namespace functional
+} // namespace tests
+
+#endif
diff --git a/Release/tests/functional/websockets/client/stdafx.cpp b/Release/tests/functional/websockets/client/stdafx.cpp
new file mode 100644 (file)
index 0000000..2e322ea
--- /dev/null
@@ -0,0 +1,14 @@
+/***
+ * 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 websocket_client_test_generate_lib = 0;
+#endif
diff --git a/Release/tests/functional/websockets/client/stdafx.h b/Release/tests/functional/websockets/client/stdafx.h
new file mode 100644 (file)
index 0000000..6e22e86
--- /dev/null
@@ -0,0 +1,30 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Pre-compiled headers
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/containerstream.h"
+#include "cpprest/filestream.h"
+#include "cpprest/producerconsumerstream.h"
+#include "cpprest/rawptrstream.h"
+#include "cpprest/ws_client.h"
+#include "cpprest/ws_msg.h"
+#include "os_utilities.h"
+#include "test_websocket_server.h"
+#include "unittestpp.h"
+#include "websocket_client_tests.h"
+#include <chrono>
+#include <thread>
diff --git a/Release/tests/functional/websockets/client/websocket_client_tests.h b/Release/tests/functional/websockets/client/websocket_client_tests.h
new file mode 100644 (file)
index 0000000..a7a7272
--- /dev/null
@@ -0,0 +1,37 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * websocket_client_tests.h
+ *
+ * Common declarations and helper functions for http_client test cases.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include "cpprest/uri.h"
+#include "unittestpp.h"
+
+namespace tests
+{
+namespace functional
+{
+namespace websocket
+{
+namespace client
+{
+class uri_address
+{
+public:
+    uri_address() : m_uri(U("ws://localhost:9980/ws")) {}
+    web::uri m_uri;
+};
+
+} // namespace client
+} // namespace websocket
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/websockets/utilities/stdafx.cpp b/Release/tests/functional/websockets/utilities/stdafx.cpp
new file mode 100644 (file)
index 0000000..c8f4d74
--- /dev/null
@@ -0,0 +1,10 @@
+/***
+ * 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"
diff --git a/Release/tests/functional/websockets/utilities/stdafx.h b/Release/tests/functional/websockets/utilities/stdafx.h
new file mode 100644 (file)
index 0000000..0c6e351
--- /dev/null
@@ -0,0 +1,33 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Pre-compiled headers
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#if defined(_WIN32)
+// Include first to avoid any issues with Windows.h.
+#include <winsock2.h>
+#endif
+
+#if defined(_WIN32)
+// Trick Boost.Asio into thinking CE, otherwise _beginthreadex will be used which is banned
+// for the Windows Runtime pre VS2015. Then CreateThread will be used instead.
+#if _MSC_VER < 1900
+#if defined(__cplusplus_winrt)
+#define UNDER_CE 1
+#endif
+#endif
+#endif
+
+#include "cpprest/asyncrt_utils.h"
+#include "cpprest/containerstream.h"
+#include "cpprest/streams.h"
+#include "cpprest/uri.h"
+#include "unittestpp.h"
diff --git a/Release/tests/functional/websockets/utilities/test_websocket_server.cpp b/Release/tests/functional/websockets/utilities/test_websocket_server.cpp
new file mode 100644 (file)
index 0000000..863376f
--- /dev/null
@@ -0,0 +1,288 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * Defines a test server to handle websocket messages.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+#include "stdafx.h"
+
+#include "test_websocket_server.h"
+
+#include <algorithm>
+#include <os_utilities.h>
+#include <thread>
+
+#ifdef _WIN32
+#pragma warning(disable : 4503) // generated too late for disable to be effective inside push/pop
+#pragma warning(push)
+#pragma warning(disable : 4100 4127 4996 4512 4701 4267 4067 4005)
+#define _WEBSOCKETPP_CPP11_STL_
+#define _WEBSOCKETPP_CONSTEXPR_TOKEN_
+#if _MSC_VER < 1900
+#define _WEBSOCKETPP_NOEXCEPT_TOKEN_
+#endif
+#endif /* _WIN32 */
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+#endif
+
+#include <websocketpp/config/asio_no_tls.hpp>
+#include <websocketpp/server.hpp>
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#ifdef _WIN32
+#pragma warning(pop)
+#endif
+
+using namespace web;
+using namespace utility;
+using namespace utility::conversions;
+
+// In the future this should be configurable through option in test server.
+#define WEBSOCKETS_TEST_SERVER_PORT 9980
+
+// Websocketpp typedefs
+typedef websocketpp::server<websocketpp::config::asio> server;
+
+namespace tests
+{
+namespace functional
+{
+namespace websocket
+{
+namespace utilities
+{
+/// <summary>
+/// Implementation of http request from websocket handshake to avoid leaking
+/// details about websocketpp into test utilities.
+/// </summary>
+class test_http_request_impl : public test_http_request_interface
+{
+public:
+    test_http_request_impl(server::connection_ptr connection) : m_connection(std::move(connection)) {}
+
+    const std::string& username() override { throw std::runtime_error("NYI"); }
+    const std::string& password() override { throw std::runtime_error("NYI"); }
+
+    const std::string& get_header_val(const std::string& header_name) override
+    {
+        return m_connection->get_request_header(header_name);
+    }
+
+private:
+    server::connection_ptr m_connection;
+};
+
+class _test_websocket_server
+{
+public:
+    _test_websocket_server(test_websocket_server* test_srv) : m_test_srv(test_srv)
+    {
+        m_srv.clear_access_channels(websocketpp::log::alevel::all);
+        m_srv.clear_error_channels(websocketpp::log::elevel::all);
+        connect();
+    }
+
+    void connect()
+    {
+        m_srv.set_validate_handler([this](websocketpp::connection_hdl hdl) {
+            auto handler = m_test_srv->get_http_handler();
+            if (handler)
+            {
+                server::connection_ptr connection = m_srv.get_con_from_hdl(hdl);
+                test_http_request request(new test_http_request_impl(connection));
+                test_http_response response = handler(std::move(request));
+
+                // Also need to indicate the connection is rejected if non 200 status code.
+                connection->set_status(static_cast<websocketpp::http::status_code::value>(response.status_code()));
+                if (response.status_code() != 200)
+                {
+                    return false;
+                }
+            }
+            return true;
+        });
+
+        m_srv.set_open_handler([this](websocketpp::connection_hdl hdl) {
+            m_con = hdl;
+            m_server_connected.set();
+        });
+
+        m_srv.set_fail_handler([this](websocketpp::connection_hdl hdl) {
+            m_con = hdl;
+            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<uint8_t>(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);
+
+            test_websocket_msg wsmsg;
+
+            wsmsg.set_data(std::vector<uint8_t>(input.begin(), input.end()));
+
+            wsmsg.set_msg_type(WEB_SOCKET_PONG_TYPE);
+            fn(wsmsg);
+        });
+
+        m_srv.set_message_handler([this](websocketpp::connection_hdl hdl, server::message_ptr msg) {
+            auto pay = msg->get_payload();
+
+            auto fn = m_test_srv->get_next_message_handler();
+            assert(fn);
+
+            test_websocket_msg wsmsg;
+
+            wsmsg.set_data(std::vector<uint8_t>(pay.begin(), pay.end()));
+
+            switch (msg->get_opcode())
+            {
+                case websocketpp::frame::opcode::binary:
+                    wsmsg.set_msg_type(utilities::WEB_SOCKET_BINARY_MESSAGE_TYPE);
+                    break;
+                case websocketpp::frame::opcode::text:
+                    wsmsg.set_msg_type(utilities::WEB_SOCKET_UTF8_MESSAGE_TYPE);
+                    break;
+                case websocketpp::frame::opcode::close: wsmsg.set_msg_type(utilities::WEB_SOCKET_CLOSE_TYPE); break;
+                default:
+                    // Websocketspp does not currently support explicit fragmentation. We should not get here.
+                    std::abort();
+            }
+
+            fn(wsmsg);
+        });
+
+        m_srv.init_asio();
+        m_srv.start_perpetual();
+
+        m_srv.set_reuse_addr(true);
+
+        websocketpp::lib::error_code ec;
+        m_srv.listen(WEBSOCKETS_TEST_SERVER_PORT, ec);
+        if (ec)
+        {
+            throw std::runtime_error(ec.message());
+        }
+
+        m_srv.start_accept();
+        m_thread = std::thread(&server::run, &m_srv);
+    }
+
+    ~_test_websocket_server()
+    {
+        close("destructor");
+        m_srv.stop_listening();
+        m_srv.stop_perpetual();
+        _ASSERTE(m_thread.joinable());
+        m_thread.join();
+    }
+
+    void send_msg(const test_websocket_msg& msg);
+
+    void close(const std::string& reasoning)
+    {
+        websocketpp::lib::error_code ec;
+        m_srv.close(m_con, websocketpp::close::status::going_away, reasoning, ec);
+        // Ignore the error code.
+    }
+
+private:
+    test_websocket_server* m_test_srv;
+
+    std::thread m_thread;
+
+    server m_srv;
+    websocketpp::connection_hdl m_con;
+    // Once the WebSocket object has been initialized,
+    // the below event wil be used to signal that the server has been initialized.
+    // The server can now send messages to the client.
+    pplx::task_completion_event<void> m_server_connected;
+};
+
+test_websocket_server::test_websocket_server() : m_p_impl(std::make_shared<_test_websocket_server>(this)) {}
+
+void test_websocket_server::next_message(std::function<void(test_websocket_msg)> handler)
+{
+    std::lock_guard<std::mutex> lg(m_handler_queue_lock);
+    assert(handler);
+    m_handler_queue.push(handler);
+    assert(m_handler_queue.front());
+}
+
+std::function<void(test_websocket_msg)> test_websocket_server::get_next_message_handler()
+{
+    std::lock_guard<std::mutex> lg(m_handler_queue_lock);
+    assert(m_handler_queue.size() > 0);
+    auto handler = m_handler_queue.front();
+    assert(handler);
+    m_handler_queue.pop();
+    assert(handler);
+    return handler;
+}
+
+void test_websocket_server::send_msg(const test_websocket_msg& msg) { m_p_impl->send_msg(msg); }
+
+std::shared_ptr<_test_websocket_server> test_websocket_server::get_impl() { return m_p_impl; }
+
+void _test_websocket_server::send_msg(const test_websocket_msg& msg)
+{
+    // Wait for the websocket server to be initialized.
+    pplx::task<void>(m_server_connected).wait();
+    const auto& data = msg.data();
+    auto flags = websocketpp::frame::opcode::close;
+    switch (msg.msg_type())
+    {
+        case test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE:
+            flags = websocketpp::frame::opcode::text; // WebSocket::FRAME_FLAG_FIN | WebSocket::FRAME_OP_TEXT;
+            break;
+        case test_websocket_message_type::WEB_SOCKET_BINARY_MESSAGE_TYPE:
+            flags = websocketpp::frame::opcode::binary; // WebSocket::FRAME_FLAG_FIN | WebSocket::FRAME_OP_BINARY;
+            break;
+        case test_websocket_message_type::WEB_SOCKET_CLOSE_TYPE:
+            flags = websocketpp::frame::opcode::close; // WebSocket::FRAME_OP_CLOSE;
+            break;
+        case test_websocket_message_type::WEB_SOCKET_UTF8_FRAGMENT_TYPE:
+        case test_websocket_message_type::WEB_SOCKET_BINARY_FRAGMENT_TYPE:
+        default: throw std::runtime_error("invalid message type");
+    }
+
+    std::string strmsg(data.begin(), data.end());
+
+    if (msg.msg_type() == test_websocket_message_type::WEB_SOCKET_CLOSE_TYPE)
+    {
+        close(strmsg);
+    }
+    else
+    {
+        // std::cerr << "Sending message from server: " << strmsg << std::endl;
+        m_srv.send(m_con, strmsg, flags);
+    }
+}
+
+} // namespace utilities
+} // namespace websocket
+} // namespace functional
+} // namespace tests
diff --git a/Release/tests/functional/websockets/utilities/test_websocket_server.h b/Release/tests/functional/websockets/utilities/test_websocket_server.h
new file mode 100644 (file)
index 0000000..23fbea1
--- /dev/null
@@ -0,0 +1,164 @@
+/***
+ * Copyright (C) Microsoft. All rights reserved.
+ * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+ *
+ * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ *
+ * test_websocket_server.h -- Defines a test server to handle incoming and outgoing messages.
+ *
+ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ ****/
+
+#pragma once
+
+#include <condition_variable>
+#include <iostream>
+#include <map>
+#include <mutex>
+#include <sstream>
+#include <unittestpp.h>
+
+#ifndef WEBSOCKET_UTILITY_API
+#ifdef WEBSOCKETTESTUTILITY_EXPORTS
+#define WEBSOCKET_UTILITY_API __declspec(dllexport)
+#else
+#define WEBSOCKET_UTILITY_API __declspec(dllimport)
+#endif
+#endif
+
+#if !defined(_M_ARM) || defined(__cplusplus_winrt)
+
+namespace tests
+{
+namespace functional
+{
+namespace websocket
+{
+namespace utilities
+{
+class _test_websocket_server;
+
+// The different types of a websocket message.
+enum test_websocket_message_type
+{
+    WEB_SOCKET_BINARY_MESSAGE_TYPE,
+    WEB_SOCKET_BINARY_FRAGMENT_TYPE,
+    WEB_SOCKET_UTF8_MESSAGE_TYPE,
+    WEB_SOCKET_UTF8_FRAGMENT_TYPE,
+    WEB_SOCKET_CLOSE_TYPE,
+    WEB_SOCKET_PING_TYPE,
+    WEB_SOCKET_PONG_TYPE
+};
+
+// Interface containing details about the HTTP handshake request received by the test server.
+class test_http_request_interface
+{
+public:
+    virtual ~test_http_request_interface() {}
+    virtual const std::string& username() = 0;
+    virtual const std::string& password() = 0;
+    virtual const std::string& get_header_val(const std::string& header_name) = 0;
+};
+typedef std::unique_ptr<test_http_request_interface> test_http_request;
+
+// Class that contains details about the HTTP handshake response to be sent by the test server
+class test_http_response
+{
+public:
+    void set_realm(std::string realm) { m_realm = std::move(realm); }
+    void set_status_code(unsigned short code) { m_status_code = code; }
+    const std::string& realm() const { return m_realm; }
+    unsigned short status_code() const { return m_status_code; }
+
+private:
+    std::string m_realm;
+    unsigned short m_status_code;
+};
+
+// Represents a websocket message at the test server.
+// Contains a vector that can contain text/binary data
+// and a type variable to denote the message type.
+class test_websocket_msg
+{
+public:
+    const std::vector<unsigned char>& data() const { return m_data; }
+    void set_data(std::vector<unsigned char> data) { m_data = std::move(data); }
+
+    test_websocket_message_type msg_type() const { return m_msg_type; }
+    void set_msg_type(test_websocket_message_type type) { m_msg_type = type; }
+
+private:
+    std::vector<unsigned char> m_data;
+    test_websocket_message_type m_msg_type;
+};
+
+class websocket_asserts
+{
+public:
+    static void assert_message_equals(test_websocket_msg& msg,
+                                      const std::string& expected_data,
+                                      test_websocket_message_type expected_flag)
+    {
+        std::vector<unsigned char> temp_vec(expected_data.begin(), expected_data.end());
+        assert_message_equals(msg, temp_vec, expected_flag);
+    }
+
+    static void assert_message_equals(test_websocket_msg& msg,
+                                      const std::vector<unsigned char>& expected_data,
+                                      test_websocket_message_type expected_flag)
+    {
+        VERIFY_ARE_EQUAL(msg.msg_type(), expected_flag);
+        auto& data = msg.data();
+        VERIFY_ARE_EQUAL(data.size(), expected_data.size());
+        VERIFY_IS_TRUE(std::equal(expected_data.begin(), expected_data.end(), data.begin()));
+    }
+
+private:
+    websocket_asserts() {}
+    ~websocket_asserts() CPPREST_NOEXCEPT {}
+};
+
+// Test websocket server.
+class test_websocket_server
+{
+public:
+    WEBSOCKET_UTILITY_API test_websocket_server();
+
+    // Tests can add a handler to handle (verify) the next message received by the server.
+    // If the test plans to send n messages, n handlers must be registered.
+    // The server will call the handler in order, for each incoming message.
+    WEBSOCKET_UTILITY_API void next_message(std::function<void __cdecl(test_websocket_msg)> msg_handler);
+    WEBSOCKET_UTILITY_API std::function<void(test_websocket_msg)> get_next_message_handler();
+
+    // Handler for initial HTTP request.
+    typedef std::function<test_http_response __cdecl(test_http_request)> http_handler;
+    WEBSOCKET_UTILITY_API void set_http_handler(http_handler handler) { m_http_handler = handler; }
+    WEBSOCKET_UTILITY_API http_handler get_http_handler() { return m_http_handler; }
+
+    // Tests can use this API to send a message from the server to the client.
+    WEBSOCKET_UTILITY_API void send_msg(const test_websocket_msg& msg);
+    WEBSOCKET_UTILITY_API std::shared_ptr<_test_websocket_server> get_impl();
+
+private:
+#if !defined(_MSC_VER) || _MSC_VER >= 1800
+    test_websocket_server(const test_websocket_server&) = delete;
+    test_websocket_server& operator=(const test_websocket_server&) = delete;
+    test_websocket_server(test_websocket_server&&) = delete;
+    test_websocket_server& operator=(test_websocket_server&&) = delete;
+#endif
+
+    // Queue to maintain the request handlers.
+    // Note: This queue is not thread-safe. Use m_handler_queue_lock to synchronize.
+    std::mutex m_handler_queue_lock;
+    std::queue<std::function<void(test_websocket_msg)>> m_handler_queue;
+    // Handler to address the HTTP handshake request. To be used in scenarios where tests may wish to fail the HTTP
+    // request and not proceed with the websocket connection.
+    http_handler m_http_handler;
+    std::shared_ptr<_test_websocket_server> m_p_impl;
+};
+} // namespace utilities
+} // namespace websocket
+} // namespace functional
+} // namespace tests
+
+#endif
diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt
new file mode 100644 (file)
index 0000000..dab0d36
--- /dev/null
@@ -0,0 +1,159 @@
+
+THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
+Do Not Translate or Localize
+
+C++ REST SDK incorporates third party material from the projects listed below. The original copyright notice and the license under which Microsoft received such third party material are set forth below. Microsoft reserves all other rights not expressly granted, whether by implication, estoppel or otherwise.  
+
+1.     Websocket++ (http://www.zaphoyd.com/websocketpp/)
+
+%% Websocket++ NOTICES, INFORMATION, AND LICENSE BEGIN HERE
+=========================================
+Main Library:
+
+Copyright (c) 2014, Peter Thorson. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the WebSocket++ Project nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Bundled Libraries:
+
+****** Base 64 Library (base64/base64.hpp) ******
+base64.hpp is a repackaging of the base64.cpp and base64.h files into a
+single header suitable for use as a header only library. This conversion was
+done by Peter Thorson (webmaster@zaphoyd.com) in 2012. All modifications to
+the code are redistributed under the same license as the original, which is
+listed below.
+
+base64.cpp and base64.h
+
+Copyright (C) 2004-2008 RenĆ© Nyffenegger
+
+This source code is provided 'as-is', without any express or implied
+warranty. In no event will the author be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this source code must not be misrepresented; you must not
+  claim that you wrote the original source code. If you use this source code
+  in a product, an acknowledgment in the product documentation would be
+  appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and must not be
+  misrepresented as being the original source code.
+
+3. This notice may not be removed or altered from any source distribution.
+
+RenĆ© Nyffenegger rene.nyffenegger@adp-gmbh.ch
+
+****** SHA1 Library (sha1/sha1.hpp) ******
+sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the shallsha1
+library (http://code.google.com/p/smallsha1/) into a single header suitable for
+use as a header only library. This conversion was done by Peter Thorson
+(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed
+under the same license as the original, which is listed below.
+
+ Copyright (c) 2011, Micael Hildenborg
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of Micael Hildenborg nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+****** MD5 Library (common/md5.hpp) ******
+md5.hpp is a reformulation of the md5.h and md5.c code from
+http://www.opensource.apple.com/source/cups/cups-59/cups/md5.c to allow it to
+function as a component of a header only library. This conversion was done by
+Peter Thorson (webmaster@zaphoyd.com) in 2012 for the WebSocket++ project. The
+changes are released under the same license as the original (listed below)
+
+Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+L. Peter Deutsch
+ghost@aladdin.com
+
+****** UTF8 Validation logic (utf8_validation.hpp) ******
+utf8_validation.hpp is adapted from code originally written by Bjoern Hoehrmann
+<bjoern@hoehrmann.de>. See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for
+details.
+
+The original license:
+
+Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+=========================================
+END OF Websocket++ NOTICES, INFORMATION, AND LICENSE
+
+
diff --git a/azure-devops/build-ubuntu-apt.yml b/azure-devops/build-ubuntu-apt.yml
new file mode 100644 (file)
index 0000000..ae3844e
--- /dev/null
@@ -0,0 +1,30 @@
+parameters:\r
+  name: 'Ubuntu_1604_Apt'\r
+  image: 'Ubuntu 16.04'\r
+\r
+jobs:\r
+- job: ${{ parameters.name }}\r
+  pool:\r
+    vmImage: ${{ parameters.image }}\r
+  steps:  \r
+  - script: |\r
+      sudo apt -y remove php*\r
+      sudo apt install -y ppa-purge\r
+      sudo ppa-purge -y ppa:ondrej/php\r
+      unset BOOST_ROOT\r
+      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\r
+      mkdir build.debug\r
+      cd build.debug\r
+      /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..\r
+      cd ..\r
+      mkdir build.release\r
+      cd build.release\r
+      /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ..\r
+      cd ..\r
+      ninja -C build.debug\r
+      ninja -C build.release\r
+      cd build.debug/Release/Binaries\r
+      ./test_runner *test.so\r
+      cd ../../../build.release/Release/Binaries\r
+      ./test_runner *test.so\r
+    displayName: Run build\r
diff --git a/azure-devops/build-ubuntu-vcpkg.yml b/azure-devops/build-ubuntu-vcpkg.yml
new file mode 100644 (file)
index 0000000..aeae2b7
--- /dev/null
@@ -0,0 +1,45 @@
+parameters:\r
+  name: 'Ubuntu_1604_Vcpkg'\r
+  image: 'Ubuntu 16.04'\r
+\r
+jobs:\r
+- job: ${{ parameters.name }}\r
+  pool:\r
+    vmImage: ${{ parameters.image }}\r
+  steps:\r
+  - script: |\r
+      sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y\r
+      sudo apt -y update\r
+      sudo apt install g++-9 ninja-build -y\r
+      git submodule update --init vcpkg\r
+      ./vcpkg/bootstrap-vcpkg.sh\r
+      ./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\r
+    displayName: Vcpkg install dependencies\r
+  - script: |\r
+      mkdir build.debug\r
+      mkdir build.release\r
+    displayName: Make Build Directories\r
+  - task: CMake@1\r
+    inputs:\r
+      workingDirectory: 'build.debug'\r
+      cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake ..'\r
+  - task: CMake@1\r
+    inputs:\r
+      workingDirectory: 'build.release'\r
+      cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake ..'\r
+  - script: |\r
+      cd build.debug\r
+      ninja\r
+    displayName: 'Run ninja debug'\r
+  - script: |\r
+      cd build.debug/Release/Binaries\r
+      ./test_runner *test.so\r
+    displayName: 'Run Tests debug'\r
+  - script: |\r
+      cd build.release\r
+      ninja\r
+    displayName: 'Run ninja, release'\r
+  - script: |\r
+      cd build.release/Release/Binaries\r
+      ./test_runner *test.so\r
+    displayName: 'Run tests, release'\r
diff --git a/azure-devops/build-windows.yml b/azure-devops/build-windows.yml
new file mode 100644 (file)
index 0000000..45045ee
--- /dev/null
@@ -0,0 +1,47 @@
+parameters:\r
+  name: 'Windows_VS2019_x86'\r
+  targetPlatform: 'x86'\r
+  image: 'windows-latest'\r
+\r
+jobs:\r
+- job: ${{ parameters.name }}\r
+  pool:\r
+    vmImage: ${{ parameters.image }}\r
+  variables:\r
+    vcpkgLocation: '$(Build.SourcesDirectory)/vcpkg'\r
+    vcpkgResponseFile: $(Build.SourcesDirectory)/azure-devops/vcpkg-windows.txt\r
+  steps:\r
+    - script: git submodule update --init vcpkg\r
+      displayName: Checkout vcpkg submodule\r
+    - task: run-vcpkg@0\r
+      displayName: 'Run vcpkg'\r
+      inputs:\r
+        vcpkgArguments: '@$(vcpkgResponseFile)'\r
+        vcpkgDirectory: '$(vcpkgLocation)'\r
+        vcpkgTriplet: ${{ parameters.targetPlatform }}-windows\r
+    - task: run-cmake@0\r
+      displayName: 'Run CMake with Ninja (Debug)'\r
+      enabled: true\r
+      inputs:\r
+        cmakeListsOrSettingsJson: 'CMakeListsTxtBasic'\r
+        cmakeBuildType: 'Debug'\r
+        useVcpkgToolchainFile: true\r
+        buildDirectory: $(Build.ArtifactStagingDirectory)/${{ parameters.targetPlatform }}_Debug\r
+        cmakeAppendedArgs: '-DCPPREST_EXCLUDE_BROTLI=OFF'\r
+    - script: |\r
+        cd $(Build.ArtifactStagingDirectory)\${{ parameters.targetPlatform }}_Debug\Release\Binaries\r
+        .\test_runner.exe *testd.dll\r
+      displayName: 'Run tests, debug'\r
+    - task: run-cmake@0\r
+      displayName: 'Run CMake with Ninja (Release)'\r
+      enabled: true\r
+      inputs:\r
+        cmakeListsOrSettingsJson: 'CMakeListsTxtBasic'\r
+        cmakeBuildType: 'Release'\r
+        useVcpkgToolchainFile: true\r
+        buildDirectory: $(Build.ArtifactStagingDirectory)/${{ parameters.targetPlatform }}_Release\r
+        cmakeAppendedArgs: '-DCPPREST_EXCLUDE_BROTLI=OFF'\r
+    - script: |\r
+        cd $(Build.ArtifactStagingDirectory)\${{ parameters.targetPlatform }}_Release\Release\Binaries\r
+        .\test_runner.exe *test.dll\r
+      displayName: 'Run tests, release'\r
diff --git a/azure-devops/vcpkg-windows.txt b/azure-devops/vcpkg-windows.txt
new file mode 100644 (file)
index 0000000..582d18d
--- /dev/null
@@ -0,0 +1,7 @@
+openssl\r
+boost-system\r
+boost-date-time\r
+boost-regex\r
+boost-interprocess\r
+websocketpp\r
+brotli\r
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644 (file)
index 0000000..b83222d
--- /dev/null
@@ -0,0 +1,205 @@
+# CppRestSdk Azure Pipelines Configuration
+
+jobs:
+  - 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: '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
+      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 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 ..'
+    - task: MSBuild@1
+      inputs:
+        solution: 'build.common/ALL_BUILD.vcxproj'
+        maximumCpuCount: true
+        platform: 'x64'
+  - 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'
+    steps:
+    - script: |
+        mkdir Build_android/build
+        cd Build_android/build
+        export NCPU=2
+        ../configure.sh --ndk /usr/local/lib/android/sdk/ndk-bundle
+      displayName: 'Build for Android'
+  - job: MacOS_Homebrew
+    pool:
+      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
+        mkdir build.release.static
+      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 ..'
+    - task: CMake@1
+      inputs:
+        workingDirectory: 'build.release.static'
+        cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF ..'
+    - script: |
+        cd build.debug
+        ninja
+      displayName: 'Run ninja, debug'
+    - script: |
+        cd build.debug/Release/Binaries
+        ./test_runner *test.dylib
+      displayName: 'Run tests, debug'
+    - script: |
+        cd build.release
+        ninja
+      displayName: 'Run ninja, release'
+    - script: |
+        cd build.release/Release/Binaries
+        ./test_runner *test.dylib
+      displayName: 'Run tests, release'
+    - script: |
+        cd build.release.static
+        ninja
+      displayName: 'Run ninja, release static'
+  - job: MacOS_Vcpkg
+    pool:
+      vmImage: 'macOS-latest'
+    steps:
+    - script: git submodule update --init vcpkg
+      displayName: Checkout vcpkg submodule
+    - script: |
+        ./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_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_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
+        $(Build.SourcesDirectory)/vcpkg/downloads/tools/ninja-1.10.1-osx/ninja
+      displayName: 'Run ninja debug'
+    - script: |
+        cd build.debug/Release/Binaries
+        ./test_runner *test.dylib
+      displayName: 'Run Tests debug'
+    - script: |
+        cd build.release
+        $(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
+  #   pool:
+  #     vmImage: 'macOS-latest'
+  #   steps:
+  #   - script: |
+  #       cd Build_iOS
+  #       ./configure.sh
+  #     displayName: 'Build for iOS'
+  - job: Ubuntu_1604_Apt_winhttppal
+    pool:
+      vmImage: 'Ubuntu 16.04'
+    steps:
+    - script: |
+        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
new file mode 100644 (file)
index 0000000..c547665
--- /dev/null
@@ -0,0 +1,243 @@
+cpprestsdk (2.10.18)\r
+* PR#1571 Add ability to parse and emit the NT Epoch 1601-01-01T00:00:00Z\r
+* PR#1571 Update vcpkg submodule\r
+* Update CI configuration\r
+-- cpprestsdk team MON, 1 Feb 2021 20:02:00 -0700\r
+\r
+cpprestsdk (2.10.17)\r
+* PR#1550 Fix year calculation for the last day of a leap year\r
+* PR#1523 Fix wrong linking of Apple Frameworks on MacOS\r
+* PR#1520 Define __STDC_FORMAT_MACROS when it hasn't been defined to avoid duplicate define error. \r
+* PR#1415 Delete apparently broken .vcxprojs and .pfxes.\r
+* Removed defunct email contact information from the readme\r
+-- cpprestsdk team WED, 30 Dec 2020 20:08:00 -0700\r
+\r
+cpprestsdk (2.10.16)\r
+* PR#1383 CMake fixes + CMake search for OpenSSL (macOS)\r
+* PR#1392 Update submodule websocketpp to 0.8.2\r
+* 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\r
+* PR#1379 Fix compilation with GCC 4.8/4.9, which was broken by commit 53fab3a.\r
+* PR#1328 Add support for HTTP redirection in ASIO and WinHTTP-based http_clients\r
+* PR#1332 Fix more http test build fails in certain configurations\r
+* PR#1370 Remove redundant std::move noted by gcc 9.2 (-Wredundant-move)\r
+* PR#1372 Static analyzer (PVS Studio) fixes\r
+* PR#1350 Expose json::value::parse for UTF8 string on Windows\r
+* PR#1344 libcpprestsdk: fix building as a static library\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  FRI, 24 Apr 2020 16:56:00 -0700\r
+\r
+cpprestsdk (2.10.15)\r
+* Extremely special thanks to @garethsb-sony for a large number of contributions in this release\r
+* PR#1209 Workarounds for two GCC 4.7.2 bugs with lambda functions\r
+* PR#1220 Fix SxS debug-release builds with Visual Studio\r
+* 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\r
+* 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."\r
+* PR#1233 Trim whitespace and nulls the same way.\r
+* PR#1248 Avoid using permissive- with ZW which breaks VS2019\r
+* PR#1182 Support for WinHTTPAL curl-to-WinHTTP adapter\r
+* PR#1253 http_server_httpsys.cpp requires linking against httpapi.lib, http_client_winhttp.cpp does not.\r
+* PR#1263 Remove trailing slash on websocketpp submodule url, which causes checkout failure on CircleCI with git 2.22.0\r
+* PR#1293 Update vcpkg and remove tests that look for web servers that no longer exist\r
+* PR#1288 Fix test case broken by commit f4c863b\r
+* PR#1276 Added comparison overrides to utility::datetime\r
+* PR#1289 Fix various warnings reported by gcc 9.3, and possibly earlier versions\r
+* PR#1334 Update vcpkg and boost on Android\r
+* PR#1306 Change default installation directory for cmake files to cmake/cpprestsdk\r
+* PR#1330 Use LC_ALL_MASK rather than LC_ALL when calling newlocale\r
+* PR#1310 Add TCP_NODELAY to disable Nagle's algorithm in Boost.ASIO-based http_client\r
+* PR#1335 Turn VS2015 back on now that vcpkg is fixed.\r
+* PR#1322 Enable HTTP compression support on all platforms\r
+* PR#1340 Add Ubuntu 18.04 testing.\r
+* PR#1342 Use C++11 synchronization classes under macOS too\r
+* PR#1339 Fix tcp::resolver data race in the asio backend and be defensive against empty results\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  THR, 22 Feb 2020 08:31:00 -0800\r
+\r
+cpprestsdk (2.10.14)\r
+* 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.\r
+* 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.\r
+* PR#1133 Add switches to make apiscan happy.\r
+* PR#1130 json: {"meow"} is not a valid object\r
+* PR#1150 Undefine compress if it is defined by zconf.h\r
+* PR#1156 Fix broken CI Builds\r
+* PR#1155 Use EVP_MAX_MD_SIZE instead of HMAC_MAX_MD_CBLOCK\r
+* PR#1145 Remove the address_configured flag on tcp::resolver::query\r
+* PR#1143 add ping and pong to message handler\r
+* PR#539 Fix reusing ASIO http_client connecting to HTTPS server via proxy\r
+* PR#1175 Fix issue #1171: Order of object destruction\r
+* PR#1183 FIX: SSL proxy tunnel support with basic auth\r
+* PR#1184 Fix profile being set on the compiler instead of the linker.\r
+* PR#1185 Update boost-for-android for Android NDK r20 and disable macOS Homebrew.\r
+* PR#1187 Replace CPPREST_TARGET_XP with version checks, remove ""s, and other cleanup\r
+* PR#1188 Remove proxy settings detection behavior in "default proxy mode."\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  TUE, 16 Jul 2019 09:06:00 +0200\r
+\r
+cpprestsdk (2.10.13)\r
+* PR#1120 Fix off by one error in leap years before year 2000, and bad day names\r
+* PR#1117 Parse and emit years from 1900 to 9999, and remove environment variable dependence on Android\r
+* PR#1106 Paranoia for overflow of sprintf buffer in the year 10000\r
+* PR#1101 Update request_timeout_microsecond timeout\r
+* PR#1097 Allow error handling for time out in http_client_asio handle_connect\r
+* PR#1094 Avoid tripping over 32 bit time_t mistakes.\r
+* PR#1093 Don't initialize atomic_flag with 0.\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  WED, 24 Apr 2019 10:57:00 -0800\r
+\r
+cpprestsdk (2.10.12)\r
+* PR#1088 Fix data race, GitHub #1085\r
+* PR#1084 Fix oauth nonces containing nulls.\r
+* PR#1082 Workaround data-race on websocketpp's _htonll function\r
+* PR#1080 Fix thread not joined\r
+* PR#1076 Rewrite date formatting and parsing\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  TUE, 26 Mar 2019 11:57:00 -0800\r
+\r
+cpprestsdk (2.10.11)\r
+* PR##1073 Move get_jvm_env back into the crossplat namespace\r
+* PR##1049 Add the missing ssl::context callback in websocket_client_config\r
+* PR##1072 Gate stdext::checked_array_iterator usage on _ITERATOR_DEBUG_LEVEL\r
+* PR##1051 Fix http_client_asio "https" with a proxy\r
+* PR##1071 Add --vcpkg-root to repair UWP.\r
+* PR##1041 Update Boost_for_android for Android R19\r
+* PR##1064 Enable testing from root directory\r
+* PR##1057 Returns int64 value in function of seeking to file end on x64 Windows.\r
+* PR##1068 Don't close the output stream when reporting errors reading the body.\r
+* PR##1053 Update vcpkg.\r
+* PR##1032 Fix HTTP/1.0 'Keep-Alive' handling in http_client\r
+* PR##1040 Disable WINHTTP_AUTOPROXY_OPTIONS machinery when using WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY.\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  WED, 20 Mar 2019 02:30:00 -0800\r
+\r
+cpprestsdk (2.10.10)\r
+----------------------\r
+* PR#1023 Handle multi-byte unicode characters in json parsing\r
+* PR#1033 Temporary fix for VS2013. Note that VS2013 is still not in support.\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  TUE, 29 Jan 2019 22:38:00 -0800\r
+\r
+cpprestsdk (2.10.9)\r
+----------------------\r
+* PR#973  Address gcc warnings-as-errors in compression code, test improvements\r
+* PR#986  Prevent infinite loop during proxy authentication\r
+* PR#987  Remove use of aligned_union that broke CentOS 7.\r
+* PR#1004 #993, #1002: Add flexibility for iOS building. Adds command line argsā€¦\r
+* PR#1009 gcc: Fix compilation with -fno-operator-names\r
+* PR#1019 FIX: crash with std::logic_error when reusing a connection that timed out on the server\r
+* PR#1021 handle null bytes when parsing utf8\r
+* PR#1017 Add in support for adding i386 slice when building for 32-bit targets. Also improve messaging and add means to clean\r
+* PR#1024 http_compression.cpp: fix build with gcc 4.7\r
+* PR#1022 Resolve double free when WinHttpSendRequest fails\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  FRI, 18 Jan 2019 16:58:00 -0800\r
+\r
+cpprestsdk (2.10.8)\r
+----------------------\r
+* PR#938 Allow ppltasks.h and pplxtasks.h to co-exist\r
+* PR#951 Fix incorrect const in reinterpret_cast\r
+* PR#955 Fix UWP missing header\r
+* PR#956 Adds support for OpenSSL 1.1.1\r
+* PR#959 Fix Android build issue by remove the crossplat name space before android parameters\r
+* PR#960 Update vcpkg to latest master to fix VS2015 build.\r
+* PR#966 Fix string size for error message generated by windows_category\r
+* PR#958 Add uri_builder::append_path_raw(...) to allow adding elements to path intentionally beginning with '/' ("//" will result in the final path value)\r
+* PR#952 cmake: add code to detect system brotli library\r
+* PR#963 Fix Brotli compress_helper early termination issue\r
+* PR#961 Fixes iOS builds and makes it more future proof\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  WED, 14 Nov 2018 10:24:00 -0800\r
+\r
+cpprestsdk (2.10.7)\r
+----------------------\r
+* cpprestsdk now has Azure Pipelines continuous integration.\r
+* Builds for Android and iOS were repaired, now checked in Azure Pipelines to make sure that doesn't bit-rot in the future.\r
+* Several race conditions in the listener were worked around; the listeners remain experimental and are unlikely to productized in their current form; the race conditions are structural, but at least the client tests pass most of the time.\r
+* Incorrect handling of connection pooling bug that caused segfaults on Ubuntu introduced in 2.10.4 has been repaired.\r
+* websocketpp checked in 0.5.1 version has been changed to a submodule and updated to 0.8.1.\r
+* Added an API to set the number of threads in the asio thread pool, see PR#883\r
+* Legacy unmaintained Visual Studio project files have been deleted, please use CMake instead.\r
+* PR#670 Export methods to set/get the ambient scheduler in cpprest dll\r
+* PR#866 Add Transfer-Encoding compression support and extensible compression API\r
+* PR#892 Improve utf8_to_utf16 speed for common path\r
+* PR#897 added URI resolution according to RFC3986\r
+* PR#935 Fix spelling mistakes across the library\r
+* PR#936 Use pplx namespace consistently\r
+* PR#937 Remove _ASYNCRTIMP from ~http_listener() and implement inline\r
+* PR#940 Avoid using identifiers reserved by C++ in header guards\r
+* PR#943 blackjack sample: use vector instead of shared pointer for array\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  MON, 30 Oct 2018 20:32:00 -0800\r
+\r
+cpprestsdk (2.10.6)\r
+----------------------\r
+* PR#844 Fix clang build error\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  MON, 30 Aug 2018 16:51:00 -0800\r
+\r
+cpprestsdk (2.10.5)\r
+----------------------\r
+* Issue#842 Fix incorrect `cpprest/version.h`\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  FRI, 17 Aug 2018 09:47:00 -0800\r
+\r
+cpprestsdk (2.10.4)\r
+----------------------\r
+* Added a `.clang-format` to enable consistent formatting.\r
+* Added support for `Host:` headers changing the checked CNAME field for SSL certificates in WinHTTP and Asio.\r
+* PR#736 passes 0666 to open() for creating files to better match the default behavior for other http clients (wget, etc).\r
+* PR#732 fixes a build issue with clang\r
+* PR#737 taught our cmake to respect the GNUInstallDirs variables\r
+* PR#762 improved handling of dead connections in the connection pool on Asio.\r
+* PR#750 improved error handling in the accept() call in `http_listener`\r
+* PR#776 improved the iOS buildsystem\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  WED, 15 Aug 2018 12:35:00 -0800\r
+\r
+cpprestsdk (2.10.3)\r
+----------------------\r
+* Added a root `CMakeLists.txt` to improve support for VS2017 Open Folder.\r
+* PR#809 improves support for `/permissive-` in MSVC\r
+* Issue#804 fixed a regression due to compression support; we no longer fail on unknown Content-Encoding headers if we did not set Accepts-Encoding\r
+* PR#813 fixes build failure with boost 1.63\r
+* PR#779 PR#787 suppress and fix some warnings with new versions of gcc and clang\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  THU, 2 Aug 2018 15:52:00 -0800\r
+\r
+cpprestsdk (2.10.0)\r
+----------------------\r
+* Removed VS2013 MSBuild files. Use CMake with the "Visual Studio 12 2013" generator.\r
+* Added VS2017 MSBuild files for convenience. It is highly recommended to use vcpkg or CMake instead to build the product library.\r
+* Added UWP versions of the Windows Store samples for VS2017.\r
+* Updated minimum required cmake version to 3.0.\r
+* Added CMake config-file support to installation. This should be consumed by doing:\r
+```cmake\r
+find_package(cpprestsdk REQUIRED)\r
+target_link_libraries(my_executable PRIVATE cpprestsdk::cpprest)\r
+```\r
+* Fixed several race conditions and memory leaks in the ASIO `http_client`.\r
+* Fixed process termination bug around certain exceptional cases in all `http_client`s.\r
+* Improved handling of `/Zcwchar_t-` on MSVC. That doesn't make it a good idea.\r
+* Fixed use-after-free in the Windows Desktop `http_client` exposed by VS2017.\r
+* Totally overhaul the CMake buildsystem for much better support of Windows and more shared code between platforms.\r
+* PR#550 adds all remaining official HTTP status codes to `http::status_codes`.\r
+* PR#563 wraps SSL errors on Windows Desktop in `http_exception`s, with more readable descriptions.\r
+* PR#562 and PR#307 fixes building with LibreSSL.\r
+* PR#551 adds convenience wrappers `json::value::has_T_field(T)` for inspecting object values.\r
+* PR#549 fixes a race condition in the ASIO client during header parsing.\r
+* PR#495 fixes a memory leak during proxy autodetection on Windows Desktop.\r
+* PR#496 and PR#500 expand proxy autodetection to also consider Internet Explorer settings on Windows Desktop.\r
+* PR#498 fixes error when handling responses of type NoContent, NotModified, or from 100 to 199.\r
+* PR#398 enables specifying the User Agent used in OAuth2 requests.\r
+* PR#494 improves the BingRequest sample's handling of proxies.\r
+* PR#516 enables certificate revocation checks on Windows Desktop.\r
+* PR#502 improves compatibility with glibc 2.26.\r
+* PR#507 adds `http_request::get_remote_address()` to expose the client's IP address for `http_listener`.\r
+* PR#521 enables use of empty passwords on Windows in `web::credentials`.\r
+* PR#526 and PR#285 improve compatibility with openssl 1.1.0.\r
+* PR#527 fixes a bug in the ASIO `http_client` where the proxy is passed the same credentials as the target host.\r
+* PR#504 makes `uri_builder::to_string()` and `uri_builder::to_uri()` `const`.\r
+* PR#446 adds handling for the host wildchar `+` to the ASIO `http_listener`.\r
+* PR#465 improves compatibility with clang on Linux.\r
+* PR#454 improves compatibility with icc 17.0.\r
+* PR#487 fixes static library builds of `test_runner` on non-Windows platforms.\r
+* PR#415 handles malformed URL requests to the ASIO `http_listener` instead of crashing.\r
+* PR#393 fixes a race condition in the websocketpp `websocket_client`.\r
+* PR#259 fixes several races in the ASIO `http_listener` which result in memory leaks or use after free of the connection objects.\r
+* PR#376 adds `http_client_config::set_nativesessionhandle_options()` which enables customization of the session handle on Windows Desktop.\r
+* PR#365 updates our convenience OpenSSL build scripts for Android to use openssl 1.0.2k.\r
+* PR#336 makes the ASIO `http_client` more consistent with the Windows clients by not appending the port when it is default. This improves compatibility with AWS S3.\r
+* PR#251 dramatically improves UTF8/16 conversions from 6s per 1MB to 3s per 1GB (2000x improvement).\r
+* PR#246 enables TLS 1.1 and 1.2 on Windows 7 and Windows 8.\r
+* PR#308 enables limited IPv6 support to `http_client` and `http_server`, depending on the underlying platform.\r
+* PR#309 fixes a bug in base64 encoding that previously read beyond the input array, causing segfaults/AVs.\r
+* PR#233 adds compression support (deflate and gzip) for Windows Desktop and ASIO `http_client`s based on Zlib.\r
+* PR#218 fixes a memory leak in the UWP `http_client` when processing headers.\r
+* PR#260 fixes inappropriate handling of certain connections errors in the ASIO `http_listener`.\r
+\r
+-- cpprestsdk team <askcasablanca@microsoft.com>  SAT, 21 Oct 2017 00:52:00 -0800\r
diff --git a/license.txt b/license.txt
new file mode 100644 (file)
index 0000000..fb805d9
--- /dev/null
@@ -0,0 +1,25 @@
+C++ REST SDK 
+
+The MIT License (MIT)
+
+Copyright (c) Microsoft Corporation
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.