Remove O2, add cpprestsdk

This commit is contained in:
Jan Kaluza 2015-11-19 15:19:14 +01:00
parent 7291e4c1ca
commit 8f1f90064c
947 changed files with 173652 additions and 4059 deletions

View file

@ -1 +1 @@
ADD_SUBDIRECTORY(o2)
ADD_SUBDIRECTORY(cpprestsdk)

204
3rdparty/cpprestsdk/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,204 @@
set(CMAKE_LEGACY_CYGWIN_WIN32 0)
cmake_minimum_required(VERSION 2.6)
enable_testing()
set(WARNINGS)
set(ANDROID_STL_FLAGS)
# Platform (not compiler) specific settings
if(IOS)
set(IOS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Build_iOS")
set(Boost_FRAMEWORK "-F ${IOS_SOURCE_DIR} -framework boost")
set(Boost_INCLUDE_DIR "${IOS_SOURCE_DIR}/boost.framework/Headers")
set(OPENSSL_FOUND 1)
set(OPENSSL_INCLUDE_DIR "${IOS_SOURCE_DIR}/openssl/include")
set(OPENSSL_LIBRARIES
"${IOS_SOURCE_DIR}/openssl/lib/libcrypto.a"
"${IOS_SOURCE_DIR}/openssl/lib/libssl.a"
)
# The cxx_flags must be reset here, because the ios-cmake toolchain file unfortunately sets "-headerpad_max_install_names" which is not a valid clang flag.
set(CMAKE_CXX_FLAGS "-fvisibility=hidden -fvisibility-inlines-hidden")
set(BUILD_SHARED_LIBS OFF)
set(BUILD_SAMPLES OFF)
option(BUILD_TESTS "Build tests." ON)
elseif(ANDROID)
set(Boost_COMPILER "-clang")
set(Boost_USE_STATIC_LIBS ON)
if(ARM)
set(BOOST_ROOT "${CMAKE_BINARY_DIR}/../Boost-for-Android/build")
set(BOOST_LIBRARYDIR "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/lib")
else()
set(BOOST_ROOT "${CMAKE_BINARY_DIR}/../Boost-for-Android-x86/build")
set(BOOST_LIBRARYDIR "${CMAKE_BINARY_DIR}/../Boost-for-Android-x86/build/lib")
endif()
find_host_package(Boost 1.55 EXACT REQUIRED COMPONENTS random system thread filesystem chrono atomic)
set(OPENSSL_FOUND 1)
if(ARM)
set(OPENSSL_INCLUDE_DIR "${CMAKE_BINARY_DIR}/../openssl/armeabi-v7a/include")
set(OPENSSL_LIBRARIES
"${CMAKE_BINARY_DIR}/../openssl/armeabi-v7a/lib/libssl.a"
"${CMAKE_BINARY_DIR}/../openssl/armeabi-v7a/lib/libcrypto.a"
)
else()
set(OPENSSL_INCLUDE_DIR "${CMAKE_BINARY_DIR}/../openssl/x86/include")
set(OPENSSL_LIBRARIES
"${CMAKE_BINARY_DIR}/../openssl/x86/lib/libssl.a"
"${CMAKE_BINARY_DIR}/../openssl/x86/lib/libcrypto.a"
)
endif()
if(ARM)
set(LIBCXX_STL "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/thumb/libgnustl_static.a")
else()
set(LIBCXX_STL "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/libs/x86/libgnustl_static.a")
endif()
# These are used in the shared library case
set(ANDROID_STL_FLAGS
${LIBCXX_STL}
atomic
dl
gcc
c
m
-nodefaultlibs
)
option(BUILD_SHARED_LIBS "Build shared Libraries." OFF)
set(BUILD_SAMPLES OFF)
option(BUILD_TESTS "Build tests." ON)
elseif(UNIX) # This includes OSX
find_package(Boost REQUIRED COMPONENTS random chrono system thread regex filesystem)
find_package(Threads REQUIRED)
if(APPLE AND NOT 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)
endif()
# This should prevent linking against the system provided 0.9.8y
set(_OPENSSL_VERSION "")
find_package(OpenSSL 1.0.0 REQUIRED)
option(BUILD_SHARED_LIBS "Build shared Libraries." ON)
option(BUILD_TESTS "Build tests." OFF)
option(BUILD_SAMPLES "Build samples." OFF)
option(CASA_INSTALL_HEADERS "Install header files." ON)
if(CASA_INSTALL_HEADERS)
file(GLOB CASA_HEADERS_CPPREST include/cpprest/*.hpp include/cpprest/*.h include/cpprest/*.dat)
install(FILES ${CASA_HEADERS_CPPREST} DESTINATION include/cpprest)
file(GLOB CASA_HEADERS_PPLX include/pplx/*.hpp include/pplx/*.h)
install(FILES ${CASA_HEADERS_PPLX} DESTINATION include/pplx)
file(GLOB CASA_HEADERS_DETAILS include/cpprest/details/*.hpp include/cpprest/details/*.h include/cpprest/details/*.dat)
install(FILES ${CASA_HEADERS_DETAILS} DESTINATION include/cpprest/details)
endif()
elseif(WIN32)
option(BUILD_SHARED_LIBS "Build shared Libraries." ON)
option(BUILD_TESTS "Build tests." ON)
option(BUILD_SAMPLES "Build samples." ON)
option(Boost_USE_STATIC_LIBS ON)
add_definitions(-DUNICODE -D_UNICODE)
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)
set(Casablanca_DEFINITIONS -D_NO_ASYNCRTIMP -D_NO_PPLXIMP CACHE INTERNAL "Definitions for consume casablanca library")
else()
set(Casablanca_DEFINITIONS "" CACHE INTERNAL "Definitions for consume casablanca library")
endif()
add_definitions(${Casablanca_DEFINITIONS} -D_WINSOCK_DEPRECATED_NO_WARNINGS -DWIN32)
find_package(Boost 1.55 REQUIRED COMPONENTS random system thread filesystem chrono atomic)
find_package(OpenSSL 1.0 REQUIRED)
else()
message(FATAL_ERROR "-- Unsupported Build Platform.")
endif()
# Compiler (not platform) specific settings
if(ANDROID)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pedantic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes -Wno-pointer-arith")
include_directories(
"${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/include"
"${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include"
"${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/include/backward"
)
elseif((CMAKE_CXX_COMPILER_ID MATCHES "Clang") OR IOS)
message("-- Setting clang options")
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-reorder")
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")
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")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
message("-- Setting msvc options")
set(WARNINGS)
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)
# These settings can be used by the test targets
set(Casablanca_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(Casablanca_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include ${Boost_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/libs/websocketpp)
set(Casablanca_LIBRARY ${LIB}cpprest)
set(Casablanca_LIBRARIES ${Casablanca_LIBRARY} ${Boost_LIBRARIES} ${Boost_FRAMEWORK})
# Everything in the project needs access to the casablanca include directories
include_directories(${Casablanca_INCLUDE_DIRS})
# 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 (NOT TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT")
target_link_libraries(${NAME}
${LIB}httptest_utilities
${LIB}common_utilities
${LIB}unittestpp
${Casablanca_LIBRARIES}
${ANDROID_STL_FLAGS}
)
if (BUILD_SHARED_LIBS)
add_test(NAME ${NAME}
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
COMMAND test_runner lib${NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}
)
endif()
endif()
endfunction()
add_subdirectory(src)
if(BUILD_TESTS)
add_subdirectory(tests)
endif()
if(BUILD_SAMPLES)
add_subdirectory(samples)
endif()

16
3rdparty/cpprestsdk/dirs.proj vendored Normal file
View file

@ -0,0 +1,16 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), build.root))\Build\Common.Build.settings" />
<ItemGroup>
<ProjectFile Include="src\dirs.proj"/>
</ItemGroup>
<ItemGroup Condition="'$(BuildTests)'!=''">
<ProjectFile Include="tests\dirs.proj" Condition="'$(Platform)'!='ARM' or '$(WindowsSDKDesktopARMSupport)' == 'true'"/>
<ProjectFile Include="samples\dirs.proj" Condition="'$(Platform)'!='ARM' or '$(WindowsSDKDesktopARMSupport)' == 'true'"/>
</ItemGroup>
<Import Project="$(TargetsPath)\Common.Build.Traversal.targets" />
</Project>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,593 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Various common utilities.
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <string>
#include <vector>
#include <cstdint>
#include <system_error>
#include <random>
#include <locale.h>
#include "pplx/pplxtasks.h"
#include "cpprest/details/basic_types.h"
#if !defined(_WIN32) || (_MSC_VER >= 1700)
#include <chrono>
#endif
#ifndef _WIN32
#include <boost/algorithm/string.hpp>
#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269
#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);
}
/// 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>
_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string &&s);
/// <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>
_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string &&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>
_ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string &s);
/// <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>
_ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string &s);
/// <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>
_ASYNCRTIMP utf16string __cdecl to_utf16string(utf16string 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>
_ASYNCRTIMP std::string __cdecl to_utf8string(std::string 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>
utility::string_t print_string(const Source &val, const std::locale &loc)
{
utility::ostringstream_t oss;
oss.imbue(loc);
oss << val;
if (oss.bad())
{
throw std::bad_cast();
}
return oss.str();
}
template <typename Source>
utility::string_t print_string(const Source &val)
{
return print_string(val, std::locale());
}
template <typename Target>
Target scan_string(const utility::string_t &str, const std::locale &loc)
{
Target t;
utility::istringstream_t iss(str);
iss.imbue(loc);
iss >> t;
if (iss.bad())
{
throw std::bad_cast();
}
return t;
}
template <typename Target>
Target scan_string(const utility::string_t &str)
{
return scan_string<Target>(str, std::locale());
}
}
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(char ch)
{
return (ch >= '0' && ch <= '9')
|| (ch >= 'A' && ch <= 'Z')
|| (ch >= 'a' && ch <= 'z');
}
/// <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)));
}
/// <summary>
/// Cross platform utility function for performing case insensitive string comparision.
/// </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>
inline bool str_icmp(const utility::string_t &left, const utility::string_t &right)
{
#ifdef _WIN32
return _wcsicmp(left.c_str(), right.c_str()) == 0;
#else
return boost::iequals(left, right);
#endif
}
#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"; }
_ASYNCRTIMP virtual std::string message(int errorCode) const CPPREST_NOEXCEPT;
_ASYNCRTIMP 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());
}
}
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 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>
/// 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;
}
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);
}
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;
}
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;
#ifdef _WIN32
// void* to avoid pulling in windows.h
static _ASYNCRTIMP bool __cdecl datetime::system_type_to_datetime(/*SYSTEMTIME*/ void* psysTime, uint64_t seconds, datetime * pdt);
#else
static datetime timeval_to_datetime(const timeval &time);
#endif
// 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;
};
#ifndef _WIN32
// temporary workaround for the fact that
// utf16char is not fully supported in GCC
class cmp
{
public:
static int icmp(std::string left, std::string right)
{
size_t i;
for (i = 0; i < left.size(); ++i)
{
if (i == right.size()) return 1;
auto l = cmp::tolower(left[i]);
auto r = cmp::tolower(right[i]);
if (l > r) return 1;
if (l < r) return -1;
}
if (i < right.size()) return -1;
return 0;
}
private:
static char tolower(char c)
{
if (c >= 'A' && c <= 'Z')
return static_cast<char>(c - 'A' + 'a');
return c;
}
};
#endif
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:
static const utility::string_t c_allowed_chars;
std::mt19937 m_random;
int m_length;
};
} // namespace utility;

View file

@ -0,0 +1,450 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Protocol independent support for URIs.
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <functional>
#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/basic_types.h"
namespace web {
namespace details
{
struct uri_components
{
uri_components() : m_path(_XPLATSTR("/")), m_port(-1)
{}
uri_components(const uri_components &other) :
m_scheme(other.m_scheme),
m_host(other.m_host),
m_user_info(other.m_user_info),
m_path(other.m_path),
m_query(other.m_query),
m_fragment(other.m_fragment),
m_port(other.m_port)
{}
uri_components & operator=(const uri_components &other)
{
if (this != &other)
{
m_scheme = other.m_scheme;
m_host = other.m_host;
m_user_info = other.m_user_info;
m_path = other.m_path;
m_query = other.m_query;
m_fragment = other.m_fragment;
m_port = other.m_port;
}
return *this;
}
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)
{}
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;
};
}
/// <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 emtpy 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>
/// <param name="utf8data">The UTF-8 string data.</param>
/// <returns>The encoded string.</returns>
_ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t &utf8data);
/// <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>
/// <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 &other) :
m_uri(other.m_uri),
m_components(other.m_components)
{}
/// <summary>
/// Copy assignment operator.
/// </summary>
uri & operator=(const uri &other)
{
if (this != &other)
{
m_uri = other.m_uri;
m_components = other.m_components;
}
return *this;
}
/// <summary>
/// Move constructor.
/// </summary>
uri(uri &&other) CPPREST_NOEXCEPT :
m_uri(std::move(other.m_uri)),
m_components(std::move(other.m_components))
{}
/// <summary>
/// Move assignment operator
/// </summary>
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 "locahost", 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;
}
_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;
// Encodes all characters not in given set determined by given function.
static utility::string_t encode_impl(const utility::string_t &raw, const std::function<bool(int)>& should_encode);
utility::string_t m_uri;
details::uri_components m_components;
};
} // namespace web

View file

@ -0,0 +1,632 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* 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 http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <vector>
#include <queue>
#include <algorithm>
#include <iterator>
#include "pplx/pplxtasks.h"
#include "cpprest/astreambuf.h"
#include "cpprest/streams.h"
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 = begin(m_data) + m_current_position;
auto readEnd = begin(m_data) + newPos;
#ifdef _WIN32
// 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, 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>());
}
};
}} // namespaces

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,141 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Platform-dependent type definitions
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#include "cpprest/details/cpprest_compat.h"
#include "cpprest/details/basic_types.h"
#ifndef _WIN32
# define __STDC_LIMIT_MACROS
# 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
#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

View file

@ -0,0 +1,103 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* 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 http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#if defined(_WIN32) // Settings specific to Windows
#if _MSC_VER >= 1900
#define CPPREST_NOEXCEPT noexcept
#else
#define CPPREST_NOEXCEPT
#endif
#define CASABLANCA_UNREFERENCED_PARAMETER(x) (x)
#include <sal.h>
#else // End settings specific to Windows
// Settings common to all but Windows
#define __declspec(x) __attribute__ ((x))
#define dllimport
#define novtable /* no novtable equivalent */
#define __assume(x) do { if (!(x)) __builtin_unreachable(); } while (false)
#define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x
#define CPPREST_NOEXCEPT noexcept
#include <assert.h>
#define _ASSERTE(x) assert(x)
// No SAL on non Windows platforms
#include "cpprest/details/nosal.h"
#if defined(__APPLE__) // Settings specific to Apple
#define __cdecl
#else
// Settings specific to Linux and Android
// Ignore cdecl on ANDROID ARM and 64bit
#if defined(__ANDROID__) && defined(__arm__) || defined(__LP64__)
#define __cdecl
#else
#define __cdecl __attribute__ ((cdecl))
#endif
#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.
#define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#endif
#ifdef __clang__
#include <cstdio>
#endif
#endif // defined(__APPLE__)
#endif
#ifdef _NO_ASYNCRTIMP
#define _ASYNCRTIMP
#else
#ifdef _ASYNCRT_EXPORT
#define _ASYNCRTIMP __declspec(dllexport)
#else
#define _ASYNCRTIMP __declspec(dllimport)
#endif
#endif
#ifdef CASABLANCA_DEPRECATION_NO_WARNINGS
#define CASABLANCA_DEPRECATED(x)
#else
#define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x))
#endif

View file

@ -0,0 +1,206 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* 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 http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifdef _WIN32
#include <cstdint>
#endif
#include "pplx/pplxtasks.h"
#include "cpprest/details/basic_types.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() {}
};
}
}}
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);
}

View file

@ -0,0 +1,466 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Client-side APIs, non-public declarations used in the implementation.
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/details/basic_types.h"
#include "cpprest/details/http_helpers.h"
#ifdef _WIN32
# define CRLF _XPLATSTR("\r\n")
#else
# define CRLF std::string("\r\n")
#endif
namespace web { namespace http { namespace client { namespace details
{
#ifdef _WIN32
static const utility::char_t * get_with_body = _XPLATSTR("A GET or HEAD request should not have an entity body.");
// Helper function to trim leading and trailing null characters from a string.
static void trim_nulls(utility::string_t &str)
{
size_t index;
for(index = 0; index < str.size() && str[index] == 0; ++index);
str.erase(0, index);
for(index = str.size(); index > 0 && str[index - 1] == 0; --index);
str.erase(index);
}
#endif
// Flatten the http_headers into a name:value pairs separated by a carriage return and line feed.
static utility::string_t flatten_http_headers(const http_headers &headers)
{
utility::string_t flattened_headers;
for(auto iter = headers.begin(); iter != headers.end(); ++iter)
{
flattened_headers.append(iter->first);
flattened_headers.push_back(':');
flattened_headers.append(iter->second);
flattened_headers.append(CRLF);
}
return flattened_headers;
}
#ifdef _WIN32
/// <summary>
/// Parses a string containing Http headers.
/// </summary>
static void parse_headers_string(_Inout_z_ utf16char *headersStr, http_headers &headers)
{
utf16char *context = nullptr;
utf16char *line = wcstok_s(headersStr, CRLF, &context);
while(line != nullptr)
{
const utility::string_t header_line(line);
const size_t colonIndex = header_line.find_first_of(_XPLATSTR(":"));
if(colonIndex != utility::string_t::npos)
{
utility::string_t key = header_line.substr(0, colonIndex);
utility::string_t value = header_line.substr(colonIndex + 1, header_line.length() - colonIndex - 1);
http::details::trim_whitespace(key);
http::details::trim_whitespace(value);
headers.add(key, value);
}
line = wcstok_s(nullptr, CRLF, &context);
}
}
#endif
class _http_client_communicator;
// Request context encapsulating everything necessary for creating and responding to a request.
class request_context
{
public:
// Destructor to clean up any held resources.
virtual ~request_context()
{
}
void complete_headers()
{
// We have already read (and transmitted) the request body. Should we explicitly close the stream?
// Well, there are test cases that assumes that the istream is valid when t receives the response!
// For now, we will drop our reference which will close the stream if the user doesn't have one.
m_request.set_body(Concurrency::streams::istream());
m_request_completion.set(m_response);
}
/// <summary>
/// Completes this request, setting the underlying task completion event, and cleaning up the handles
/// </summary>
void complete_request(utility::size64_t body_size)
{
m_response._get_impl()->_complete(body_size);
finish();
}
void report_error(unsigned long error_code, const std::string &errorMessage)
{
report_exception(http_exception(static_cast<int>(error_code), errorMessage));
}
#ifdef _WIN32
void report_error(unsigned long error_code, const std::wstring &errorMessage)
{
report_exception(http_exception(static_cast<int>(error_code), errorMessage));
}
#endif
template<typename _ExceptionType>
void report_exception(const _ExceptionType &e)
{
report_exception(std::make_exception_ptr(e));
}
virtual void report_exception(std::exception_ptr exceptionPtr)
{
auto response_impl = m_response._get_impl();
// If cancellation has been triggered then ignore any errors.
if(m_request._cancellation_token().is_canceled())
{
exceptionPtr = std::make_exception_ptr(http_exception((int)std::errc::operation_canceled, std::generic_category()));
}
// First try to complete the headers with an exception.
if(m_request_completion.set_exception(exceptionPtr))
{
// Complete the request with no msg body. The exception
// should only be propagated to one of the tce.
response_impl->_complete(0);
}
else
{
// Complete the request with an exception
response_impl->_complete(0, exceptionPtr);
}
finish();
}
virtual concurrency::streams::streambuf<uint8_t> _get_readbuffer()
{
auto instream = m_request.body();
_ASSERTE((bool)instream);
return instream.streambuf();
}
concurrency::streams::streambuf<uint8_t> _get_writebuffer()
{
auto outstream = m_response._get_impl()->outstream();
_ASSERTE((bool)outstream);
return outstream.streambuf();
}
// Reference to the http_client implementation.
std::shared_ptr<_http_client_communicator> m_http_client;
// request/response pair.
http_request m_request;
http_response m_response;
utility::size64_t m_uploaded;
utility::size64_t m_downloaded;
// task completion event to signal request is completed.
pplx::task_completion_event<http_response> m_request_completion;
// Registration for cancellation notification if enabled.
pplx::cancellation_token_registration m_cancellationRegistration;
protected:
request_context(const std::shared_ptr<_http_client_communicator> &client, const http_request &request)
: m_http_client(client),
m_request(request),
m_uploaded(0),
m_downloaded(0)
{
auto responseImpl = m_response._get_impl();
// Copy the user specified output stream over to the response
responseImpl->set_outstream(request._get_impl()->_response_stream(), false);
// Prepare for receiving data from the network. Ideally, this should be done after
// we receive the headers and determine that there is a response body. We will do it here
// since it is not immediately apparent where that would be in the callback handler
responseImpl->_prepare_to_receive_data();
}
virtual void finish();
};
//
// Interface used by client implementations. Concrete implementations are responsible for
// sending HTTP requests and receiving the responses.
//
class _http_client_communicator
{
public:
// Destructor to clean up any held resources.
virtual ~_http_client_communicator() {}
// Asynchronously send a HTTP request and process the response.
void async_send_request(const std::shared_ptr<request_context> &request)
{
if(m_client_config.guarantee_order())
{
// Send to call block to be processed.
push_request(request);
}
else
{
// Schedule a task to start sending.
pplx::create_task([this, request]
{
open_and_send_request(request);
});
}
}
void finish_request()
{
// If guarantee order is specified we don't need to do anything.
if(m_client_config.guarantee_order())
{
pplx::extensibility::scoped_critical_section_t l(m_open_lock);
--m_scheduled;
if( !m_requests_queue.empty())
{
auto request = m_requests_queue.front();
m_requests_queue.pop();
// Schedule a task to start sending.
pplx::create_task([this, request]
{
open_and_send_request(request);
});
}
}
}
const http_client_config& client_config() const
{
return m_client_config;
}
const uri & base_uri() const
{
return m_uri;
}
protected:
_http_client_communicator(http::uri address, http_client_config client_config)
: m_uri(std::move(address)), m_client_config(std::move(client_config)), m_opened(false), m_scheduled(0)
{
}
// Method to open client.
virtual unsigned long open() = 0;
// HTTP client implementations must implement send_request.
virtual void send_request(_In_ const std::shared_ptr<request_context> &request) = 0;
// URI to connect to.
const http::uri m_uri;
private:
http_client_config m_client_config;
bool m_opened;
pplx::extensibility::critical_section_t m_open_lock;
// Wraps opening the client around sending a request.
void open_and_send_request(const std::shared_ptr<request_context> &request)
{
// First see if client needs to be opened.
auto error = open_if_required();
if (error != 0)
{
// Failed to open
request->report_error(error, _XPLATSTR("Open failed"));
// DO NOT TOUCH the this pointer after completing the request
// This object could be freed along with the request as it could
// be the last reference to this object
return;
}
send_request(request);
}
unsigned long open_if_required()
{
unsigned long error = 0;
if(!m_opened)
{
pplx::extensibility::scoped_critical_section_t l(m_open_lock);
// Check again with the lock held
if (!m_opened)
{
error = open();
if (error == 0)
{
m_opened = true;
}
}
}
return error;
}
void push_request(const std::shared_ptr<request_context> &request)
{
pplx::extensibility::scoped_critical_section_t l(m_open_lock);
if(++m_scheduled == 1)
{
// Schedule a task to start sending.
pplx::create_task([this, request]()
{
open_and_send_request(request);
});
}
else
{
m_requests_queue.push(request);
}
}
// Queue used to guarantee ordering of requests, when applicable.
std::queue<std::shared_ptr<request_context>> m_requests_queue;
int m_scheduled;
};
inline void request_context::finish()
{
// If cancellation is enabled and registration was performed, unregister.
if(m_cancellationRegistration != pplx::cancellation_token_registration())
{
_ASSERTE(m_request._cancellation_token() != pplx::cancellation_token::none());
m_request._cancellation_token().deregister_callback(m_cancellationRegistration);
}
m_http_client->finish_request();
}
class http_network_handler : public http_pipeline_stage
{
public:
http_network_handler(const uri &base_uri, const http_client_config &client_config);
virtual pplx::task<http_response> propagate(http_request request);
const std::shared_ptr<details::_http_client_communicator>& http_client_impl() const
{
return m_http_client_impl;
}
private:
std::shared_ptr<_http_client_communicator> m_http_client_impl;
};
// Helper function to check to make sure the uri is valid.
void verify_uri(const uri &uri)
{
// Some things like proper URI schema are verified by the URI class.
// We only need to check certain things specific to HTTP.
if (uri.scheme() != _XPLATSTR("http") && uri.scheme() != _XPLATSTR("https"))
{
throw std::invalid_argument("URI scheme must be 'http' or 'https'");
}
if(uri.host().empty())
{
throw std::invalid_argument("URI must contain a hostname.");
}
}
} // namespace details
http_client::http_client(const uri &base_uri)
{
build_pipeline(base_uri, http_client_config());
}
http_client::http_client(const uri &base_uri, const http_client_config &client_config)
{
build_pipeline(base_uri, client_config);
}
void http_client::build_pipeline(const uri &base_uri, const http_client_config &client_config)
{
if (base_uri.scheme().empty())
{
auto uribuilder = uri_builder(base_uri);
uribuilder.set_scheme(_XPLATSTR("http"));
uri uriWithScheme = uribuilder.to_uri();
details::verify_uri(uriWithScheme);
m_pipeline = ::web::http::http_pipeline::create_pipeline(std::make_shared<details::http_network_handler>(uriWithScheme, client_config));
}
else
{
details::verify_uri(base_uri);
m_pipeline = ::web::http::http_pipeline::create_pipeline(std::make_shared<details::http_network_handler>(base_uri, client_config));
}
#if !defined(CPPREST_TARGET_XP) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP || _MSC_VER > 1700)
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())));
}
const http_client_config & http_client::client_config() const
{
auto ph = std::static_pointer_cast<details::http_network_handler>(m_pipeline->last_stage());
return ph->http_client_impl()->client_config();
}
const uri & http_client::base_uri() const
{
auto ph = std::static_pointer_cast<details::http_network_handler>(m_pipeline->last_stage());
return ph->http_client_impl()->base_uri();
}
}}} // namespaces

View file

@ -0,0 +1,179 @@
#ifdef _METHODS
DAT(GET, _XPLATSTR("GET"))
DAT(POST, _XPLATSTR("POST"))
DAT(PUT, _XPLATSTR("PUT"))
DAT(DEL, _XPLATSTR("DELETE"))
DAT(HEAD, _XPLATSTR("HEAD"))
DAT(OPTIONS, _XPLATSTR("OPTIONS"))
DAT(TRCE, _XPLATSTR("TRACE"))
DAT(CONNECT, _XPLATSTR("CONNECT"))
DAT(MERGE, _XPLATSTR("MERGE"))
DAT(PATCH, _XPLATSTR("PATCH"))
#endif
#ifdef _PHRASES
DAT(Continue, 100, _XPLATSTR("Continue"))
DAT(SwitchingProtocols, 101, _XPLATSTR("Switching Protocols"))
DAT(OK, 200, _XPLATSTR("OK"))
DAT(Created, 201, _XPLATSTR("Created"))
DAT(Accepted, 202, _XPLATSTR("Accepted"))
DAT(NonAuthInfo, 203, _XPLATSTR("Non-Authoritative Information"))
DAT(NoContent, 204, _XPLATSTR("No Content"))
DAT(ResetContent, 205, _XPLATSTR("Reset Content"))
DAT(PartialContent, 206, _XPLATSTR("Partial Content"))
DAT(MultipleChoices, 300, _XPLATSTR("Multiple Choices"))
DAT(MovedPermanently, 301, _XPLATSTR("Moved Permanently"))
DAT(Found, 302, _XPLATSTR("Found"))
DAT(SeeOther, 303, _XPLATSTR("See Other"))
DAT(NotModified, 304, _XPLATSTR("Not Modified"))
DAT(UseProxy, 305, _XPLATSTR("Use Proxy"))
DAT(TemporaryRedirect, 307, _XPLATSTR("Temporary Redirect"))
DAT(BadRequest, 400, _XPLATSTR("Bad Request"))
DAT(Unauthorized, 401, _XPLATSTR("Unauthorized"))
DAT(PaymentRequired, 402, _XPLATSTR("Payment Required"))
DAT(Forbidden, 403, _XPLATSTR("Forbidden"))
DAT(NotFound, 404, _XPLATSTR("Not Found"))
DAT(MethodNotAllowed, 405, _XPLATSTR("Method Not Allowed"))
DAT(NotAcceptable, 406, _XPLATSTR("Not Acceptable"))
DAT(ProxyAuthRequired, 407, _XPLATSTR("Proxy Authentication Required"))
DAT(RequestTimeout, 408, _XPLATSTR("Request Time-out"))
DAT(Conflict, 409, _XPLATSTR("Conflict"))
DAT(Gone, 410, _XPLATSTR("Gone"))
DAT(LengthRequired, 411, _XPLATSTR("Length Required"))
DAT(PreconditionFailed, 412, _XPLATSTR("Precondition Failed"))
DAT(RequestEntityTooLarge, 413, _XPLATSTR("Request Entity Too Large"))
DAT(RequestUriTooLarge, 414, _XPLATSTR("Request Uri Too Large"))
DAT(UnsupportedMediaType, 415, _XPLATSTR("Unsupported Media Type"))
DAT(RangeNotSatisfiable, 416, _XPLATSTR("Requested range not satisfiable"))
DAT(ExpectationFailed, 417, _XPLATSTR("Expectation Failed"))
DAT(InternalError, 500, _XPLATSTR("Internal Error"))
DAT(NotImplemented, 501, _XPLATSTR("Not Implemented"))
DAT(BadGateway, 502, _XPLATSTR("Bad Gateway"))
DAT(ServiceUnavailable, 503, _XPLATSTR("Service Unavailable"))
DAT(GatewayTimeout, 504, _XPLATSTR("Gateway Time-out"))
DAT(HttpVersionNotSupported, 505, _XPLATSTR("HTTP Version not supported"))
#endif // _PHRASES
#ifdef _HEADER_NAMES
DAT(accept, "Accept")
DAT(accept_charset, "Accept-Charset")
DAT(accept_encoding, "Accept-Encoding")
DAT(accept_language, "Accept-Language")
DAT(accept_ranges, "Accept-Ranges")
DAT(age, "Age")
DAT(allow, "Allow")
DAT(authorization, "Authorization")
DAT(cache_control, "Cache-Control")
DAT(connection, "Connection")
DAT(content_encoding, "Content-Encoding")
DAT(content_language, "Content-Language")
DAT(content_length, "Content-Length")
DAT(content_location, "Content-Location")
DAT(content_md5, "Content-MD5")
DAT(content_range, "Content-Range")
DAT(content_type, "Content-Type")
DAT(content_disposition, "Content-Disposition")
DAT(date, "Date")
DAT(etag, "ETag")
DAT(expect, "Expect")
DAT(expires, "Expires")
DAT(from, "From")
DAT(host, "Host")
DAT(if_match, "If-Match")
DAT(if_modified_since, "If-Modified-Since")
DAT(if_none_match, "If-None-Match")
DAT(if_range, "If-Range")
DAT(if_unmodified_since, "If-Unmodified-Since")
DAT(last_modified, "Last-Modified")
DAT(location, "Location")
DAT(max_forwards, "Max-Forwards")
DAT(pragma, "Pragma")
DAT(proxy_authenticate, "Proxy-Authenticate")
DAT(proxy_authorization, "Proxy-Authorization")
DAT(range, "Range")
DAT(referer, "Referer")
DAT(retry_after, "Retry-After")
DAT(server, "Server")
DAT(te, "TE")
DAT(trailer, "Trailer")
DAT(transfer_encoding, "Transfer-Encoding")
DAT(upgrade, "Upgrade")
DAT(user_agent, "User-Agent")
DAT(vary, "Vary")
DAT(via, "Via")
DAT(warning, "Warning")
DAT(www_authenticate, "WWW-Authenticate")
#endif // _HEADER_NAMES
#ifdef _MIME_TYPES
DAT(application_atom_xml, "application/atom+xml")
DAT(application_http, "application/http")
DAT(application_javascript, "application/javascript")
DAT(application_json, "application/json")
DAT(application_xjson, "application/x-json")
DAT(application_octetstream, "application/octet-stream")
DAT(application_x_www_form_urlencoded, "application/x-www-form-urlencoded")
DAT(multipart_form_data, "multipart/form-data")
DAT(boundary, "boundary")
DAT(form_data, "form-data")
DAT(application_xjavascript, "application/x-javascript")
DAT(application_xml, "application/xml")
DAT(message_http, "message/http")
DAT(text, "text")
DAT(text_javascript, "text/javascript")
DAT(text_json, "text/json")
DAT(text_plain, "text/plain")
DAT(text_plain_utf16, "text/plain; charset=utf-16")
DAT(text_plain_utf16le, "text/plain; charset=utf-16le")
DAT(text_plain_utf8, "text/plain; charset=utf-8")
DAT(text_xjavascript, "text/x-javascript")
DAT(text_xjson, "text/x-json")
#endif // _MIME_TYPES
#ifdef _CHARSET_TYPES
DAT(ascii, "ascii")
DAT(usascii, "us-ascii")
DAT(latin1, "iso-8859-1")
DAT(utf8, "utf-8")
DAT(utf16, "utf-16")
DAT(utf16le, "utf-16le")
DAT(utf16be, "utf-16be")
#endif // _CHARSET_TYPES
#ifdef _OAUTH1_METHODS
DAT(hmac_sha1, _XPLATSTR("HMAC-SHA1"))
DAT(plaintext, _XPLATSTR("PLAINTEXT"))
#endif // _OAUTH1_METHODS
#ifdef _OAUTH1_STRINGS
DAT(callback, "oauth_callback")
DAT(callback_confirmed, "oauth_callback_confirmed")
DAT(consumer_key, "oauth_consumer_key")
DAT(nonce, "oauth_nonce")
DAT(realm, "realm") // NOTE: No "oauth_" prefix.
DAT(signature, "oauth_signature")
DAT(signature_method, "oauth_signature_method")
DAT(timestamp, "oauth_timestamp")
DAT(token, "oauth_token")
DAT(token_secret, "oauth_token_secret")
DAT(verifier, "oauth_verifier")
DAT(version, "oauth_version")
#endif // _OAUTH1_STRINGS
#ifdef _OAUTH2_STRINGS
DAT(access_token, "access_token")
DAT(authorization_code, "authorization_code")
DAT(bearer, "bearer")
DAT(client_id, "client_id")
DAT(client_secret, "client_secret")
DAT(code, "code")
DAT(expires_in, "expires_in")
DAT(grant_type, "grant_type")
DAT(redirect_uri, "redirect_uri")
DAT(refresh_token, "refresh_token")
DAT(response_type, "response_type")
DAT(scope, "scope")
DAT(state, "state")
DAT(token, "token")
DAT(token_type, "token_type")
#endif // _OAUTH2_STRINGS

View file

@ -0,0 +1,122 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* 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 http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/http_msg.h"
namespace web { namespace http
{
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
};
/// <summary>
/// Determines whether or not the given content type is 'textual' according the feature specifications.
/// </summary>
bool is_content_type_textual(const utility::string_t &content_type);
/// <summary>
/// Determines whether or not the given content type is JSON according the feature specifications.
/// </summary>
bool is_content_type_json(const utility::string_t &content_type);
/// <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>
void parse_content_type_and_charset(const utility::string_t &content_type, utility::string_t &content, utility::string_t &charset);
/// <summary>
/// Gets the default charset for given content type. If the MIME type is not textual or recognized Latin1 will be returned.
/// </summary>
utility::string_t get_default_charset(const utility::string_t &content_type);
/// <summary>
/// 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).
/// </summary>
utility::string_t convert_utf16_to_string_t(utf16string src);
utf16string convert_utf16_to_utf16(utf16string src);
std::string convert_utf16_to_utf8(utf16string src);
utility::string_t convert_utf16le_to_string_t(utf16string src, bool erase_bom);
std::string convert_utf16le_to_utf8(utf16string src, bool erase_bom);
utility::string_t convert_utf16be_to_string_t(utf16string src, bool erase_bom);
std::string convert_utf16be_to_utf8(utf16string src, bool erase_bom);
utf16string convert_utf16be_to_utf16le(utf16string src, bool erase_bom);
// simple helper functions to trim whitespace.
_ASYNCRTIMP void __cdecl ltrim_whitespace(utility::string_t &str);
_ASYNCRTIMP void __cdecl rtrim_whitespace(utility::string_t &str);
_ASYNCRTIMP void __cdecl trim_whitespace(utility::string_t &str);
bool validate_method(const utility::string_t& method);
namespace chunked_encoding
{
// Transfer-Encoding: chunked support
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);
}
}}}

View file

@ -0,0 +1,81 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: 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 web::http

View file

@ -0,0 +1,101 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: 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 <memory>
#include "cpprest/http_listener.h"
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();
};
}}}} // namespaces

View file

@ -0,0 +1,202 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#endif
#include <boost/asio.hpp>
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#include <boost/bind.hpp>
#include <set>
#include "pplx/threadpool.h"
#include "cpprest/details/http_server.h"
namespace web
{
namespace http
{
namespace experimental
{
namespace listener
{
class http_linux_server;
namespace details
{
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&);
linux_request_context& operator=(const linux_request_context&);
};
class hostport_listener;
class connection
{
private:
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;
http_request m_request;
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
public:
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_refs(1)
{
start_request_response();
}
connection(const connection&) = delete;
connection& operator=(const connection&) = delete;
void close();
private:
void start_request_response();
void handle_http_line(const boost::system::error_code& ec);
void handle_headers();
void handle_body(const boost::system::error_code& ec);
void handle_chunked_header(const boost::system::error_code& ec);
void handle_chunked_body(const boost::system::error_code& ec, int toWrite);
void dispatch_request_to_listener();
void do_response(bool bad_request);
template <typename ReadHandler>
void async_read_until_buffersize(size_t size, const ReadHandler &handler);
void async_process_response(http_response response);
void cancel_sending_response_with_error(const http_response &response, const std::exception_ptr &);
void handle_headers_written(const http_response &response, const boost::system::error_code& ec);
void handle_write_large_response(const http_response &response, const boost::system::error_code& ec);
void handle_write_chunked_response(const http_response &response, const boost::system::error_code& ec);
void handle_response_written(const http_response &response, const boost::system::error_code& ec);
void finish_request_response();
};
class hostport_listener
{
private:
friend class connection;
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_acceptor;
std::map<std::string, web::http::experimental::listener::details::http_listener_impl* > m_listeners;
pplx::extensibility::reader_writer_lock_t m_listeners_lock;
pplx::extensibility::recursive_lock_t m_connections_lock;
pplx::extensibility::event_t m_all_connections_complete;
std::set<connection*> m_connections;
http_linux_server* m_p_server;
std::string m_host;
std::string m_port;
public:
hostport_listener(http_linux_server* server, const std::string& hostport)
: m_acceptor()
, m_listeners()
, m_listeners_lock()
, m_connections_lock()
, m_connections()
, m_p_server(server)
{
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, web::http::experimental::listener::details::http_listener_impl* listener);
void remove_listener(const std::string& path, web::http::experimental::listener::details::http_listener_impl* listener);
private:
void on_accept(boost::asio::ip::tcp::socket* socket, const boost::system::error_code& ec);
};
}
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 http::experimental::listener::details::connection;
pplx::extensibility::reader_writer_lock_t m_listeners_lock;
std::map<std::string, std::unique_ptr<details::hostport_listener>, iequal_to> m_listeners;
std::unordered_map<details::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(web::http::experimental::listener::details::http_listener_impl* listener);
virtual pplx::task<void> unregister_listener(web::http::experimental::listener::details::http_listener_impl* listener);
pplx::task<void> respond(http::http_response response);
};
}}}}

View file

@ -0,0 +1,250 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: 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 <atomic>
#include <mutex>
#include "cpprest/details/http_server.h"
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)
{
CASABLANCA_UNREFERENCED_PARAMETER(io);
CASABLANCA_UNREFERENCED_PARAMETER(context);
CASABLANCA_UNREFERENCED_PARAMETER(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);
// 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;
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 web::http

View file

@ -0,0 +1,89 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
***/
#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)

View file

@ -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

View file

@ -0,0 +1,215 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* URI parsing implementation
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <string>
namespace web { namespace details
{
namespace uri_parser
{
/// <summary>
/// Parses the uri, attempting to determine its validity.
///
/// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query')
/// </summary>
bool validate(const utility::string_t &encoded_string);
/// <summary>
/// Parses the uri, setting each provided string to the value of that component. Components
/// that are not part of the provided text are set to the empty string. Component strings
/// DO NOT contain their beginning or ending delimiters.
///
/// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query')
/// </summary>
bool parse(const utility::string_t &encoded_string, uri_components &components);
/// <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 host portion include:
/// - Any unreserved character
/// - The percent character ('%'), and thus any percent-endcoded octet
/// - The sub-delimiters
/// - ':' (colon)
/// - '[' (open bracket)
/// - ']' (close bracket)
/// </summary>
inline bool is_host_character(int c)
{
return is_unreserved(c) || is_sub_delim(c) || c == '%' || 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)
///
/// Note that we don't currently support:
/// - IPv6 addresses (requires '[]')
/// </summary>
inline bool is_authority_character(int c)
{
return is_unreserved(c) || is_sub_delim(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)
/// - '@' (ampersand)
/// </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);
}
/// <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 inner_parse(
const utility::char_t *encoded,
const utility::char_t **scheme_begin, const utility::char_t **scheme_end,
const utility::char_t **uinfo_begin, const utility::char_t **uinfo_end,
const utility::char_t **host_begin, const utility::char_t **host_end,
_Out_ int *port,
const utility::char_t **path_begin, const utility::char_t **path_end,
const utility::char_t **query_begin, const utility::char_t **query_end,
const utility::char_t **fragment_begin, const utility::char_t **fragment_end);
}
}}

View file

@ -0,0 +1,248 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* utility classes used by the different web:: clients
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/asyncrt_utils.h"
namespace web
{
namespace http { namespace client { namespace details {
class winhttp_client;
class winrt_client;
}}}
namespace websockets { namespace client { namespace details {
class winrt_callback_client;
class wspp_callback_client;
}}}
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;
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
#if defined(__cplusplus_winrt)
#if !(WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP && _MSC_VER < 1800)
class winrt_encryption
{
public:
winrt_encryption() {}
_ASYNCRTIMP winrt_encryption(const std::wstring &data);
_ASYNCRTIMP plaintext_string decrypt() const;
private:
::pplx::task<Windows::Storage::Streams::IBuffer ^> m_buffer;
};
#endif
#else
class win32_encryption
{
public:
win32_encryption() {}
_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
#endif
}
/// <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) && !defined(CPPREST_TARGET_XP)
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP && _MSC_VER < 1800
return m_password;
#else
return utility::string_t(*m_password.decrypt());
#endif
#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(); }
private:
friend class http::client::details::winhttp_client;
friend class http::client::details::winrt_client;
friend class websockets::client::details::winrt_callback_client;
friend class websockets::client::details::wspp_callback_client;
details::plaintext_string decrypt() const
{
// Encryption APIs not supported on Windows Phone 8.0 or XP
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP && _MSC_VER < 1800
return details::plaintext_string(new ::utility::string_t(m_password));
#else
return m_password.decrypt();
#endif
#else
return details::plaintext_string(new ::utility::string_t(m_password));
#endif
}
::utility::string_t m_username;
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
#if defined(__cplusplus_winrt)
#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP && _MSC_VER < 1800
::utility::string_t m_password;
#else
details::winrt_encryption m_password;
#endif
#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(_XPLATSTR("")), 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(_XPLATSTR("")), 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;
};
}

View file

@ -0,0 +1,57 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Contains utility functions for helping to verify server certificates in OS X/iOS and Android.
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <vector>
#include <string>
#if defined(__APPLE__) || (defined(ANDROID) || defined(__ANDROID__)) || (defined(_WIN32) && !defined(__cplusplus_winrt) && !defined(_M_ARM) && !defined(CPPREST_EXCLUDE_WEBSOCKETS))
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4005)
#endif
#include <boost/asio/ssl.hpp>
#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 iOS, Android, and OS X.
/// </summary>
/// <param name="verifyCtx">Boost.ASIO context 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);
bool verify_X509_cert_chain(const std::vector<std::string> &certChain, const std::string &hostName);
}}}}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,670 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Client-side APIs.
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#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 <wrl.h>
#include <msxml6.h>
namespace web { namespace http{namespace client{
typedef IXMLHTTPRequest2* native_handle;}}}
#else
namespace web { namespace http{namespace client{
typedef void* native_handle;}}}
#endif // __cplusplus_winrt
#include <memory>
#include <limits>
#include "pplx/pplxtasks.h"
#include "cpprest/http_msg.h"
#include "cpprest/json.h"
#include "cpprest/uri.h"
#include "cpprest/details/web_utilities.h"
#include "cpprest/details/basic_types.h"
#include "cpprest/asyncrt_utils.h"
#if !defined(CPPREST_TARGET_XP) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP || _MSC_VER > 1700)
#include "cpprest/oauth1.h"
#endif
#include "cpprest/oauth2.h"
/// 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(utility::seconds(30)),
m_chunksize(0)
#if !defined(__cplusplus_winrt)
, m_validate_certificates(true)
#endif
, m_set_user_nativehandle_options([](native_handle)->void{})
#if defined(_WIN32) && !defined(__cplusplus_winrt)
, m_buffer_request(false)
#endif
{
}
#if !defined(CPPREST_TARGET_XP) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP || _MSC_VER > 1700)
/// <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 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(const utility::seconds &timeout)
{
m_timeout = 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;
}
#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
#ifdef _WIN32
#if !defined(__cplusplus_winrt)
/// <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
#endif
/// <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
{
m_set_user_nativehandle_options(handle);
}
private:
#if !defined(CPPREST_TARGET_XP) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP || _MSC_VER > 1700)
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;
utility::seconds m_timeout;
size_t m_chunksize;
#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;
#if defined(_WIN32) && !defined(__cplusplus_winrt)
bool m_buffer_request;
#endif
};
/// <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>
~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>
void add_handler(const std::function<pplx::task<http_response>(http_request, std::shared_ptr<http::http_pipeline_stage>)> &handler)
{
m_pipeline->append(std::make_shared<::web::http::details::function_pipeline_wrapper>(handler));
}
/// <summary>
/// Adds an HTTP pipeline stage to the client.
/// </summary>
/// <param name="stage">A shared pointer to a pipeline stage.</param>
void add_handler(const std::shared_ptr<http::http_pipeline_stage> &stage)
{
m_pipeline->append(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:
void build_pipeline(const uri &base_uri, const http_client_config &client_config);
std::shared_ptr<::web::http::http_pipeline> m_pipeline;
};
}}}
#endif

View file

@ -0,0 +1,331 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <system_error>
#include "cpprest/asyncrt_utils.h"
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;
}
/// <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
{
#ifdef _WIN32
return _wcsicmp(str1.c_str(), str2.c_str()) < 0;
#else
return utility::cmp::icmp(str1, str2) < 0;
#endif
}
};
/// <summary>
/// STL-style typedefs
/// </summary>
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::key_type key_type;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::key_compare key_compare;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::allocator_type allocator_type;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::size_type size_type;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::difference_type difference_type;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::pointer pointer;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::const_pointer const_pointer;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::reference reference;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::const_reference const_reference;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::iterator iterator;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::const_iterator const_iterator;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::reverse_iterator reverse_iterator;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::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)
{
if (has(name))
{
m_headers[name] = m_headers[name].append(_XPLATSTR(", ") + utility::conversions::print_string(value));
}
else
{
m_headers[name] = utility::conversions::print_string(value);
}
}
/// <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())
{
// Check to see if doesn't have a value.
if(iter->second.empty())
{
bind_impl(iter->second, value);
return true;
}
return bind_impl(iter->second, value);
}
else
{
return false;
}
}
/// <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:
template<typename _t>
bool bind_impl(const key_type &text, _t &ref) const
{
utility::istringstream_t iss(text);
iss.imbue(std::locale::classic());
iss >> ref;
if (iss.fail() || !iss.eof())
{
return false;
}
return true;
}
bool bind_impl(const key_type &text, ::utility::string_t &ref) const
{
ref = text;
return true;
}
// Headers are stored in a map with case insensitive key.
std::map<utility::string_t, utility::string_t, _case_insensitive_cmp> m_headers;
};
}}

View file

@ -0,0 +1,314 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: HTTP listener (server-side) APIs
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef _CASA_HTTP_LISTENER_H
#define _CASA_HTTP_LISTENER_H
#include <limits>
#include <functional>
#include "cpprest/http_msg.h"
#if !defined(_WIN32) || (_WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt))
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))
{}
/// <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)
{}
/// <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))
{}
/// <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;
}
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);
}
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);
}
private:
utility::seconds m_timeout;
};
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>
_ASYNCRTIMP ~http_listener();
/// <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;
};
}}}}
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,536 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* 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 http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "pplx/pplxtasks.h"
#include "cpprest/astreambuf.h"
#include "cpprest/streams.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
}} // namespaces
#if defined(_WIN32)
#pragma warning(pop)
#endif

1936
3rdparty/cpprestsdk/include/cpprest/json.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,577 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Oauth 1.0
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef _CASA_OAUTH1_H
#define _CASA_OAUTH1_H
#include "cpprest/http_msg.h"
namespace web
{
namespace http
{
namespace client
{
// Forward declaration to avoid circular include dependency.
class http_client_config;
}
/// 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 web::http::oauth1::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 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;
oauth1_token() {}
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>
const oauth1_token& 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
{
static const oauth1_token empty_token;
return empty_token;
}
}
/// <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(); }
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::print_string(utility::datetime::utc_timestamp(), std::locale::classic());
}
_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;
utility::nonce_generator m_nonce_generator;
bool m_is_authorization_completed;
};
} // namespace web::http::oauth1::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;
};
}}}}
#endif

View file

@ -0,0 +1,523 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Oauth 2.0
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef _CASA_OAUTH2_H
#define _CASA_OAUTH2_H
#include "cpprest/http_msg.h"
namespace web
{
namespace http
{
namespace client
{
// Forward declaration to avoid circular include dependency.
class http_client_config;
}
/// 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 web::http::oauth2::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()) :
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_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); }
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;
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 web::http::oauth2::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;
};
}}}}
#endif

View file

@ -0,0 +1,709 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* 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 http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef _CASA_PRODUCER_CONSUMER_STREAMS_H
#define _CASA_PRODUCER_CONSUMER_STREAMS_H
#include <vector>
#include <queue>
#include <algorithm>
#include <iterator>
#include "pplx/pplxtasks.h"
#include "cpprest/astreambuf.h"
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_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;
#ifdef _WIN32
// 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;
#ifdef _WIN32
// 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))
{
}
};
}} // namespaces
#endif

View file

@ -0,0 +1,642 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* 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 http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef _CASA_RAWPTR_STREAMS_H
#define _CASA_RAWPTR_STREAMS_H
#include <vector>
#include <queue>
#include <algorithm>
#include <iterator>
#include "pplx/pplxtasks.h"
#include "cpprest/astreambuf.h"
#include "cpprest/streams.h"
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;
#ifdef _WIN32
// 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
#ifdef _WIN32
// 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));
}
};
}} // namespaces
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,35 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Protocol independent support for URIs.
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef _CASA_URI_H
#define _CASA_URI_H
#include "cpprest/base_uri.h"
#include "cpprest/uri_builder.h"
#endif

View file

@ -0,0 +1,266 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Builder style class for creating URIs.
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <sstream>
#include <string>
#include <vector>
#include "cpprest/base_uri.h"
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() {}
/// <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)
{
m_uri.m_user_info = do_encoding ? uri::encode_uri(user_info, uri::components::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)
{
m_uri.m_host = do_encoding ? uri::encode_uri(host, uri::components::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>
uri_builder & set_port(const utility::string_t &port)
{
utility::istringstream_t portStream(port);
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;
}
/// <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)
{
m_uri.m_path = do_encoding ? uri::encode_uri(path, uri::components::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)
{
m_uri.m_query = do_encoding ? uri::encode_uri(query, uri::components::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)
{
m_uri.m_fragment = do_encoding ? uri::encode_uri(fragment, uri::components::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 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(utility::string_t name, const T &value, bool do_encoding = true)
{
utility::ostringstream_t ss;
ss.imbue(std::locale::classic());
ss << name << _XPLATSTR("=") << value;
return append_query(ss.str(), do_encoding);
}
/// <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();
/// <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();
/// <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:
details::uri_components m_uri;
};
} // namespace web

View file

@ -0,0 +1,22 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
*/
#define CPPREST_VERSION_REVISION 0
#define CPPREST_VERSION_MINOR 6
#define CPPREST_VERSION_MAJOR 2
#define CPPREST_VERSION (CPPREST_VERSION_MAJOR*100000+CPPREST_VERSION_MINOR*100+CPPREST_VERSION_REVISION)

View file

@ -0,0 +1,627 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* 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 <memory>
#include <limits>
#include <condition_variable>
#include <mutex>
#include "pplx/pplxtasks.h"
#include "cpprest/uri.h"
#include "cpprest/details/web_utilities.h"
#include "cpprest/http_headers.h"
#include "cpprest/asyncrt_utils.h"
#include "cpprest/ws_msg.h"
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) {}
/// <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 usefor TLS Server Name Indication (SNI).
/// </summary>
/// <returns>Host name as a string.</returns>
const utf8string & server_name() const
{
return m_sni_hostname;
}
/// <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;
private:
web::web_proxy m_proxy;
web::credentials m_credentials;
web::http::http_headers m_headers;
bool m_sni_enabled;
utf8string m_sni_hostname;
};
/// <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 = _XPLATSTR("")) = 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;
};
}
/// <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=_XPLATSTR(""))
{
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 = _XPLATSTR(""))
{
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;
};
}}}
#endif
#endif

View file

@ -0,0 +1,242 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Websocket incoming and outgoing message definitions.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/details/basic_types.h"
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
#include <memory>
#include <limits>
#include "pplx/pplxtasks.h"
#include "cpprest/streams.h"
#include "cpprest/containerstream.h"
#include "cpprest/uri.h"
#include "cpprest/asyncrt_utils.h"
namespace web
{
namespace websockets
{
namespace client
{
namespace details
{
class winrt_callback_client;
class wspp_callback_client;
#if defined(__cplusplus_winrt)
ref class ReceiveContext;
#endif
}
/// <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:
/// <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; }
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;
};
}}}
#endif

237
3rdparty/cpprestsdk/include/pplx/pplx.h vendored Normal file
View file

@ -0,0 +1,237 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Parallel Patterns Library
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,84 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Utilities to convert between PPL tasks and PPLX tasks
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#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 <ppltasks.h>
#include "pplx/pplxtasks.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;
}
}
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

View file

@ -0,0 +1,259 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* PPL interfaces
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#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
#if (defined(ANDROID) || defined(__ANDROID__))
// This prevents users from requiring -Wno-attributes when using gcc-4.8 with the android NDK.
#define _pplx_cdecl
#else
#define _pplx_cdecl __cdecl
#endif
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 pplx
#endif // _PPLXINTERFACE_H

View file

@ -0,0 +1,329 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Linux specific pplx implementations
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#if (defined(_MSC_VER))
#error This file must not be included for Visual Studio
#endif
#ifndef _WIN32
#include <signal.h>
#include "pthread.h"
#include "cpprest/details/cpprest_compat.h"
#if defined(__APPLE__)
#include <dispatch/dispatch.h>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#else
#include <mutex>
#include <condition_variable>
#endif
#include "pplx/pplxinterface.h"
namespace pplx
{
#if defined(__APPLE__)
namespace cpprest_synchronization = ::boost;
#else
namespace cpprest_synchronization = ::std;
#endif
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>
/// Caputeres the callstack
/// </summary>
__declspec(noinline) inline static size_t CaptureCallstack(void **, size_t, size_t)
{
return 0;
}
}
/// <summary>
/// Manual reset event
/// </summary>
class event_impl
{
private:
cpprest_synchronization::mutex _lock;
cpprest_synchronization::condition_variable _condition;
bool _signaled;
public:
static const unsigned int timeout_infinite = 0xFFFFFFFF;
event_impl()
: _signaled(false)
{
}
void set()
{
cpprest_synchronization::lock_guard<cpprest_synchronization::mutex> lock(_lock);
_signaled = true;
_condition.notify_all();
}
void reset()
{
cpprest_synchronization::lock_guard<cpprest_synchronization::mutex> lock(_lock);
_signaled = false;
}
unsigned int wait(unsigned int timeout)
{
cpprest_synchronization::unique_lock<cpprest_synchronization::mutex> lock(_lock);
if (timeout == event_impl::timeout_infinite)
{
_condition.wait(lock, [this]() -> bool { return _signaled; });
return 0;
}
else
{
cpprest_synchronization::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:
cpprest_synchronization::mutex _M_cs;
volatile 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);
};
} // namespace details
/// <summary>
/// A generic RAII wrapper for locks that implements the critical_section interface
/// cpprest_synchronization::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 cpprest_synchronization::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;
}
/// <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
}
//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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,297 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Windows specific pplx implementations
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#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
}
/// <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 recursive_lock_impl::lock()
{
auto id = ::pplx::details::platform::GetCurrentThreadId();
if ( _M_owner == id )
{
_M_recursionCount++;
}
else
{
_M_cs.lock();
_M_owner = id;
_M_recursionCount = 1;
}
}
void recursive_lock_impl::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;
}
/// <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

View file

@ -0,0 +1,161 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
*
* Simple Linux implementation of a static thread pool.
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
***/
#pragma once
#include <pthread.h>
#include <vector>
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wunreachable-code"
#endif
#include "boost/asio.hpp"
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#if (defined(ANDROID) || defined(__ANDROID__))
#include <atomic>
#include <jni.h>
#include "pplx/pplx.h"
#endif
namespace crossplat {
#if (defined(ANDROID) || 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:
threadpool(size_t n)
: m_service(n),
m_work(m_service)
{
for (size_t i = 0; i < n; i++)
add_thread();
}
static threadpool& shared_instance();
~threadpool()
{
m_service.stop();
for (auto iter = m_threads.begin(); iter != m_threads.end(); ++iter)
{
pthread_t t = *iter;
void* res;
pthread_join(t, &res);
}
}
template<typename T>
void schedule(T task)
{
m_service.post(task);
}
boost::asio::io_service& service()
{
return m_service;
}
private:
struct _cancel_thread { };
void add_thread()
{
pthread_t t;
auto result = pthread_create(&t, nullptr, &thread_start, this);
if (result == 0)
m_threads.push_back(t);
}
void remove_thread()
{
schedule([]() -> void { throw _cancel_thread(); });
}
#if (defined(ANDROID) || defined(__ANDROID__))
static void detach_from_java(void*)
{
JVM.load()->DetachCurrentThread();
}
#endif
static void* thread_start(void *arg)
{
#if (defined(ANDROID) || defined(__ANDROID__))
// Calling get_jvm_env() here forces the thread to be attached.
get_jvm_env();
pthread_cleanup_push(detach_from_java, nullptr);
#endif
threadpool* _this = reinterpret_cast<threadpool*>(arg);
try
{
_this->m_service.run();
}
catch (const _cancel_thread&)
{
// thread was cancelled
}
catch (...)
{
// Something bad happened
#if (defined(ANDROID) || defined(__ANDROID__))
// Reach into the depths of the 'droid!
// NOTE: Uses internals of the bionic library
// Written against android ndk r9d, 7/26/2014
__pthread_cleanup_pop(&__cleanup, true);
throw;
#endif
}
#if (defined(ANDROID) || defined(__ANDROID__))
pthread_cleanup_pop(true);
#endif
return arg;
}
std::vector<pthread_t> m_threads;
boost::asio::io_service m_service;
boost::asio::io_service::work m_work;
};
}

View file

@ -0,0 +1,18 @@
# Lineendings
*.sln eol=crlf
*.vcproj eol=crlf
*.vcxproj* eol=crlf
# Whitespace rules
# strict (no trailing, no tabs)
*.cpp whitespace=trailing-space,space-before-tab,tab-in-indent,cr-at-eol
*.hpp whitespace=trailing-space,space-before-tab,tab-in-indent,cr-at-eol
*.c whitespace=trailing-space,space-before-tab,tab-in-indent,cr-at-eol
*.h whitespace=trailing-space,space-before-tab,tab-in-indent,cr-at-eol
# normal (no trailing)
*.sql whitespace=trailing-space,space-before-tab,cr-at-eol
*.txt whitespace=trailing-space,space-before-tab,cr-at-eol
# special files which must ignore whitespace
*.patch whitespace=-trailing-space

View file

@ -0,0 +1,90 @@
# make .git* files visible to git
!.gitignore
!.gitattributes
.DS_Store
#vim stuff
*~
*.swp
*.o
*.so
*.so.?
*.so.?.?.?
*.a
*.dylib
lib/*
# CMake
*.cmake
*.dir
CMakeFiles
INSTALL.*
ZERO_CHECK.*
CMakeCache.txt
install_manifest.txt
# Windows/Visual Studio
*.vcproj*
*.sln
*.suo
*.ncb
*/Debug/*
*/*/Debug/*
bin/Debug
*/Release/*
*/*/Release/*
*/RelWithDebInfo/*
*/*/RelWithDebInfo/*
# explicitly allow this path with /debug/ in it
!websocketpp/transport/debug/*
objs_shared/
objs_static/
examples/chat_server/chat_server
examples/echo_server/echo_server
examples/chat_client/chat_client
examples/echo_client/echo_client
test/basic/tests
libwebsocketpp.dylib.0.1.0
websocketpp.xcodeproj/xcuserdata/*
websocketpp.xcodeproj/project.xcworkspace/xcuserdata/*
policy_based_notes.hpp
examples/echo_server_tls/echo_server_tls
examples/fuzzing_client/fuzzing_client
examples/stress_client/stress_client
examples/broadcast_server_tls/broadcast_server
test/basic/perf
examples/echo_server_tls/echo_server_tls
examples/concurrent_server/concurrent_server
examples/fuzzing_server_tls/fuzzing_server
examples/wsperf/wsperf
.sconsign.dblite
build/
doxygen/
examples/wsperf/wsperf_client
*.out
*.log
*.opensdf
*.sdf
*.vcxproj
*.vcxproj.filters
*.user
install

View file

@ -0,0 +1,23 @@
language: cpp
compiler:
- gcc
before_install:
- sudo apt-get install libboost-regex1.48-dev libboost-system1.48-dev libboost-thread1.48-dev libboost-test1.48-dev libboost-random1.48-dev -y
env:
global:
- BOOST_INCLUDES=/usr/include
- BOOST_LIBS=/usr/lib
script: scons -j 2 && scons test
branches:
only:
- master
- permessage-deflate
- experimental
- 0.3.x-cmake
- develop
notifications:
recipients:
- travis@zaphoyd.com
email:
on_success: change
on_failure: always

View file

@ -0,0 +1,244 @@
############ Setup project and cmake
# Project name
project (websocketpp)
# Minimum cmake requirement. We should require a quite recent
# cmake for the dependency find macros etc. to be up to date.
cmake_minimum_required (VERSION 2.6)
set (WEBSOCKETPP_MAJOR_VERSION 0)
set (WEBSOCKETPP_MINOR_VERSION 5)
set (WEBSOCKETPP_PATCH_VERSION 1)
set (WEBSOCKETPP_VERSION ${WEBSOCKETPP_MAJOR_VERSION}.${WEBSOCKETPP_MINOR_VERSION}.${WEBSOCKETPP_PATCH_VERSION})
set(INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files")
if (WIN32 AND NOT CYGWIN)
set (DEF_INSTALL_CMAKE_DIR cmake)
else ()
set (DEF_INSTALL_CMAKE_DIR lib/cmake/websocketpp)
endif ()
set (INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files")
# Make relative paths absolute (needed later on)
foreach (p INCLUDE CMAKE)
set (var INSTALL_${p}_DIR)
if (NOT IS_ABSOLUTE "${${var}}")
set (${var} "${CMAKE_INSTALL_PREFIX}/${${var}}")
endif ()
endforeach ()
# Set CMake library search policy
if (COMMAND cmake_policy)
cmake_policy (SET CMP0003 NEW)
cmake_policy (SET CMP0005 NEW)
endif ()
# Disable unnecessary build types
set (CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo;Debug" CACHE STRING "Configurations" FORCE)
# Include our cmake macros
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include (CMakeHelpers)
############ Paths
set (WEBSOCKETPP_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
set (WEBSOCKETPP_INCLUDE ${WEBSOCKETPP_ROOT}/websocketpp)
set (WEBSOCKETPP_BUILD_ROOT ${CMAKE_CURRENT_BINARY_DIR})
set (WEBSOCKETPP_BIN ${WEBSOCKETPP_BUILD_ROOT}/bin)
set (WEBSOCKETPP_LIB ${WEBSOCKETPP_BUILD_ROOT}/lib)
# CMake install step prefix. I assume linux users want the prefix to
# be the default /usr or /usr/local so this is only adjusted on Windows.
# - Windows: Build the INSTALL project in your solution file.
# - Linux/OSX: make install.
if (MSVC)
set (CMAKE_INSTALL_PREFIX "${WEBSOCKETPP_ROOT}/install")
endif ()
############ Build customization
# Override from command line "CMake -D<OPTION>=TRUE/FALSE/0/1/ON/OFF"
option (ENABLE_CPP11 "Build websocketpp with CPP11 features enabled." TRUE)
option (BUILD_EXAMPLES "Build websocketpp examples." FALSE)
option (BUILD_TESTS "Build websocketpp tests." FALSE)
if (BUILD_TESTS OR BUILD_EXAMPLES)
############ Compiler specific setup
set (WEBSOCKETPP_PLATFORM_LIBS "")
set (WEBSOCKETPP_PLATFORM_TSL_LIBS "")
set (WEBSOCKETPP_BOOST_LIBS "")
# VC9 and C++11 reasoning
if (ENABLE_CPP11 AND MSVC AND MSVC90)
message("* Detected Visual Studio 9 2008, disabling C++11 support.")
set (ENABLE_CPP11 FALSE)
endif ()
# Detect clang. Not officially reported by cmake.
execute_process(COMMAND "${CMAKE_CXX_COMPILER}" "-v" ERROR_VARIABLE CXX_VER_STDERR)
if ("${CXX_VER_STDERR}" MATCHES ".*clang.*")
set (CMAKE_COMPILER_IS_CLANGXX 1)
endif ()
# C++11 defines
if (ENABLE_CPP11)
if (MSVC)
add_definitions (-D_WEBSOCKETPP_CPP11_FUNCTIONAL_)
add_definitions (-D_WEBSOCKETPP_CPP11_SYSTEM_ERROR_)
add_definitions (-D_WEBSOCKETPP_CPP11_RANDOM_DEVICE_)
add_definitions (-D_WEBSOCKETPP_CPP11_MEMORY_)
else()
add_definitions (-D_WEBSOCKETPP_CPP11_STL_)
endif()
endif ()
# Visual studio
if (MSVC)
set (WEBSOCKETPP_BOOST_LIBS system thread)
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL /Gy /GF /Ox /Ob2 /Ot /Oi /MP /arch:SSE2 /fp:fast")
set (CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG /INCREMENTAL:NO /OPT:REF /OPT:ICF")
add_definitions (/W3 /wd4996 /wd4995 /wd4355)
add_definitions (-DUNICODE -D_UNICODE)
add_definitions (-D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
add_definitions (-DNOMINMAX)
endif ()
# g++
if (CMAKE_COMPILER_IS_GNUCXX)
set (WEBSOCKETPP_PLATFORM_LIBS pthread rt)
set (WEBSOCKETPP_PLATFORM_TSL_LIBS ssl crypto)
set (WEBSOCKETPP_BOOST_LIBS system thread)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
if (NOT APPLE)
add_definitions (-DNDEBUG -Wall -Wcast-align) # todo: should we use CMAKE_C_FLAGS for these?
endif ()
# Try to detect version. Note: Not tested!
execute_process (COMMAND ${CMAKE_CXX_COMPILER} "-dumpversion" OUTPUT_VARIABLE GCC_VERSION)
if ("${GCC_VERSION}" STRGREATER "4.4.0")
message("* C++11 support partially enabled due to GCC version ${GCC_VERSION}")
set (WEBSOCKETPP_BOOST_LIBS system thread)
endif ()
endif ()
# clang
if (CMAKE_COMPILER_IS_CLANGXX)
if (NOT APPLE)
set (WEBSOCKETPP_PLATFORM_LIBS pthread rt)
else()
set (WEBSOCKETPP_PLATFORM_LIBS pthread)
endif()
set (WEBSOCKETPP_PLATFORM_TSL_LIBS ssl crypto)
set (WEBSOCKETPP_BOOST_LIBS system thread)
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++0x -stdlib=libc++") # todo: is libc++ really needed here?
if (NOT APPLE)
add_definitions (-DNDEBUG -Wall -Wno-padded) # todo: should we use CMAKE_C_FLAGS for these?
endif ()
endif ()
# OSX, can override above.
if (APPLE)
add_definitions (-DNDEBUG -Wall)
endif ()
if (BUILD_EXAMPLES)
list (APPEND WEBSOCKETPP_BOOST_LIBS random)
endif()
############ Dependencies
# Set BOOST_ROOT env variable or pass with cmake -DBOOST_ROOT=path.
# BOOST_ROOT can also be defined by a previous run from cmake cache.
if (NOT "$ENV{BOOST_ROOT_CPP11}" STREQUAL "")
# Scons documentation for BOOST_ROOT_CPP11:
# "look for optional second boostroot compiled with clang's libc++ STL library
# this prevents warnings/errors when linking code built with two different
# incompatible STL libraries."
file (TO_CMAKE_PATH "$ENV{BOOST_ROOT_CPP11}" BOOST_ROOT)
set (BOOST_ROOT ${BOOST_ROOT} CACHE PATH "BOOST_ROOT dependency path" FORCE)
endif ()
if ("${BOOST_ROOT}" STREQUAL "")
file (TO_CMAKE_PATH "$ENV{BOOST_ROOT}" BOOST_ROOT)
# Cache BOOST_ROOT for runs that do not define $ENV{BOOST_ROOT}.
set (BOOST_ROOT ${BOOST_ROOT} CACHE PATH "BOOST_ROOT dependency path" FORCE)
endif ()
message ("* Configuring Boost")
message (STATUS "-- Using BOOST_ROOT")
message (STATUS " " ${BOOST_ROOT})
if (MSVC)
set (Boost_USE_MULTITHREADED TRUE)
set (Boost_USE_STATIC_LIBS TRUE)
else ()
set (Boost_USE_MULTITHREADED FALSE)
set (Boost_USE_STATIC_LIBS FALSE)
endif ()
set (Boost_FIND_REQUIRED TRUE)
set (Boost_FIND_QUIETLY TRUE)
set (Boost_DEBUG FALSE)
set (Boost_USE_MULTITHREADED TRUE)
set (Boost_ADDITIONAL_VERSIONS "1.39.0" "1.40.0" "1.41.0" "1.42.0" "1.43.0" "1.44.0" "1.46.1") # todo: someone who knows better spesify these!
find_package (Boost 1.39.0 COMPONENTS "${WEBSOCKETPP_BOOST_LIBS}")
if (Boost_FOUND)
# Boost is a project wide global dependency.
include_directories (${Boost_INCLUDE_DIRS})
link_directories (${Boost_LIBRARY_DIRS})
# Pretty print status
message (STATUS "-- Include Directories")
foreach (include_dir ${Boost_INCLUDE_DIRS})
message (STATUS " " ${include_dir})
endforeach ()
message (STATUS "-- Library Directories")
foreach (library_dir ${Boost_LIBRARY_DIRS})
message (STATUS " " ${library_dir})
endforeach ()
message (STATUS "-- Libraries")
foreach (boost_lib ${Boost_LIBRARIES})
message (STATUS " " ${boost_lib})
endforeach ()
message ("")
else ()
message (FATAL_ERROR "Failed to find required dependency: boost")
endif ()
find_package(OpenSSL)
endif()
############ Add projects
# Add main library
add_subdirectory (websocketpp)
# Add examples
if (BUILD_EXAMPLES)
add_subdirectory (examples)
endif ()
# Add tests
if (BUILD_TESTS)
add_subdirectory (test)
endif ()
print_used_build_config()
export (PACKAGE websocketpp)
configure_file (websocketpp-config.cmake.in "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-config.cmake" @ONLY)
configure_file (websocketpp-configVersion.cmake.in "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-configVersion.cmake" @ONLY)
# Install the websocketpp-config.cmake and websocketpp-configVersion.cmake
install (FILES
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-config.cmake"
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-configVersion.cmake"
DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev)

View file

@ -0,0 +1,145 @@
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.

View file

@ -0,0 +1,266 @@
import os, sys, commands
env = Environment(ENV = os.environ)
# figure out a better way to configure this
if os.environ.has_key('CXX'):
env['CXX'] = os.environ['CXX']
if os.environ.has_key('DEBUG'):
env['DEBUG'] = os.environ['DEBUG']
if os.environ.has_key('CXXFLAGS'):
#env['CXXFLAGS'] = os.environ['CXXFLAGS']
env.Append(CXXFLAGS = os.environ['CXXFLAGS'])
if os.environ.has_key('LINKFLAGS'):
#env['LDFLAGS'] = os.environ['LDFLAGS']
env.Append(LINKFLAGS = os.environ['LINKFLAGS'])
## Boost
##
## Note: You need to either set BOOSTROOT to the root of a stock Boost distribution
## or set BOOST_INCLUDES and BOOST_LIBS if Boost comes with your OS distro e.g. and
## needs BOOST_INCLUDES=/usr/include/boost and BOOST_LIBS=/usr/lib like Ubuntu.
##
if os.environ.has_key('BOOSTROOT'):
os.environ['BOOST_ROOT'] = os.environ['BOOSTROOT']
if os.environ.has_key('BOOST_ROOT'):
env['BOOST_INCLUDES'] = os.environ['BOOST_ROOT']
env['BOOST_LIBS'] = os.path.join(os.environ['BOOST_ROOT'], 'stage', 'lib')
elif os.environ.has_key('BOOST_INCLUDES') and os.environ.has_key('BOOST_LIBS'):
env['BOOST_INCLUDES'] = os.environ['BOOST_INCLUDES']
env['BOOST_LIBS'] = os.environ['BOOST_LIBS']
else:
raise SCons.Errors.UserError, "Neither BOOST_ROOT, nor BOOST_INCLUDES + BOOST_LIBS was set!"
if os.environ.has_key('WSPP_ENABLE_CPP11'):
env['WSPP_ENABLE_CPP11'] = True
else:
env['WSPP_ENABLE_CPP11'] = False
boost_linkshared = False
def boostlibs(libnames,localenv):
if localenv['PLATFORM'].startswith('win'):
# Win/VC++ supports autolinking. nothing to do.
# http://www.boost.org/doc/libs/1_49_0/more/getting_started/windows.html#auto-linking
return []
else:
libs = []
prefix = localenv['SHLIBPREFIX'] if boost_linkshared else localenv['LIBPREFIX']
suffix = localenv['SHLIBSUFFIX'] if boost_linkshared else localenv['LIBSUFFIX']
for name in libnames:
lib = File(os.path.join(localenv['BOOST_LIBS'], '%sboost_%s%s' % (prefix, name, suffix)))
libs.append(lib)
return libs
if env['PLATFORM'].startswith('win'):
env.Append(CPPDEFINES = ['WIN32',
'NDEBUG',
'WIN32_LEAN_AND_MEAN',
'_WIN32_WINNT=0x0600',
'_CONSOLE',
'BOOST_TEST_DYN_LINK',
'NOMINMAX',
'_WEBSOCKETPP_CPP11_MEMORY_',
'_WEBSOCKETPP_CPP11_FUNCTIONAL_'])
arch_flags = '/arch:SSE2'
opt_flags = '/Ox /Oi /fp:fast'
warn_flags = '/W3 /wd4996 /wd4995 /wd4355'
env['CCFLAGS'] = '%s /EHsc /GR /GS- /MD /nologo %s %s' % (warn_flags, arch_flags, opt_flags)
env['LINKFLAGS'] = '/INCREMENTAL:NO /MANIFEST /NOLOGO /OPT:REF /OPT:ICF /MACHINE:X86'
elif env['PLATFORM'] == 'posix':
if env.has_key('DEBUG'):
env.Append(CCFLAGS = ['-g', '-O0'])
else:
env.Append(CPPDEFINES = ['NDEBUG'])
env.Append(CCFLAGS = ['-O1', '-fomit-frame-pointer'])
env.Append(CCFLAGS = ['-Wall'])
#env['LINKFLAGS'] = ''
elif env['PLATFORM'] == 'darwin':
if not os.environ.has_key('CXX'):
env['CXX'] = "clang++"
if env.has_key('DEBUG'):
env.Append(CCFLAGS = ['-g', '-O0'])
else:
env.Append(CPPDEFINES = ['NDEBUG'])
env.Append(CCFLAGS = ['-O1', '-fomit-frame-pointer'])
env.Append(CCFLAGS = ['-Wall'])
#env['LINKFLAGS'] = ''
if env['PLATFORM'].startswith('win'):
#env['LIBPATH'] = env['BOOST_LIBS']
pass
else:
env['LIBPATH'] = ['/usr/lib',
'/usr/local/lib'] #, env['BOOST_LIBS']
# Compiler specific warning flags
if env['CXX'].startswith('g++'):
#env.Append(CCFLAGS = ['-Wconversion'])
env.Append(CCFLAGS = ['-Wcast-align'])
env.Append(CCFLAGS = ['-Wshadow'])
env.Append(CCFLAGS = ['-Wunused-parameter'])
elif env['CXX'].startswith('clang++'):
#env.Append(CCFLAGS = ['-Wcast-align'])
#env.Append(CCFLAGS = ['-Wglobal-constructors'])
#env.Append(CCFLAGS = ['-Wconversion'])
env.Append(CCFLAGS = ['-Wno-padded'])
env.Append(CCFLAGS = ['-Wshadow'])
env.Append(CCFLAGS = ['-Wunused-parameter'])
env.Append(CCFLAGS = ['-Wsometimes-uninitialized'])
env.Append(CCFLAGS = ['-Wuninitialized'])
#env.Append(CCFLAGS = ['-Weverything'])
#env.Append(CCFLAGS = ['-Wno-documentation'])
#env.Append(CCFLAGS = ['-Wno-weak-vtables'])
#env.Append(CCFLAGS = ['-Wno-global-constructors'])
#env.Append(CCFLAGS = ['-Wno-sign-conversion'])
#env.Append(CCFLAGS = ['-Wno-exit-time-destructors'])
# Wpadded
# Wsign-conversion
platform_libs = []
tls_libs = []
tls_build = False
if env['PLATFORM'] == 'posix':
platform_libs = ['pthread', 'rt']
tls_libs = ['ssl', 'crypto']
tls_build = True
elif env['PLATFORM'] == 'darwin':
tls_libs = ['ssl', 'crypto']
tls_build = True
elif env['PLATFORM'].startswith('win'):
# Win/VC++ supports autolinking. nothing to do.
pass
## Append WebSocket++ path
env.Append(CPPPATH = ['#'])
##### Set up C++11 environment
polyfill_libs = [] # boost libraries used as drop in replacements for incomplete
# C++11 STL implementations
env_cpp11 = env.Clone ()
if env_cpp11['CXX'].startswith('g++'):
# TODO: check g++ version
GCC_VERSION = commands.getoutput(env_cpp11['CXX'] + ' -dumpversion')
if GCC_VERSION > "4.4.0":
print "C++11 build environment partially enabled"
env_cpp11.Append(WSPP_CPP11_ENABLED = "true",CXXFLAGS = ['-std=c++0x'],TOOLSET = ['g++'],CPPDEFINES = ['_WEBSOCKETPP_CPP11_STL_'])
else:
print "C++11 build environment is not supported on this version of G++"
elif env_cpp11['CXX'].startswith('clang++'):
print "C++11 build environment enabled"
env.Append(CXXFLANGS = ['-stdlib=libc++'],LINKFLAGS=['-stdlib=libc++'])
env_cpp11.Append(WSPP_CPP11_ENABLED = "true",CXXFLAGS = ['-std=c++0x','-stdlib=libc++'],LINKFLAGS = ['-stdlib=libc++'],TOOLSET = ['clang++'],CPPDEFINES = ['_WEBSOCKETPP_CPP11_STL_'])
# look for optional second boostroot compiled with clang's libc++ STL library
# this prevents warnings/errors when linking code built with two different
# incompatible STL libraries.
if os.environ.has_key('BOOST_ROOT_CPP11'):
env_cpp11['BOOST_INCLUDES'] = os.environ['BOOST_ROOT_CPP11']
env_cpp11['BOOST_LIBS'] = os.path.join(os.environ['BOOST_ROOT_CPP11'], 'stage', 'lib')
elif os.environ.has_key('BOOST_INCLUDES_CPP11') and os.environ.has_key('BOOST_LIBS_CPP11'):
env_cpp11['BOOST_INCLUDES'] = os.environ['BOOST_INCLUDES_CPP11']
env_cpp11['BOOST_LIBS'] = os.environ['BOOST_LIBS_CPP11']
else:
print "C++11 build environment disabled"
# if the build system is known to allow the isystem modifier for library include
# values then use it for the boost libraries. Otherwise just add them to the
# regular CPPPATH values.
if env['CXX'].startswith('g++') or env['CXX'].startswith('clang'):
env.Append(CPPFLAGS = '-isystem ' + env['BOOST_INCLUDES'])
else:
env.Append(CPPPATH = [env['BOOST_INCLUDES']])
env.Append(LIBPATH = [env['BOOST_LIBS']])
# if the build system is known to allow the isystem modifier for library include
# values then use it for the boost libraries. Otherwise just add them to the
# regular CPPPATH values.
if env_cpp11['CXX'].startswith('g++') or env_cpp11['CXX'].startswith('clang'):
env_cpp11.Append(CPPFLAGS = '-isystem ' + env_cpp11['BOOST_INCLUDES'])
else:
env_cpp11.Append(CPPPATH = [env_cpp11['BOOST_INCLUDES']])
env_cpp11.Append(LIBPATH = [env_cpp11['BOOST_LIBS']])
releasedir = 'build/release/'
debugdir = 'build/debug/'
testdir = 'build/test/'
builddir = releasedir
Export('env')
Export('env_cpp11')
Export('platform_libs')
Export('boostlibs')
Export('tls_libs')
Export('polyfill_libs')
## END OF CONFIG !!
## TARGETS:
if not env['PLATFORM'].startswith('win'):
# Unit tests, add test folders with SConscript files to to_test list.
to_test = ['utility','http','logger','random','processors','message_buffer','extension','transport/iostream','transport/asio','roles','endpoint','connection','transport'] #,'http','processors','connection'
for t in to_test:
new_tests = SConscript('#/test/'+t+'/SConscript',variant_dir = testdir + t, duplicate = 0)
for a in new_tests:
new_alias = Alias('test', [a], a.abspath)
AlwaysBuild(new_alias)
# Main test application
#main = SConscript('#/examples/dev/SConscript',variant_dir = builddir + 'dev',duplicate = 0)
# echo_server
echo_server = SConscript('#/examples/echo_server/SConscript',variant_dir = builddir + 'echo_server',duplicate = 0)
# echo_server_tls
if tls_build:
echo_server_tls = SConscript('#/examples/echo_server_tls/SConscript',variant_dir = builddir + 'echo_server_tls',duplicate = 0)
echo_server_both = SConscript('#/examples/echo_server_both/SConscript',variant_dir = builddir + 'echo_server_both',duplicate = 0)
# broadcast_server
broadcast_server = SConscript('#/examples/broadcast_server/SConscript',variant_dir = builddir + 'broadcast_server',duplicate = 0)
# testee_server
testee_server = SConscript('#/examples/testee_server/SConscript',variant_dir = builddir + 'testee_server',duplicate = 0)
# testee_client
testee_client = SConscript('#/examples/testee_client/SConscript',variant_dir = builddir + 'testee_client',duplicate = 0)
# utility_client
utility_client = SConscript('#/examples/utility_client/SConscript',variant_dir = builddir + 'utility_client',duplicate = 0)
# debug_client
debug_client = SConscript('#/examples/debug_client/SConscript',variant_dir = builddir + 'debug_client',duplicate = 0)
# debug_server
debug_server = SConscript('#/examples/debug_server/SConscript',variant_dir = builddir + 'debug_server',duplicate = 0)
# subprotocol_server
subprotocol_server = SConscript('#/examples/subprotocol_server/SConscript',variant_dir = builddir + 'subprotocol_server',duplicate = 0)
# telemetry_server
telemetry_server = SConscript('#/examples/telemetry_server/SConscript',variant_dir = builddir + 'telemetry_server',duplicate = 0)
if not env['PLATFORM'].startswith('win'):
# iostream_server
iostream_server = SConscript('#/examples/iostream_server/SConscript',variant_dir = builddir + 'iostream_server',duplicate = 0)
# telemetry_client
telemetry_client = SConscript('#/examples/telemetry_client/SConscript',variant_dir = builddir + 'telemetry_client',duplicate = 0)
# print_server
print_server = SConscript('#/examples/print_server/SConscript',variant_dir = builddir + 'print_server',duplicate = 0)

View file

@ -0,0 +1,234 @@
HEAD
0.5.1 - 2015-02-27
- Bug: Fixes an issue where some frame data was counted against the max header
size limit, resulting in connections that included a lot of frame data
immediately after the opening handshake to fail.
- Bug: Fix a type in the name of the set method for `max_http_body_size`. #406
Thank you jplatte for reporting.
0.5.0 - 2015-01-22
- BREAKING UTILITY CHANGE: Deprecated methods `http::parser::parse_headers`,
`http::response::parse_complete`, and `http::request::parse_complete` have
been removed.
- Security: Disabled SSLv3 in example servers.
- Feature: Adds basic support for accessing HTTP request bodies in the http
handler. #181
- Feature: Adds the ability to register a shutdown handler when using the
iostream transport. This provides a clean interface for triggering the shut
down of external sockets and other cleanup without hooking in to higher level
WebSocket handlers.
- Feature: Adds the ability to register a write handler when using the iostream
transport. This handler can be used to handle transport output in place of
registering an ostream to write to.
- Feature: Adds a new logging policy that outputs to syslog. #386 Thank you Tom
Hughes for submitting the initial version of this policy.
- Improvement: Message payload logging now prints text for text messages rather
than binary.
- Improvement: Overhaul of handshake state machine. Should make it impossible
for exceptions to bubble out of transport methods like `io_service::run`.
- Improvement: Overhaul of handshake error reporting. Fail handler error codes
will be more detailed and precise. Adds new [fail] and [http] logging channels
that log failed websocket connections and successful HTTP connections
respectively. A new aggregate channel package, `alevel::access_core`, allows
enabling connect, disconnect, fail, and http together. Successful HTTP
connections will no longer trigger a fail handler.
- Improvement: Ability to terminate connection during an http handler to cleanly
suppress the default outgoing HTTP response.
- Documentation: Add Sending & Receiving Messages step to chapter one of the
`utility_client` tutorial. Update `utility_client` example to match.
- Cleanup: Removes unused files & STL includes. Adds required STL includes.
Normalizes include order.
- Bug: Fixes a fatal state error when a handshake response is completed
immediately after that handshake times out. #389
- Bug: MinGW fixes; C++11 feature detection, localtime use. #393 Thank you
Schebb for reporting, code, and testing.
- Bug: Fixes an issue where `websocketpp::exception::what()` could return an out
of scope pointer. #397 Thank you fabioang for reporting.
- Bug: Fixes an issue where endpoints were not reset properly after a call to
`endpoint::listen` failed. #390 Thank you wyyqyl for reporting.
0.4.0 - 2014-11-04
- BREAKING API CHANGE: All WebSocket++ methods now throw an exception of type
`websocketpp::exception` which derives from `std::exception`. This normalizes
all exception types under the standard exception hierarchy and allows
WebSocket++ exceptions to be caught in the same statement as others. The error
code that was previously thrown is wrapped in the exception object and can be
accessed via the `websocketpp::exception::code()` method.
- BREAKING API CHANGE: Custom logging policies have some new required
constructors that take generic config settings rather than pointers to
std::ostreams. This allows writing logging policies that do not involve the
use of std::ostream. This does not affect anyone using the built in logging
policies.
- BREAKING UTILITY CHANGE: `websocketpp::lib::net::htonll` and
`websocketpp::lib::net::ntohll` have been prefixed with an underscore to avoid
conflicts with similarly named macros in some operating systems. If you are
using the WebSocket++ provided 64 bit host/network byte order functions you
will need to switch to the prefixed versions.
- BREAKING UTILITY CHANGE: The signature of `base64_encode` has changed from
`websocketpp::base64_encode(unsigned char const *, unsigned int)` to
`websocketpp::base64_encode(unsigned char const *, size_t)`.
- BREAKING UTILITY CHANGE: The signature of `sha1::calc` has changed from
`websocketpp::sha1::calc(void const *, int, unsigned char *)` to
`websocketpp::sha1::calc(void const *, size_t, unsigned char *)`
- Feature: Adds incomplete `minimal_server` and `minimal_client` configs that
can be used to build custom configs without pulling in the dependencies of
`core` or `core_client`. These configs will offer a stable base config to
future-proof custom configs.
- Improvement: Core library no longer has std::iostream as a dependency.
std::iostream is still required for the optional iostream logging policy and
iostream transport.
- Bug: C++11 Chrono support was being incorrectly detected by the `boost_config`
header. Thank you Max Dmitrichenko for reporting and a patch.
- Bug: use of `std::put_time` is now guarded by a unique flag rather than a
chrono library flag. Thank you Max Dmitrichenko for reporting.
- Bug: Fixes non-thread safe use of std::localtime. #347 #383
- Compatibility: Adjust usage of std::min to be more compatible with systems
that define a min(...) macro.
- Compatibility: Removes unused parameters from all library, test, and example
code. This assists with those developing with -Werror and -Wunused-parameter
#376
- Compatibility: Renames ntohll and htonll methods to avoid conflicts with
platform specific macros. #358 #381, #382 Thank you logotype, unphased,
svendjo
- Cleanup: Removes unused functions, fixes variable shadow warnings, normalizes
all whitespace in library, examples, and tests to 4 spaces. #376
0.3.0 - 2014-08-10
- Feature: Adds `start_perpetual` and `stop_perpetual` methods to asio transport
These may be used to replace manually managed `asio::io_service::work` objects
- Feature: Allow setting pong and handshake timeouts at runtime.
- Feature: Allows changing the listen backlog queue length.
- Feature: Split tcp init into pre and post init.
- Feature: Adds URI method to extract query string from URI. Thank you Banaan
for code. #298
- Feature: Adds a compile time switch to asio transport config to disable
certain multithreading features (some locks, asio strands)
- Feature: Adds the ability to pause reading on a connection. Paused connections
will not read more data from their socket, allowing TCP flow control to work
without blocking the main thread.
- Feature: Adds the ability to specify whether or not to use the `SO_REUSEADDR`
TCP socket option. The default for this value has been changed from `true` to
`false`.
- Feature: Adds the ability to specify a maximum message size.
- Feature: Adds `close::status::get_string(...)` method to look up a human
readable string given a close code value.
- Feature: Adds `connection::read_all(...)` method to iostream transport as a
convenience method for reading all data into the connection buffer without the
end user needing to manually loop on `read_some`.
- Improvement: Open, close, and pong timeouts can be disabled entirely by
setting their duration to 0.
- Improvement: Numerous performance improvements. Including: tuned default
buffer sizes based on profiling, caching of handler binding for async
reads/writes, non-malloc allocators for read/write handlers, disabling of a
number of questionably useful range sanity checks in tight inner loops.
- Improvement: Cleaned up the handling of TLS related errors. TLS errors will
now be reported with more detail on the info channel rather than all being
`tls_short_read` or `pass_through`. In addition, many cases where a TLS short
read was in fact expected are no longer classified as errors. Expected TLS
short reads and quasi-expected socket shutdown related errors will no longer
be reported as unclean WebSocket shutdowns to the application. Information
about them will remain in the info error channel for debugging purposes.
- Improvement: `start_accept` and `listen` errors are now reported to the caller
either via an exception or an ec parameter.
- Improvement: Outgoing writes are now batched for improved message throughput
and reduced system call and TCP frame overhead.
- Bug: Fix some cases of calls to empty lib::function objects.
- Bug: Fix memory leak of connection objects due to cached handlers holding on to
reference counted pointers. #310 Thank you otaras for reporting.
- Bug: Fix issue with const endpoint accessors (such as `get_user_agent`) not
compiling due to non-const mutex use. #292 Thank you logofive for reporting.
- Bug: Fix handler allocation crash with multithreaded `io_service`.
- Bug: Fixes incorrect whitespace handling in header parsing. #301 Thank you
Wolfram Schroers for reporting
- Bug: Fix a crash when parsing empty HTTP headers. Thank you Thingol for
reporting.
- Bug: Fix a crash following use of the `stop_listening` function. Thank you
Thingol for reporting.
- Bug: Fix use of variable names that shadow function parameters. The library
should compile cleanly with -Wshadow now. Thank you giszo for reporting. #318
- Bug: Fix an issue where `set_open_handshake_timeout` was ignored by server
code. Thank you Robin Rowe for reporting.
- Bug: Fix an issue where custom timeout values weren't being propagated from
endpoints to new connections.
- Bug: Fix a number of memory leaks related to server connection failures. #323
#333 #334 #335 Thank you droppy and aydany for reporting and patches.
reporting.
- Compatibility: Fix compile time conflict with Visual Studio's MIN/MAX macros.
Thank you Robin Rowe for reporting.
- Documentation: Examples and test suite build system now defaults to clang on
OS X
0.3.0-alpha4 - 2013-10-11
- HTTP requests ending normally are no longer logged as errors. Thank you Banaan
for reporting. #294
- Eliminates spurious expired timers in certain error conditions. Thank you
Banaan for reporting. #295
- Consolidates all bundled library licenses into the COPYING file. #294
- Updates bundled sha1 library to one with a cleaner interface and more
straight-forward license. Thank you lotodore for reporting and Evgeni Golov
for reviewing. #294
- Re-introduces strands to asio transport, allowing `io_service` thread pools to
be used (with some limitations).
- Removes endpoint code that kept track of a connection list that was never used
anywhere. Removes a lock and reduces connection creation/deletion complexity
from O(log n) to O(1) in the number of connections.
- A number of internal changes to transport APIs
- Deprecates iostream transport `readsome` in favor of `read_some` which is more
consistent with the naming of the rest of the library.
- Adds preliminary signaling to iostream transport of eof and fatal transport
errors
- Updates transport code to use shared pointers rather than raw pointers to
prevent asio from retaining pointers to connection methods after the
connection goes out of scope. #293 Thank you otaras for reporting.
- Fixes an issue where custom headers couldn't be set for client connections
Thank you Jerry Win and Wolfram Schroers for reporting.
- Fixes a compile error on visual studio when using interrupts. Thank you Javier
Rey Neira for reporting this.
- Adds new 1012 and 1013 close codes per IANA registry
- Add `set_remote_endpoint` method to iostream transport.
- Add `set_secure` method to iostream transport.
- Fix typo in .gitattributes file. Thank you jstarasov for reporting this. #280
- Add missing locale include. Thank you Toninoso for reporting this. #281
- Refactors `asio_transport` endpoint and adds full documentation and exception
free varients of all methods.
- Removes `asio_transport` endpoint method cancel(). Use `stop_listen()` instead
- Wrap internal `io_service` `run_one()` method
- Suppress error when trying to shut down a connection that was already closed
0.3.0-alpha3 - 2013-07-16
- Minor refactor to bundled sha1 library
- HTTP header comparisons are now case insensitive. #220, #275
- Refactors URI to be exception free and not use regular expressions. This
eliminates the dependency on boost or C++11 regex libraries allowing native
C++11 usage on GCC 4.4 and higher and significantly reduces staticly built
binary sizes.
- Updates handling of Server and User-Agent headers to better handle custom
settings and allow suppression of these headers for security purposes.
- Fix issue where pong timeout handler always fired. Thank you Steven Klassen
for reporting this bug.
- Add ping and pong endpoint wrapper methods
- Add `get_request()` pass through method to connection to allow calling methods
specific to the HTTP policy in use.
- Fix issue compile error with `WEBSOCKETPP_STRICT_MASKING` enabled and another
issue where `WEBSOCKETPP_STRICT_MASKING` was not applied to incoming messages.
Thank you Petter Norby for reporting and testing these bugs. #264
- Add additional macro guards for use with boost_config. Thank you breyed
for testing and code. #261
0.3.0-alpha2 - 2013-06-09
- Fix a regression that caused servers being sent two close frames in a row
to end a connection uncleanly. #259
- Fix a regression that caused spurious frames following a legitimate close
frames to erroneously trigger handlers. #258
- Change default HTTP response error code when no http_handler is defined from
500/Internal Server Error to 426/Upgrade Required
- Remove timezone from logger timestamp to work around issues with the Windows
implementation of strftime. Thank you breyed for testing and code. #257
- Switch integer literals to char literals to improve VCPP compatibility.
Thank you breyed for testing and code. #257
- Add MSVCPP warning suppression for the bundled SHA1 library. Thank you breyed
for testing and code. #257
0.3.0-alpha1 - 2013-06-09
- Initial Release

View file

@ -0,0 +1,52 @@
#include <set>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
class broadcast_server {
public:
broadcast_server() {
m_server.init_asio();
m_server.set_open_handler(bind(&broadcast_server::on_open,this,::_1));
m_server.set_close_handler(bind(&broadcast_server::on_close,this,::_1));
m_server.set_message_handler(bind(&broadcast_server::on_message,this,::_1,::_2));
}
void on_open(connection_hdl hdl) {
m_connections.insert(hdl);
}
void on_close(connection_hdl hdl) {
m_connections.erase(hdl);
}
void on_message(connection_hdl hdl, server::message_ptr msg) {
for (auto it : m_connections) {
m_server.send(it,msg);
}
}
void run(uint16_t port) {
m_server.listen(port);
m_server.start_accept();
m_server.run();
}
private:
typedef std::set<connection_hdl,std::owner_less<connection_hdl>> con_list;
server m_server;
con_list m_connections;
};
int main() {
broadcast_server server;
server.run(9002);
}

View file

@ -0,0 +1,65 @@
#include <functional>
#include <mutex>
#include <set>
#include <thread>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
class count_server {
public:
count_server() : m_count(0) {
m_server.init_asio();
m_server.set_open_handler(bind(&count_server::on_open,this,_1));
m_server.set_close_handler(bind(&count_server::on_close,this,_1));
}
void on_open(connection_hdl hdl) {
std::lock_guard<std::mutex> lock(m_mutex);
m_connections.insert(hdl);
}
void on_close(connection_hdl hdl) {
std::lock_guard<std::mutex> lock(m_mutex);
m_connections.erase(hdl);
}
void count() {
while (1) {
sleep(1);
m_count++;
std::stringstream ss;
ss << m_count;
std::lock_guard<std::mutex> lock(m_mutex);
for (auto it : m_connections) {
m_server.send(it,ss.str(),websocketpp::frame::opcode::text);
}
}
}
void run(uint16_t port) {
m_server.listen(port);
m_server.start_accept();
m_server.run();
}
private:
typedef std::set<connection_hdl,std::owner_less<connection_hdl>> con_list;
int m_count;
server m_server;
con_list m_connections;
std::mutex m_mutex;
};
int main() {
count_server server;
std::thread t(std::bind(&count_server::count,&server));
server.run(9002);
}

View file

@ -0,0 +1,6 @@
file (GLOB SDIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *)
foreach (SUBDIR ${SDIRS})
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/CMakeLists.txt")
add_subdirectory (${SUBDIR})
endif ()
endforeach ()

View file

@ -0,0 +1,88 @@
#include <iostream>
#include <map>
#include <exception>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
struct connection_data {
int sessionid;
std::string name;
};
class print_server {
public:
print_server() : m_next_sessionid(1) {
m_server.init_asio();
m_server.set_open_handler(bind(&print_server::on_open,this,::_1));
m_server.set_close_handler(bind(&print_server::on_close,this,::_1));
m_server.set_message_handler(bind(&print_server::on_message,this,::_1,::_2));
}
void on_open(connection_hdl hdl) {
connection_data data;
data.sessionid = m_next_sessionid++;
data.name = "";
m_connections[hdl] = data;
}
void on_close(connection_hdl hdl) {
connection_data& data = get_data_from_hdl(hdl);
std::cout << "Closing connection " << data.name
<< " with sessionid " << data.sessionid << std::endl;
m_connections.erase(hdl);
}
void on_message(connection_hdl hdl, server::message_ptr msg) {
connection_data& data = get_data_from_hdl(hdl);
if (data.name == "") {
data.name = msg->get_payload();
std::cout << "Setting name of connection with sessionid "
<< data.sessionid << " to " << data.name << std::endl;
} else {
std::cout << "Got a message from connection " << data.name
<< " with sessionid " << data.sessionid << std::endl;
}
}
connection_data& get_data_from_hdl(connection_hdl hdl) {
auto it = m_connections.find(hdl);
if (it == m_connections.end()) {
// this connection is not in the list. This really shouldn't happen
// and probably means something else is wrong.
throw std::invalid_argument("No data available for session");
}
return it->second;
}
void run(uint16_t port) {
m_server.listen(port);
m_server.start_accept();
m_server.run();
}
private:
typedef std::map<connection_hdl,connection_data,std::owner_less<connection_hdl>> con_list;
int m_next_sessionid;
server m_server;
con_list m_connections;
};
int main() {
print_server server;
server.run(9002);
}

View file

@ -0,0 +1,23 @@
## Broadcast Server example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('broadcast_server', ["broadcast_server.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system','thread'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('broadcast_server', ["broadcast_server.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,156 @@
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
#include <set>
/*#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>*/
#include <websocketpp/common/thread.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
using websocketpp::lib::thread;
using websocketpp::lib::mutex;
using websocketpp::lib::unique_lock;
using websocketpp::lib::condition_variable;
/* on_open insert connection_hdl into channel
* on_close remove connection_hdl from channel
* on_message queue send to all channels
*/
enum action_type {
SUBSCRIBE,
UNSUBSCRIBE,
MESSAGE
};
struct action {
action(action_type t, connection_hdl h) : type(t), hdl(h) {}
action(action_type t, connection_hdl h, server::message_ptr m)
: type(t), hdl(h), msg(m) {}
action_type type;
websocketpp::connection_hdl hdl;
server::message_ptr msg;
};
class broadcast_server {
public:
broadcast_server() {
// Initialize Asio Transport
m_server.init_asio();
// Register handler callbacks
m_server.set_open_handler(bind(&broadcast_server::on_open,this,::_1));
m_server.set_close_handler(bind(&broadcast_server::on_close,this,::_1));
m_server.set_message_handler(bind(&broadcast_server::on_message,this,::_1,::_2));
}
void run(uint16_t port) {
// listen on specified port
m_server.listen(port);
// Start the server accept loop
m_server.start_accept();
// Start the ASIO io_service run loop
try {
m_server.run();
} catch (const std::exception & e) {
std::cout << e.what() << std::endl;
}
}
void on_open(connection_hdl hdl) {
unique_lock<mutex> lock(m_action_lock);
//std::cout << "on_open" << std::endl;
m_actions.push(action(SUBSCRIBE,hdl));
lock.unlock();
m_action_cond.notify_one();
}
void on_close(connection_hdl hdl) {
unique_lock<mutex> lock(m_action_lock);
//std::cout << "on_close" << std::endl;
m_actions.push(action(UNSUBSCRIBE,hdl));
lock.unlock();
m_action_cond.notify_one();
}
void on_message(connection_hdl hdl, server::message_ptr msg) {
// queue message up for sending by processing thread
unique_lock<mutex> lock(m_action_lock);
//std::cout << "on_message" << std::endl;
m_actions.push(action(MESSAGE,hdl,msg));
lock.unlock();
m_action_cond.notify_one();
}
void process_messages() {
while(1) {
unique_lock<mutex> lock(m_action_lock);
while(m_actions.empty()) {
m_action_cond.wait(lock);
}
action a = m_actions.front();
m_actions.pop();
lock.unlock();
if (a.type == SUBSCRIBE) {
unique_lock<mutex> con_lock(m_connection_lock);
m_connections.insert(a.hdl);
} else if (a.type == UNSUBSCRIBE) {
unique_lock<mutex> con_lock(m_connection_lock);
m_connections.erase(a.hdl);
} else if (a.type == MESSAGE) {
unique_lock<mutex> con_lock(m_connection_lock);
con_list::iterator it;
for (it = m_connections.begin(); it != m_connections.end(); ++it) {
m_server.send(*it,a.msg);
}
} else {
// undefined.
}
}
}
private:
typedef std::set<connection_hdl,std::owner_less<connection_hdl> > con_list;
server m_server;
con_list m_connections;
std::queue<action> m_actions;
mutex m_action_lock;
mutex m_connection_lock;
condition_variable m_action_cond;
};
int main() {
try {
broadcast_server server_instance;
// Start a thread to run the processing loop
thread t(bind(&broadcast_server::process_messages,&server_instance));
// Run the asio loop with the main thread
server_instance.run(9002);
t.join();
} catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
}
}

View file

@ -0,0 +1,24 @@
## Debug client example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
Import('tls_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] + [tls_libs]
prgs += env_cpp11.Program('debug_client', ["debug_client.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system','random'],env) + [platform_libs] + [polyfill_libs] + [tls_libs]
prgs += env.Program('debug_client', ["debug_client.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,166 @@
/*
* 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.
*/
/** ====== WARNING ========
* This example is presently used as a scratch space. It may or may not be broken
* at any given time.
*/
#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/client.hpp>
#include <iostream>
#include <chrono>
typedef websocketpp::client<websocketpp::config::asio_client> client;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef websocketpp::config::asio_tls_client::message_type::ptr message_ptr;
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
typedef client::connection_ptr connection_ptr;
class perftest {
public:
typedef perftest type;
typedef std::chrono::duration<int,std::micro> dur_type;
perftest () {
m_endpoint.set_access_channels(websocketpp::log::alevel::all);
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
// Initialize ASIO
m_endpoint.init_asio();
// Register our handlers
m_endpoint.set_socket_init_handler(bind(&type::on_socket_init,this,::_1));
//m_endpoint.set_tls_init_handler(bind(&type::on_tls_init,this,::_1));
m_endpoint.set_message_handler(bind(&type::on_message,this,::_1,::_2));
m_endpoint.set_open_handler(bind(&type::on_open,this,::_1));
m_endpoint.set_close_handler(bind(&type::on_close,this,::_1));
m_endpoint.set_fail_handler(bind(&type::on_fail,this,::_1));
}
void start(std::string uri) {
websocketpp::lib::error_code ec;
client::connection_ptr con = m_endpoint.get_connection(uri, ec);
if (ec) {
m_endpoint.get_alog().write(websocketpp::log::alevel::app,ec.message());
}
//con->set_proxy("http://humupdates.uchicago.edu:8443");
m_endpoint.connect(con);
// Start the ASIO io_service run loop
m_start = std::chrono::high_resolution_clock::now();
m_endpoint.run();
}
void on_socket_init(websocketpp::connection_hdl) {
m_socket_init = std::chrono::high_resolution_clock::now();
}
context_ptr on_tls_init(websocketpp::connection_hdl) {
m_tls_init = std::chrono::high_resolution_clock::now();
context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv1);
try {
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
boost::asio::ssl::context::single_dh_use);
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
return ctx;
}
void on_fail(websocketpp::connection_hdl hdl) {
client::connection_ptr con = m_endpoint.get_con_from_hdl(hdl);
std::cout << "Fail handler" << std::endl;
std::cout << con->get_state() << std::endl;
std::cout << con->get_local_close_code() << std::endl;
std::cout << con->get_local_close_reason() << std::endl;
std::cout << con->get_remote_close_code() << std::endl;
std::cout << con->get_remote_close_reason() << std::endl;
std::cout << con->get_ec() << " - " << con->get_ec().message() << std::endl;
}
void on_open(websocketpp::connection_hdl hdl) {
m_open = std::chrono::high_resolution_clock::now();
m_endpoint.send(hdl, "", websocketpp::frame::opcode::text);
}
void on_message(websocketpp::connection_hdl hdl, message_ptr) {
m_message = std::chrono::high_resolution_clock::now();
m_endpoint.close(hdl,websocketpp::close::status::going_away,"");
}
void on_close(websocketpp::connection_hdl) {
m_close = std::chrono::high_resolution_clock::now();
std::cout << "Socket Init: " << std::chrono::duration_cast<dur_type>(m_socket_init-m_start).count() << std::endl;
std::cout << "TLS Init: " << std::chrono::duration_cast<dur_type>(m_tls_init-m_start).count() << std::endl;
std::cout << "Open: " << std::chrono::duration_cast<dur_type>(m_open-m_start).count() << std::endl;
std::cout << "Message: " << std::chrono::duration_cast<dur_type>(m_message-m_start).count() << std::endl;
std::cout << "Close: " << std::chrono::duration_cast<dur_type>(m_close-m_start).count() << std::endl;
}
private:
client m_endpoint;
std::chrono::high_resolution_clock::time_point m_start;
std::chrono::high_resolution_clock::time_point m_socket_init;
std::chrono::high_resolution_clock::time_point m_tls_init;
std::chrono::high_resolution_clock::time_point m_open;
std::chrono::high_resolution_clock::time_point m_message;
std::chrono::high_resolution_clock::time_point m_close;
};
int main(int argc, char* argv[]) {
std::string uri = "wss://echo.websocket.org";
if (argc == 2) {
uri = argv[1];
}
try {
perftest endpoint;
endpoint.start(uri);
} catch (const std::exception & e) {
std::cout << e.what() << std::endl;
} catch (websocketpp::lib::error_code e) {
std::cout << e.message() << std::endl;
} catch (...) {
std::cout << "other exception" << std::endl;
}
}

View file

@ -0,0 +1,10 @@
file (GLOB SOURCE_FILES *.cpp)
file (GLOB HEADER_FILES *.hpp)
init_target (debug_server)
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
link_boost ()
final_target ()

View file

@ -0,0 +1,23 @@
## Debug server example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('debug_server', ["debug_server.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('debug_server', ["debug_server.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,174 @@
/*
* 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.
*/
/** ====== WARNING ========
* This example is presently used as a scratch space. It may or may not be broken
* at any given time.
*/
#include <websocketpp/config/debug_asio_no_tls.hpp>
// Custom logger
#include <websocketpp/logger/syslog.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
////////////////////////////////////////////////////////////////////////////////
///////////////// Custom Config for debugging custom policies //////////////////
////////////////////////////////////////////////////////////////////////////////
struct debug_custom : public websocketpp::config::debug_asio {
typedef debug_custom type;
typedef debug_asio base;
typedef base::concurrency_type concurrency_type;
typedef base::request_type request_type;
typedef base::response_type response_type;
typedef base::message_type message_type;
typedef base::con_msg_manager_type con_msg_manager_type;
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
/// Custom Logging policies
/*typedef websocketpp::log::syslog<concurrency_type,
websocketpp::log::elevel> elog_type;
typedef websocketpp::log::syslog<concurrency_type,
websocketpp::log::alevel> alog_type;
*/
typedef base::alog_type alog_type;
typedef base::elog_type elog_type;
typedef base::rng_type rng_type;
struct transport_config : public base::transport_config {
typedef type::concurrency_type concurrency_type;
typedef type::alog_type alog_type;
typedef type::elog_type elog_type;
typedef type::request_type request_type;
typedef type::response_type response_type;
typedef websocketpp::transport::asio::basic_socket::endpoint
socket_type;
};
typedef websocketpp::transport::asio::endpoint<transport_config>
transport_type;
static const long timeout_open_handshake = 0;
};
////////////////////////////////////////////////////////////////////////////////
typedef websocketpp::server<debug_custom> server;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;
bool validate(server *, websocketpp::connection_hdl) {
//sleep(6);
return true;
}
void on_http(server* s, websocketpp::connection_hdl hdl) {
server::connection_ptr con = s->get_con_from_hdl(hdl);
std::string res = con->get_request_body();
std::stringstream ss;
ss << "got HTTP request with " << res.size() << " bytes of body data.";
con->set_body(ss.str());
con->set_status(websocketpp::http::status_code::ok);
}
void on_fail(server* s, websocketpp::connection_hdl hdl) {
server::connection_ptr con = s->get_con_from_hdl(hdl);
std::cout << "Fail handler: " << con->get_ec() << " " << con->get_ec().message() << std::endl;
}
void on_close(websocketpp::connection_hdl) {
std::cout << "Close handler" << std::endl;
}
// Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
std::cout << "on_message called with hdl: " << hdl.lock().get()
<< " and message: " << msg->get_payload()
<< std::endl;
try {
s->send(hdl, msg->get_payload(), msg->get_opcode());
} catch (const websocketpp::lib::error_code& e) {
std::cout << "Echo failed because: " << e
<< "(" << e.message() << ")" << std::endl;
}
}
int main() {
// Create a server endpoint
server echo_server;
try {
// Set logging settings
echo_server.set_access_channels(websocketpp::log::alevel::all);
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
// Initialize ASIO
echo_server.init_asio();
echo_server.set_reuse_addr(true);
// Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
echo_server.set_http_handler(bind(&on_http,&echo_server,::_1));
echo_server.set_fail_handler(bind(&on_fail,&echo_server,::_1));
echo_server.set_close_handler(&on_close);
echo_server.set_validate_handler(bind(&validate,&echo_server,::_1));
// Listen on port 9012
echo_server.listen(9012);
// Start the server accept loop
echo_server.start_accept();
// Start the ASIO io_service run loop
echo_server.run();
} catch (const std::exception & e) {
std::cout << e.what() << std::endl;
} catch (websocketpp::lib::error_code e) {
std::cout << e.message() << std::endl;
} catch (...) {
std::cout << "other exception" << std::endl;
}
}

View file

@ -0,0 +1,18 @@
## Main development example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
env_cpp11 = env_cpp11.Clone ()
prgs = []
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
BOOST_LIBS_CPP11 = boostlibs(['unit_test_framework','system','timer','chrono'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('main', ["main.cpp"], LIBS = BOOST_LIBS_CPP11)
Return('prgs')

View file

@ -0,0 +1,200 @@
//#ifndef _WEBSOCKETPP_CPP11_STL_
// #define _WEBSOCKETPP_CPP11_STL_
//#endif
#include <random>
#include <boost/timer/timer.hpp>
#include <websocketpp/config/core.hpp>
//#include <websocketpp/security/none.hpp>
//#include <websocketpp/concurrency/none.hpp>
//#include <websocketpp/concurrency/stl.hpp>
//#include <websocketpp/transport/iostream.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
#include <sstream>
//typedef websocketpp::concurrency::stl concurrency;
//typedef websocketpp::transport::iostream<concurrency> transport;
//typedef websocketpp::server<concurrency,transport> server;
typedef websocketpp::server<websocketpp::config::core> server;
/*class handler : public server::handler {
bool validate(connection_ptr con) {
std::cout << "handler validate" << std::endl;
if (con->get_origin() != "http://www.example.com") {
con->set_status(websocketpp::http::status_code::FORBIDDEN);
return false;
}
return true;
}
void http(connection_ptr con) {
std::cout << "handler http" << std::endl;
}
void on_load(connection_ptr con, ptr old_handler) {
std::cout << "handler on_load" << std::endl;
}
void on_unload(connection_ptr con, ptr new_handler) {
std::cout << "handler on_unload" << std::endl;
}
void on_open(connection_ptr con) {
std::cout << "handler on_open" << std::endl;
}
void on_fail(connection_ptr con) {
std::cout << "handler on_fail" << std::endl;
}
void on_message(connection_ptr con, message_ptr msg) {
std::cout << "handler on_message" << std::endl;
}
void on_close(connection_ptr con) {
std::cout << "handler on_close" << std::endl;
}
};*/
int main() {
typedef websocketpp::message_buffer::message<websocketpp::message_buffer::alloc::con_msg_manager>
message_type;
typedef websocketpp::message_buffer::alloc::con_msg_manager<message_type>
con_msg_man_type;
con_msg_man_type::ptr manager = websocketpp::lib::make_shared<con_msg_man_type>();
size_t foo = 1024;
message_type::ptr input = manager->get_message(websocketpp::frame::opcode::TEXT,foo);
message_type::ptr output = manager->get_message(websocketpp::frame::opcode::TEXT,foo);
websocketpp::frame::masking_key_type key;
std::random_device dev;
key.i = 0x12345678;
double m = 18094238402394.0824923;
/*std::cout << "Some Math" << std::endl;
{
boost::timer::auto_cpu_timer t;
for (int i = 0; i < foo; i++) {
m /= 1.001;
}
}*/
std::cout << m << std::endl;
std::cout << "Random Gen" << std::endl;
{
boost::timer::auto_cpu_timer t;
input->get_raw_payload().replace(0,foo,foo,'\0');
output->get_raw_payload().replace(0,foo,foo,'\0');
}
std::cout << "Out of place accelerated" << std::endl;
{
boost::timer::auto_cpu_timer t;
websocketpp::frame::word_mask_exact(reinterpret_cast<uint8_t*>(const_cast<char*>(input->get_raw_payload().data())), reinterpret_cast<uint8_t*>(const_cast<char*>(output->get_raw_payload().data())), foo, key);
}
std::cout << websocketpp::utility::to_hex(input->get_payload().c_str(),20) << std::endl;
std::cout << websocketpp::utility::to_hex(output->get_payload().c_str(),20) << std::endl;
input->get_raw_payload().replace(0,foo,foo,'\0');
output->get_raw_payload().replace(0,foo,foo,'\0');
std::cout << "In place accelerated" << std::endl;
{
boost::timer::auto_cpu_timer t;
websocketpp::frame::word_mask_exact(reinterpret_cast<uint8_t*>(const_cast<char*>(input->get_raw_payload().data())), reinterpret_cast<uint8_t*>(const_cast<char*>(input->get_raw_payload().data())), foo, key);
}
std::cout << websocketpp::utility::to_hex(input->get_payload().c_str(),20) << std::endl;
std::cout << websocketpp::utility::to_hex(output->get_payload().c_str(),20) << std::endl;
input->get_raw_payload().replace(0,foo,foo,'\0');
output->get_raw_payload().replace(0,foo,foo,'\0');
std::cout << "Out of place byte by byte" << std::endl;
{
boost::timer::auto_cpu_timer t;
websocketpp::frame::byte_mask(input->get_raw_payload().begin(), input->get_raw_payload().end(), output->get_raw_payload().begin(), key);
}
std::cout << websocketpp::utility::to_hex(input->get_payload().c_str(),20) << std::endl;
std::cout << websocketpp::utility::to_hex(output->get_payload().c_str(),20) << std::endl;
input->get_raw_payload().replace(0,foo,foo,'\0');
output->get_raw_payload().replace(0,foo,foo,'\0');
std::cout << "In place byte by byte" << std::endl;
{
boost::timer::auto_cpu_timer t;
websocketpp::frame::byte_mask(input->get_raw_payload().begin(), input->get_raw_payload().end(), input->get_raw_payload().begin(), key);
}
std::cout << websocketpp::utility::to_hex(input->get_payload().c_str(),20) << std::endl;
std::cout << websocketpp::utility::to_hex(output->get_payload().c_str(),20) << std::endl;
input->get_raw_payload().replace(0,foo,foo,'a');
output->get_raw_payload().replace(0,foo,foo,'b');
std::cout << "Copy" << std::endl;
{
boost::timer::auto_cpu_timer t;
std::copy(input->get_raw_payload().begin(), input->get_raw_payload().end(), output->get_raw_payload().begin());
}
std::cout << websocketpp::utility::to_hex(input->get_payload().c_str(),20) << std::endl;
std::cout << websocketpp::utility::to_hex(output->get_payload().c_str(),20) << std::endl;
/*server::handler::ptr h(new handler());
server test_server(h);
server::connection_ptr con;
std::stringstream output;
test_server.register_ostream(&output);
con = test_server.get_connection();
con->start();
//foo.handle_accept(con,true);
std::stringstream input;
input << "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
//input << "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n";
input >> *con;
std::stringstream input2;
input2 << "messageabc2";
input2 >> *con;
std::stringstream input3;
input3 << "messageabc3";
input3 >> *con;
std::stringstream input4;
input4 << "close";
input4 >> *con;
std::cout << "connection output:" << std::endl;
std::cout << output.str() << std::endl;*/
}

View file

@ -0,0 +1,10 @@
file (GLOB SOURCE_FILES *.cpp)
file (GLOB HEADER_FILES *.hpp)
init_target (echo_server)
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
link_boost ()
final_target ()

View file

@ -0,0 +1,23 @@
## Main development example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('echo_server', ["echo_server.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('echo_server', ["echo_server.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2012, 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.
*
*/
#ifndef WEBSOCKETPP_ECHO_SERVER_HANDLER_HPP
#define WEBSOCKETPP_ECHO_SERVER_HANDLER_HPP
class echo_handler : public server::handler {
void on_message(connection_ptr con, std::string msg) {
con->write(msg);
}
};
#endif // WEBSOCKETPP_ECHO_SERVER_HANDLER_HPP

View file

@ -0,0 +1,58 @@
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;
// Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
std::cout << "on_message called with hdl: " << hdl.lock().get()
<< " and message: " << msg->get_payload()
<< std::endl;
try {
s->send(hdl, msg->get_payload(), msg->get_opcode());
} catch (const websocketpp::lib::error_code& e) {
std::cout << "Echo failed because: " << e
<< "(" << e.message() << ")" << std::endl;
}
}
int main() {
// Create a server endpoint
server echo_server;
try {
// Set logging settings
echo_server.set_access_channels(websocketpp::log::alevel::all);
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
// Initialize ASIO
echo_server.init_asio();
// Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
// Listen on port 9002
echo_server.listen(9002);
// Start the server accept loop
echo_server.start_accept();
// Start the ASIO io_service run loop
echo_server.run();
} catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
} catch (...) {
std::cout << "other exception" << std::endl;
}
}

View file

@ -0,0 +1,15 @@
file (GLOB SOURCE_FILES *.cpp)
file (GLOB HEADER_FILES *.hpp)
if (OPENSSL_FOUND)
init_target (echo_server_both)
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
link_boost ()
link_openssl()
final_target ()
endif()

View file

@ -0,0 +1,24 @@
## Combo plain+tls echo server
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
Import('tls_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] + [tls_libs]
prgs += env_cpp11.Program('echo_server_both', ["echo_server_both.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs] + [tls_libs]
prgs += env.Program('echo_server_both', ["echo_server_both.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,87 @@
#include <websocketpp/config/asio.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
// define types for two different server endpoints, one for each config we are
// using
typedef websocketpp::server<websocketpp::config::asio> server_plain;
typedef websocketpp::server<websocketpp::config::asio_tls> server_tls;
// alias some of the bind related functions as they are a bit long
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// type of the ssl context pointer is long so alias it
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
// The shared on_message handler takes a template parameter so the function can
// resolve any endpoint dependent types like message_ptr or connection_ptr
template <typename EndpointType>
void on_message(EndpointType* s, websocketpp::connection_hdl hdl,
typename EndpointType::message_ptr msg)
{
std::cout << "on_message called with hdl: " << hdl.lock().get()
<< " and message: " << msg->get_payload()
<< std::endl;
try {
s->send(hdl, msg->get_payload(), msg->get_opcode());
} catch (const websocketpp::lib::error_code& e) {
std::cout << "Echo failed because: " << e
<< "(" << e.message() << ")" << std::endl;
}
}
// No change to TLS init methods from echo_server_tls
std::string get_password() {
return "test";
}
context_ptr on_tls_init(websocketpp::connection_hdl hdl) {
std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl;
context_ptr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1));
try {
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
boost::asio::ssl::context::single_dh_use);
ctx->set_password_callback(bind(&get_password));
ctx->use_certificate_chain_file("server.pem");
ctx->use_private_key_file("server.pem", boost::asio::ssl::context::pem);
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
return ctx;
}
int main() {
// set up an external io_service to run both endpoints on. This is not
// strictly necessary, but simplifies thread management a bit.
boost::asio::io_service ios;
// set up plain endpoint
server_plain endpoint_plain;
// initialize asio with our external io_service rather than an internal one
endpoint_plain.init_asio(&ios);
endpoint_plain.set_message_handler(
bind(&on_message<server_plain>,&endpoint_plain,::_1,::_2));
endpoint_plain.listen(80);
endpoint_plain.start_accept();
// set up tls endpoint
server_tls endpoint_tls;
endpoint_tls.init_asio(&ios);
endpoint_tls.set_message_handler(
bind(&on_message<server_tls>,&endpoint_tls,::_1,::_2));
// TLS endpoint has an extra handler for the tls init
endpoint_tls.set_tls_init_handler(bind(&on_tls_init,::_1));
// tls endpoint listens on a different port
endpoint_tls.listen(443);
endpoint_tls.start_accept();
// Start the ASIO io_service run loop running both endpoints
ios.run();
}

View file

@ -0,0 +1,58 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,A0ED66EF872A48A9
gXuvKojXzApVhhPVNdRliiajbC4PtwQG5c8TA7JADLgwOR7o9t6KtXEr37bDRpvB
9aO9P+SJaK5OOp3XKPGthOdqv+tvCRTlmzmC8GjPLBX389DWT2xoGu7JkGwDtdSm
rnF49Rlp5bfjpACk5xKNiKeDo1CWfeEJzw9Kto0g+5eMaEdors64oPzjXs3geA2g
TxCJSHv9qSX6++pCLKKCUTbyzidAxV/Zb0AAubt5V40QKqX4HhSwwstFnTaX3tlb
3QOdY+y04VIkM6d7qN5W8M7NzRkMpZ1qBpQcUMpkhQcRzWP2wub5AAff9D2GntRd
4Dz1vn3u41U3Okdr0CNj+iH7byCzuokoAhk6ZQEN6WB+GTpGgfBXdtUZrfpb0MKm
UNYP5AF2AmUqJRXhViTDVtu/V2tHF3LGuNT+W2Dz+spFZEq0byEO0N858eR0dikc
6jOASvNQbSwD0+mkgBC1gXKKU3ngj2gpJUwljeACdWFd8N2egrZfyI05CmX7vPNC
NXbs7k2buWNdjP4/D8IM+HDVidWzQa/kG/qokXKqllem9Egg37lUucwnP3cX2/Hw
U2mfaBWzeZtqc+GqRp08rYIql+Reai3sUYlQMnNk01prVY47UQb+dxuqjaxGV5Xx
Xkx0s2mfQnNRjL4S7Hjhqelufi6GpkCQ2EGsPpA+6K1ztZ0ame9Q2BE1SXeM/6vU
rxT5nRrCxueyXAyQSGcqMX9//gSeK8WWBqG/c1IAMVDa0NWrJeOJhSziE+ta3B0m
bHAPBY6vh0iB3lLdRlbUOPbC6R1TpxMOs+6Vbs2+OTifFpvOVymEoZq/nroyg68P
vn5uCKogwWA7o8EArf/UTlIwWJmH9bgILdZKld4wMel2HQg16RDzm+mEXAJi52a/
FC+fgfphdxltmUJ+rqOyR4AHULjaTWUQqTIB6sdlzgmES1nXAiE71zX//KFqomar
O60SPPk3C1bs0x5DsvmGJa8SIfDhyd+D7NPyqwEKqrZsaotYGklNkfqxa6pa8mrc
ejxquW1PK4FvBk26+osu5a90Jih0PcQM7DUMMr2WHdTiMSXWAiK2ToYF8Itt25Qv
Cd0CsSYw9CJkXNr1u1+mObheaY9QYOmztnSJLy4ZO2JsMhqNwuAueIcwmhXOREq7
kzlnGMgJcuSeAS/OBNj8Zgx0c7QQ0kzc+YmnOCsqoMtPsu/CsXJ4iJiM3Tki/2jT
bywrTiQwE6R3a/87GREOREX+WLicZBWX3k9/4tBL5XSe1p5wPpuIRQUDvAGNfNHP
JN7kujDF4SehilF1qtvCygAwvxHFDj+EwhXKNDKJzoZZIM15rAk3k92n2j6nz1qH
a3xOU05yydOlO6F6w51I1QoDddmkzCRNB0TeO3D6rekHsCK1aDWmC+qRcm2ZFtVz
sY6fdZN2NEmMQokIh9Opi1f8CSYSizPESMzdu2SF0xVO9n/IGIkn1ksK04O2BZo0
X3LBPHLfCRsQNY1eF17bj07fYU2oPZKs/XzJiwxkqK6LFvpeAVaYrtg9fqRO/UVe
QhUIj3BL550ocEpa15xLehLrmwzYiW5zwGjSHQ4EgZluGLCwyKGTh4QswEJRA9Rt
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIE0DCCA7igAwIBAgIJAM5MuKJezXq0MA0GCSqGSIb3DQEBBQUAMIGgMQswCQYD
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xGDAW
BgNVBAoTD1phcGhveWQgU3R1ZGlvczEUMBIGA1UECxMLV2ViU29ja2V0KysxFjAU
BgNVBAMTDVBldGVyIFRob3Jzb24xJDAiBgkqhkiG9w0BCQEWFXdlYm1hc3RlckB6
YXBob3lkLmNvbTAeFw0xMTExMTUyMTIwMDZaFw0xMjExMTQyMTIwMDZaMIGgMQsw
CQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28x
GDAWBgNVBAoTD1phcGhveWQgU3R1ZGlvczEUMBIGA1UECxMLV2ViU29ja2V0Kysx
FjAUBgNVBAMTDVBldGVyIFRob3Jzb24xJDAiBgkqhkiG9w0BCQEWFXdlYm1hc3Rl
ckB6YXBob3lkLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANR0
tdwAnIB8I9qRZ7QbzEWY95RpM7GIn0u/9oH90PzdHiE0rXSkKT+yw3XUzH0iw5t0
5dEwSC+srSP5Vm4cA6kXc94agVVaPW89tGcdP4fHptCruSrzQsDXELCPl5UUvMpA
YUcGisdXYPN/EeOoqb9wKWxoW5mREsyyeWWS89fYN5qU/d0QpbSvEWghqLbL/ZS2
hOlXT9LufOeA+vHiV1/T/h5xC7ecIH02YDQw1EnqxbPmkLPcWThztLS9FiufNDRM
Rhcoaj2b9VDHvDwdbeA0T5v5qNdG34LaapYOelxzQMOtM0f9Dgqehodyxl2qm9mR
lq432dlOEzDnVCPNHwECAwEAAaOCAQkwggEFMB0GA1UdDgQWBBTTPKfNMnKOykhv
+vKS7vql5JsMyzCB1QYDVR0jBIHNMIHKgBTTPKfNMnKOykhv+vKS7vql5JsMy6GB
pqSBozCBoDELMAkGA1UEBhMCVVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQH
EwdDaGljYWdvMRgwFgYDVQQKEw9aYXBob3lkIFN0dWRpb3MxFDASBgNVBAsTC1dl
YlNvY2tldCsrMRYwFAYDVQQDEw1QZXRlciBUaG9yc29uMSQwIgYJKoZIhvcNAQkB
FhV3ZWJtYXN0ZXJAemFwaG95ZC5jb22CCQDOTLiiXs16tDAMBgNVHRMEBTADAQH/
MA0GCSqGSIb3DQEBBQUAA4IBAQB+SH0s/hrv5VYqgX6SNLzxdSLvCVsUkCdTpxwY
wOJ84XmYcXDMhKDtZqLtOtN6pfEwVusFlC9mkieuunztCnWNmsSG83RuljJPjFSi
1d4Id4bKEQkQ4cfnjoHKivRrViWLnxuNnLzC6tpyGH/35kKWhhr6T58AXerFgVw3
mHvLPTr1DuhdAZA0ZuvuseVAFFAjI3RetSySwHJE3ak8KswDVfLi6E3XxMVsIWTS
/iFsC2WwoZQlljya2V/kRYIhu+uCdqJ01wunn2BvmURPSgr4GTBF0FQ9JGpNbXxM
TAU7oQJgyFc5sCcuEgPTO0dWVQTvdZVgay4tkmduKDRkmJBF
-----END CERTIFICATE-----

View file

@ -0,0 +1,15 @@
file (GLOB SOURCE_FILES *.cpp)
file (GLOB HEADER_FILES *.hpp)
if (OPENSSL_FOUND)
init_target (echo_server_tls)
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
link_boost ()
link_openssl()
final_target ()
endif()

View file

@ -0,0 +1,24 @@
## Main development example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
Import('tls_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] + [tls_libs]
prgs += env_cpp11.Program('echo_server_tls', ["echo_server_tls.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs] + [tls_libs]
prgs += env.Program('echo_server_tls', ["echo_server_tls.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,73 @@
#include <websocketpp/config/asio.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
typedef websocketpp::server<websocketpp::config::asio_tls> server;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef websocketpp::config::asio::message_type::ptr message_ptr;
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
std::cout << "on_message called with hdl: " << hdl.lock().get()
<< " and message: " << msg->get_payload()
<< std::endl;
try {
s->send(hdl, msg->get_payload(), msg->get_opcode());
} catch (const websocketpp::lib::error_code& e) {
std::cout << "Echo failed because: " << e
<< "(" << e.message() << ")" << std::endl;
}
}
std::string get_password() {
return "test";
}
context_ptr on_tls_init(websocketpp::connection_hdl hdl) {
std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl;
context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv1);
try {
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
boost::asio::ssl::context::single_dh_use);
ctx->set_password_callback(bind(&get_password));
ctx->use_certificate_chain_file("server.pem");
ctx->use_private_key_file("server.pem", boost::asio::ssl::context::pem);
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
return ctx;
}
int main() {
// Create a server endpoint
server echo_server;
// Initialize ASIO
echo_server.init_asio();
// Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
echo_server.set_tls_init_handler(bind(&on_tls_init,::_1));
// Listen on port 9002
echo_server.listen(9002);
// Start the server accept loop
echo_server.start_accept();
// Start the ASIO io_service run loop
echo_server.run();
}

View file

@ -0,0 +1,58 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,A0ED66EF872A48A9
gXuvKojXzApVhhPVNdRliiajbC4PtwQG5c8TA7JADLgwOR7o9t6KtXEr37bDRpvB
9aO9P+SJaK5OOp3XKPGthOdqv+tvCRTlmzmC8GjPLBX389DWT2xoGu7JkGwDtdSm
rnF49Rlp5bfjpACk5xKNiKeDo1CWfeEJzw9Kto0g+5eMaEdors64oPzjXs3geA2g
TxCJSHv9qSX6++pCLKKCUTbyzidAxV/Zb0AAubt5V40QKqX4HhSwwstFnTaX3tlb
3QOdY+y04VIkM6d7qN5W8M7NzRkMpZ1qBpQcUMpkhQcRzWP2wub5AAff9D2GntRd
4Dz1vn3u41U3Okdr0CNj+iH7byCzuokoAhk6ZQEN6WB+GTpGgfBXdtUZrfpb0MKm
UNYP5AF2AmUqJRXhViTDVtu/V2tHF3LGuNT+W2Dz+spFZEq0byEO0N858eR0dikc
6jOASvNQbSwD0+mkgBC1gXKKU3ngj2gpJUwljeACdWFd8N2egrZfyI05CmX7vPNC
NXbs7k2buWNdjP4/D8IM+HDVidWzQa/kG/qokXKqllem9Egg37lUucwnP3cX2/Hw
U2mfaBWzeZtqc+GqRp08rYIql+Reai3sUYlQMnNk01prVY47UQb+dxuqjaxGV5Xx
Xkx0s2mfQnNRjL4S7Hjhqelufi6GpkCQ2EGsPpA+6K1ztZ0ame9Q2BE1SXeM/6vU
rxT5nRrCxueyXAyQSGcqMX9//gSeK8WWBqG/c1IAMVDa0NWrJeOJhSziE+ta3B0m
bHAPBY6vh0iB3lLdRlbUOPbC6R1TpxMOs+6Vbs2+OTifFpvOVymEoZq/nroyg68P
vn5uCKogwWA7o8EArf/UTlIwWJmH9bgILdZKld4wMel2HQg16RDzm+mEXAJi52a/
FC+fgfphdxltmUJ+rqOyR4AHULjaTWUQqTIB6sdlzgmES1nXAiE71zX//KFqomar
O60SPPk3C1bs0x5DsvmGJa8SIfDhyd+D7NPyqwEKqrZsaotYGklNkfqxa6pa8mrc
ejxquW1PK4FvBk26+osu5a90Jih0PcQM7DUMMr2WHdTiMSXWAiK2ToYF8Itt25Qv
Cd0CsSYw9CJkXNr1u1+mObheaY9QYOmztnSJLy4ZO2JsMhqNwuAueIcwmhXOREq7
kzlnGMgJcuSeAS/OBNj8Zgx0c7QQ0kzc+YmnOCsqoMtPsu/CsXJ4iJiM3Tki/2jT
bywrTiQwE6R3a/87GREOREX+WLicZBWX3k9/4tBL5XSe1p5wPpuIRQUDvAGNfNHP
JN7kujDF4SehilF1qtvCygAwvxHFDj+EwhXKNDKJzoZZIM15rAk3k92n2j6nz1qH
a3xOU05yydOlO6F6w51I1QoDddmkzCRNB0TeO3D6rekHsCK1aDWmC+qRcm2ZFtVz
sY6fdZN2NEmMQokIh9Opi1f8CSYSizPESMzdu2SF0xVO9n/IGIkn1ksK04O2BZo0
X3LBPHLfCRsQNY1eF17bj07fYU2oPZKs/XzJiwxkqK6LFvpeAVaYrtg9fqRO/UVe
QhUIj3BL550ocEpa15xLehLrmwzYiW5zwGjSHQ4EgZluGLCwyKGTh4QswEJRA9Rt
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIE0DCCA7igAwIBAgIJAM5MuKJezXq0MA0GCSqGSIb3DQEBBQUAMIGgMQswCQYD
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xGDAW
BgNVBAoTD1phcGhveWQgU3R1ZGlvczEUMBIGA1UECxMLV2ViU29ja2V0KysxFjAU
BgNVBAMTDVBldGVyIFRob3Jzb24xJDAiBgkqhkiG9w0BCQEWFXdlYm1hc3RlckB6
YXBob3lkLmNvbTAeFw0xMTExMTUyMTIwMDZaFw0xMjExMTQyMTIwMDZaMIGgMQsw
CQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28x
GDAWBgNVBAoTD1phcGhveWQgU3R1ZGlvczEUMBIGA1UECxMLV2ViU29ja2V0Kysx
FjAUBgNVBAMTDVBldGVyIFRob3Jzb24xJDAiBgkqhkiG9w0BCQEWFXdlYm1hc3Rl
ckB6YXBob3lkLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANR0
tdwAnIB8I9qRZ7QbzEWY95RpM7GIn0u/9oH90PzdHiE0rXSkKT+yw3XUzH0iw5t0
5dEwSC+srSP5Vm4cA6kXc94agVVaPW89tGcdP4fHptCruSrzQsDXELCPl5UUvMpA
YUcGisdXYPN/EeOoqb9wKWxoW5mREsyyeWWS89fYN5qU/d0QpbSvEWghqLbL/ZS2
hOlXT9LufOeA+vHiV1/T/h5xC7ecIH02YDQw1EnqxbPmkLPcWThztLS9FiufNDRM
Rhcoaj2b9VDHvDwdbeA0T5v5qNdG34LaapYOelxzQMOtM0f9Dgqehodyxl2qm9mR
lq432dlOEzDnVCPNHwECAwEAAaOCAQkwggEFMB0GA1UdDgQWBBTTPKfNMnKOykhv
+vKS7vql5JsMyzCB1QYDVR0jBIHNMIHKgBTTPKfNMnKOykhv+vKS7vql5JsMy6GB
pqSBozCBoDELMAkGA1UEBhMCVVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQH
EwdDaGljYWdvMRgwFgYDVQQKEw9aYXBob3lkIFN0dWRpb3MxFDASBgNVBAsTC1dl
YlNvY2tldCsrMRYwFAYDVQQDEw1QZXRlciBUaG9yc29uMSQwIgYJKoZIhvcNAQkB
FhV3ZWJtYXN0ZXJAemFwaG95ZC5jb22CCQDOTLiiXs16tDAMBgNVHRMEBTADAQH/
MA0GCSqGSIb3DQEBBQUAA4IBAQB+SH0s/hrv5VYqgX6SNLzxdSLvCVsUkCdTpxwY
wOJ84XmYcXDMhKDtZqLtOtN6pfEwVusFlC9mkieuunztCnWNmsSG83RuljJPjFSi
1d4Id4bKEQkQ4cfnjoHKivRrViWLnxuNnLzC6tpyGH/35kKWhhr6T58AXerFgVw3
mHvLPTr1DuhdAZA0ZuvuseVAFFAjI3RetSySwHJE3ak8KswDVfLi6E3XxMVsIWTS
/iFsC2WwoZQlljya2V/kRYIhu+uCdqJ01wunn2BvmURPSgr4GTBF0FQ9JGpNbXxM
TAU7oQJgyFc5sCcuEgPTO0dWVQTvdZVgay4tkmduKDRkmJBF
-----END CERTIFICATE-----

View file

@ -0,0 +1,87 @@
#include <iostream>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
struct connection_data {
int sessionid;
std::string name;
};
struct custom_config : public websocketpp::config::asio {
// pull default settings from our core config
typedef websocketpp::config::asio core;
typedef core::concurrency_type concurrency_type;
typedef core::request_type request_type;
typedef core::response_type response_type;
typedef core::message_type message_type;
typedef core::con_msg_manager_type con_msg_manager_type;
typedef core::endpoint_msg_manager_type endpoint_msg_manager_type;
typedef core::alog_type alog_type;
typedef core::elog_type elog_type;
typedef core::rng_type rng_type;
typedef core::transport_type transport_type;
typedef core::endpoint_base endpoint_base;
// Set a custom connection_base class
typedef connection_data connection_base;
};
typedef websocketpp::server<custom_config> server;
typedef server::connection_ptr connection_ptr;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
class print_server {
public:
print_server() : m_next_sessionid(1) {
m_server.init_asio();
m_server.set_open_handler(bind(&print_server::on_open,this,::_1));
m_server.set_close_handler(bind(&print_server::on_close,this,::_1));
m_server.set_message_handler(bind(&print_server::on_message,this,::_1,::_2));
}
void on_open(connection_hdl hdl) {
connection_ptr con = m_server.get_con_from_hdl(hdl);
con->sessionid = m_next_sessionid++;
}
void on_close(connection_hdl hdl) {
connection_ptr con = m_server.get_con_from_hdl(hdl);
std::cout << "Closing connection " << con->name
<< " with sessionid " << con->sessionid << std::endl;
}
void on_message(connection_hdl hdl, server::message_ptr msg) {
connection_ptr con = m_server.get_con_from_hdl(hdl);
if (con->name == "") {
con->name = msg->get_payload();
std::cout << "Setting name of connection with sessionid "
<< con->sessionid << " to " << con->name << std::endl;
} else {
std::cout << "Got a message from connection " << con->name
<< " with sessionid " << con->sessionid << std::endl;
}
}
void run(uint16_t port) {
m_server.listen(port);
m_server.start_accept();
m_server.run();
}
private:
int m_next_sessionid;
server m_server;
};
int main() {
print_server server;
server.run(9002);
}

View file

@ -0,0 +1,42 @@
#include <iostream>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
using websocketpp::lib::ref;
void custom_on_msg(server & s, connection_hdl hdl, server::message_ptr msg) {
std::cout << "Message sent to custom handler" << std::endl;
}
void default_on_msg(server & s, connection_hdl hdl, server::message_ptr msg) {
std::cout << "Message sent to default handler" << std::endl;
if (msg->get_payload() == "upgrade") {
// Upgrade our connection_hdl to a full connection_ptr
server::connection_ptr con = s.get_con_from_hdl(hdl);
// Change the on message handler for this connection only to
// custom_on_mesage
con->set_message_handler(bind(&custom_on_msg,ref(s),::_1,::_2));
std::cout << "Upgrading connection to custom handler" << std::endl;
}
}
int main() {
server s;
s.set_message_handler(bind(&default_on_msg,ref(s),::_1,::_2));
s.init_asio();
s.listen(9002);
s.start_accept();
s.run();
}

View file

@ -0,0 +1,23 @@
## iostream server example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('iostream_server', ["iostream_server.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('iostream_server', ["iostream_server.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,89 @@
#include <websocketpp/config/core.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
#include <fstream>
typedef websocketpp::server<websocketpp::config::core> server;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;
// Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
if (msg->get_opcode() == websocketpp::frame::opcode::text) {
s->get_alog().write(websocketpp::log::alevel::app,
"Text Message Received: "+msg->get_payload());
} else {
s->get_alog().write(websocketpp::log::alevel::app,
"Binary Message Received: "+websocketpp::utility::to_hex(msg->get_payload()));
}
try {
s->send(hdl, msg->get_payload(), msg->get_opcode());
} catch (const websocketpp::lib::error_code& e) {
s->get_alog().write(websocketpp::log::alevel::app,
"Echo Failed: "+e.message());
}
}
int main() {
server s;
std::ofstream log;
try {
// set up access channels to only log interesting things
s.clear_access_channels(websocketpp::log::alevel::all);
s.set_access_channels(websocketpp::log::alevel::connect);
s.set_access_channels(websocketpp::log::alevel::disconnect);
s.set_access_channels(websocketpp::log::alevel::app);
// Log to a file rather than stdout, as we are using stdout for real
// output
log.open("output.log");
s.get_alog().set_ostream(&log);
s.get_elog().set_ostream(&log);
// print all output to stdout
s.register_ostream(&std::cout);
// Register our message handler
s.set_message_handler(bind(&on_message,&s,::_1,::_2));
server::connection_ptr con = s.get_connection();
con->start();
// C++ iostream's don't support the idea of asynchronous i/o. As such
// there are two input strategies demonstrated here. Buffered I/O will
// read from stdin in chunks until EOF. This works very well for
// replaying canned connections as would be done in automated testing.
//
// If the server is being used live however, assuming input is being
// piped from elsewhere in realtime, this strategy will result in small
// messages being buffered forever. The non-buffered strategy below
// reads characters from stdin one at a time. This is inefficient and
// for more serious uses should be replaced with a platform specific
// asyncronous i/o technique like select, poll, IOCP, etc
bool buffered_io = false;
if (buffered_io) {
std::cin >> *con;
con->eof();
} else {
char a;
while(std::cin.get(a)) {
con->read_some(&a,1);
}
con->eof();
}
} catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
}
log.close();
}

View file

@ -0,0 +1,10 @@
file (GLOB SOURCE_FILES *.cpp)
file (GLOB HEADER_FILES *.hpp)
init_target (print_server)
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
link_boost ()
final_target ()

View file

@ -0,0 +1,23 @@
## Print server example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('print_server', ["print_server.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('print_server', ["print_server.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,22 @@
#include <iostream>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
void on_message(websocketpp::connection_hdl, server::message_ptr msg) {
std::cout << msg->get_payload() << std::endl;
}
int main() {
server print_server;
print_server.set_message_handler(&on_message);
print_server.init_asio();
print_server.listen(9002);
print_server.start_accept();
print_server.run();
}

View file

@ -0,0 +1,51 @@
#include <set>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
class broadcast_server {
public:
broadcast_server() {
m_server.init_asio();
m_server.set_open_handler(bind(&broadcast_server::on_open,this,::_1));
m_server.set_close_handler(bind(&broadcast_server::on_close,this,::_1));
m_server.set_message_handler(bind(&broadcast_server::on_message,this,::_1,::_2));
}
void on_open(connection_hdl hdl) {
m_connections.insert(hdl);
}
void on_close(connection_hdl hdl) {
m_connections.erase(hdl);
}
void on_message(connection_hdl hdl, server::message_ptr msg) {
for (auto it : m_connections) {
m_server.send(it,msg);
}
}
void run(uint16_t port) {
m_server.listen(port);
m_server.start_accept();
m_server.run();
}
private:
typedef std::set<connection_hdl,std::owner_less<connection_hdl>> con_list;
server m_server;
con_list m_connections;
};
int main() {
broadcast_server server;
server.run(9002);
}

View file

@ -0,0 +1,11 @@
file (GLOB SOURCE_FILES *.cpp)
file (GLOB HEADER_FILES *.hpp)
init_target (sip_client)
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
link_boost ()
final_target ()

View file

@ -0,0 +1,22 @@
Checkout the project from git
At the top level, run cmake:
cmake -G 'Unix Makefiles' \
-D BUILD_EXAMPLES=ON \
-D WEBSOCKETPP_ROOT=/tmp/cm1 \
-D ENABLE_CPP11=OFF .
and then make the example:
make -C examples/sip_client
Now run it:
bin/sip_client ws://ws-server:80
It has been tested against the repro SIP proxy from reSIProcate
http://www.resiprocate.org/WebRTC_and_SIP_Over_WebSockets

View file

@ -0,0 +1,23 @@
## SIP client example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is avaliable build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('sip_client', ["sip_client.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system','random'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('sip_client', ["sip_client.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,84 @@
#include <condition_variable>
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>
#include <iostream>
#include <boost/thread/thread.hpp>
typedef websocketpp::client<websocketpp::config::asio_client> client;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef websocketpp::config::asio_client::message_type::ptr message_ptr;
// Create a server endpoint
client sip_client;
bool received;
void on_open(client* c, websocketpp::connection_hdl hdl) {
// now it is safe to use the connection
std::cout << "connection ready" << std::endl;
received=false;
// Send a SIP OPTIONS message to the server:
std::string SIP_msg="OPTIONS sip:carol@chicago.com SIP/2.0\r\nVia: SIP/2.0/WS df7jal23ls0d.invalid;rport;branch=z9hG4bKhjhs8ass877\r\nMax-Forwards: 70\r\nTo: <sip:carol@chicago.com>\r\nFrom: Alice <sip:alice@atlanta.com>;tag=1928301774\r\nCall-ID: a84b4c76e66710\r\nCSeq: 63104 OPTIONS\r\nContact: <sip:alice@pc33.atlanta.com>\r\nAccept: application/sdp\r\nContent-Length: 0\r\n\r\n";
sip_client.send(hdl, SIP_msg.c_str(), websocketpp::frame::opcode::text);
}
void on_message(client* c, websocketpp::connection_hdl hdl, message_ptr msg) {
client::connection_ptr con = sip_client.get_con_from_hdl(hdl);
std::cout << "Received a reply:" << std::endl;
fwrite(msg->get_payload().c_str(), msg->get_payload().size(), 1, stdout);
received=true;
}
int main(int argc, char* argv[]) {
std::string uri = "ws://localhost:9001";
if (argc == 2) {
uri = argv[1];
}
try {
// We expect there to be a lot of errors, so suppress them
sip_client.clear_access_channels(websocketpp::log::alevel::all);
sip_client.clear_error_channels(websocketpp::log::elevel::all);
// Initialize ASIO
sip_client.init_asio();
// Register our handlers
sip_client.set_open_handler(bind(&on_open,&sip_client,::_1));
sip_client.set_message_handler(bind(&on_message,&sip_client,::_1,::_2));
websocketpp::lib::error_code ec;
client::connection_ptr con = sip_client.get_connection(uri, ec);
// Specify the SIP subprotocol:
con->add_subprotocol("sip");
sip_client.connect(con);
// Start the ASIO io_service run loop
sip_client.run();
while(!received) {
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
}
std::cout << "done" << std::endl;
} catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
}
}

View file

@ -0,0 +1,23 @@
## Main development example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('subprotocol_server', ["subprotocol_server.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('subprotocol_server', ["subprotocol_server.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,48 @@
#include <iostream>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
using websocketpp::lib::ref;
bool validate(server & s, connection_hdl hdl) {
server::connection_ptr con = s.get_con_from_hdl(hdl);
std::cout << "Cache-Control: " << con->get_request_header("Cache-Control") << std::endl;
const std::vector<std::string> & subp_requests = con->get_requested_subprotocols();
std::vector<std::string>::const_iterator it;
for (it = subp_requests.begin(); it != subp_requests.end(); ++it) {
std::cout << "Requested: " << *it << std::endl;
}
if (subp_requests.size() > 0) {
con->select_subprotocol(subp_requests[0]);
}
return true;
}
int main() {
try {
server s;
s.set_validate_handler(bind(&validate,ref(s),::_1));
s.init_asio();
s.listen(9005);
s.start_accept();
s.run();
} catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
}
}

View file

@ -0,0 +1,10 @@
file (GLOB SOURCE_FILES *.cpp)
file (GLOB HEADER_FILES *.hpp)
init_target (telemetry_client)
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
link_boost ()
final_target ()

View file

@ -0,0 +1,23 @@
## Telemetry client example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('telemetry_client', ["telemetry_client.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system','random'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('telemetry_client', ["telemetry_client.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,156 @@
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>
// This header pulls in the WebSocket++ abstracted thread support that will
// select between boost::thread and std::thread based on how the build system
// is configured.
#include <websocketpp/common/thread.hpp>
/**
* The telemetry client connects to a WebSocket server and sends a message every
* second containing an integer count. This example can be used as the basis for
* programs where a client connects and pushes data for logging, stress/load
* testing, etc.
*/
class telemetry_client {
public:
typedef websocketpp::client<websocketpp::config::asio_client> client;
typedef websocketpp::lib::lock_guard<websocketpp::lib::mutex> scoped_lock;
telemetry_client() : m_open(false),m_done(false) {
// set up access channels to only log interesting things
m_client.clear_access_channels(websocketpp::log::alevel::all);
m_client.set_access_channels(websocketpp::log::alevel::connect);
m_client.set_access_channels(websocketpp::log::alevel::disconnect);
m_client.set_access_channels(websocketpp::log::alevel::app);
// Initialize the Asio transport policy
m_client.init_asio();
// Bind the handlers we are using
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::bind;
m_client.set_open_handler(bind(&telemetry_client::on_open,this,::_1));
m_client.set_close_handler(bind(&telemetry_client::on_close,this,::_1));
m_client.set_fail_handler(bind(&telemetry_client::on_fail,this,::_1));
}
// This method will block until the connection is complete
void run(const std::string & uri) {
// Create a new connection to the given URI
websocketpp::lib::error_code ec;
client::connection_ptr con = m_client.get_connection(uri, ec);
if (ec) {
m_client.get_alog().write(websocketpp::log::alevel::app,
"Get Connection Error: "+ec.message());
return;
}
// Grab a handle for this connection so we can talk to it in a thread
// safe manor after the event loop starts.
m_hdl = con->get_handle();
// Queue the connection. No DNS queries or network connections will be
// made until the io_service event loop is run.
m_client.connect(con);
// Create a thread to run the ASIO io_service event loop
websocketpp::lib::thread asio_thread(&client::run, &m_client);
// Create a thread to run the telemetry loop
websocketpp::lib::thread telemetry_thread(&telemetry_client::telemetry_loop,this);
asio_thread.join();
telemetry_thread.join();
}
// The open handler will signal that we are ready to start sending telemetry
void on_open(websocketpp::connection_hdl) {
m_client.get_alog().write(websocketpp::log::alevel::app,
"Connection opened, starting telemetry!");
scoped_lock guard(m_lock);
m_open = true;
}
// The close handler will signal that we should stop sending telemetry
void on_close(websocketpp::connection_hdl) {
m_client.get_alog().write(websocketpp::log::alevel::app,
"Connection closed, stopping telemetry!");
scoped_lock guard(m_lock);
m_done = true;
}
// The fail handler will signal that we should stop sending telemetry
void on_fail(websocketpp::connection_hdl) {
m_client.get_alog().write(websocketpp::log::alevel::app,
"Connection failed, stopping telemetry!");
scoped_lock guard(m_lock);
m_done = true;
}
void telemetry_loop() {
uint64_t count = 0;
std::stringstream val;
websocketpp::lib::error_code ec;
while(1) {
bool wait = false;
{
scoped_lock guard(m_lock);
// If the connection has been closed, stop generating telemetry
if (m_done) {break;}
// If the connection hasn't been opened yet wait a bit and retry
if (!m_open) {
wait = true;
}
}
if (wait) {
sleep(1);
continue;
}
val.str("");
val << "count is " << count++;
m_client.get_alog().write(websocketpp::log::alevel::app, val.str());
m_client.send(m_hdl,val.str(),websocketpp::frame::opcode::text,ec);
// The most likely error that we will get is that the connection is
// not in the right state. Usually this means we tried to send a
// message to a connection that was closed or in the process of
// closing. While many errors here can be easily recovered from,
// in this simple example, we'll stop the telemetry loop.
if (ec) {
m_client.get_alog().write(websocketpp::log::alevel::app,
"Send Error: "+ec.message());
break;
}
sleep(1);
}
}
private:
client m_client;
websocketpp::connection_hdl m_hdl;
websocketpp::lib::mutex m_lock;
bool m_open;
bool m_done;
};
int main(int argc, char* argv[]) {
telemetry_client c;
std::string uri = "ws://localhost:9002";
if (argc == 2) {
uri = argv[1];
}
c.run(uri);
}

View file

@ -0,0 +1,10 @@
file (GLOB SOURCE_FILES *.cpp)
file (GLOB HEADER_FILES *.hpp)
init_target (telemetry_server)
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
link_boost ()
final_target ()

View file

@ -0,0 +1,23 @@
## Main development example
##
Import('env')
Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('telemetry_server', ["telemetry_server.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('telemetry_server', ["telemetry_server.cpp"], LIBS = ALL_LIBS)
Return('prgs')

View file

@ -0,0 +1,85 @@
<!doctype html>
<html>
<head>
<title>WebSocket++ Telemetry Client</title>
</head>
<body>
<script type="text/javascript">
var ws;
var url;
function connect() {
url = document.getElementById("server_url").value;
if ("WebSocket" in window) {
ws = new WebSocket(url);
} else if ("MozWebSocket" in window) {
ws = new MozWebSocket(url);
} else {
document.getElementById("messages").innerHTML += "This Browser does not support WebSockets<br />";
return;
}
ws.onopen = function(e) {
document.getElementById("messages").innerHTML += "Client: A connection to "+ws.url+" has been opened.<br />";
document.getElementById("server_url").disabled = true;
document.getElementById("toggle_connect").innerHTML = "Disconnect";
};
ws.onerror = function(e) {
document.getElementById("messages").innerHTML += "Client: An error occured, see console log for more details.<br />";
console.log(e);
};
ws.onclose = function(e) {
document.getElementById("messages").innerHTML += "Client: The connection to "+url+" was closed. ["+e.code+(e.reason != "" ? ","+e.reason : "")+"]<br />";
cleanup_disconnect();
};
ws.onmessage = function(e) {
document.getElementById("messages").innerHTML += "Server: "+e.data+"<br />";
};
}
function disconnect() {
ws.close();
cleanup_disconnect();
}
function cleanup_disconnect() {
document.getElementById("server_url").disabled = false;
document.getElementById("toggle_connect").innerHTML = "Connect";
}
function toggle_connect() {
if (document.getElementById("server_url").disabled === false) {
connect();
} else {
disconnect();
}
}
</script>
<style>
body,html {
margin: 0px;
padding: 0px;
}
#controls {
float:right;
background-color: #999;
}
</style>
<div id="controls">
<div id="server">
<input type="text" name="server_url" id="server_url" value="ws://localhost:9002" /><br />
<button id="toggle_connect" onclick="toggle_connect();">Connect</button>
</div>
</div>
<div id="messages"></div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show more