Add boost and cpprestsdk

This commit is contained in:
2025-05-28 13:59:55 -03:00
parent d3fb88a65d
commit 7079ba75f2
14583 changed files with 3063824 additions and 1 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,736 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Various common utilities.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/details/basic_types.h"
#include "pplx/pplxtasks.h"
#include <chrono>
#include <cstdint>
#include <limits.h>
#include <locale.h>
#include <random>
#include <string>
#include <system_error>
#include <vector>
#ifndef _WIN32
#include <sys/time.h>
#if !defined(ANDROID) && !defined(__ANDROID__) && defined(HAVE_XLOCALE_H) // CodePlex 269
/* Systems using glibc: xlocale.h has been removed from glibc 2.26
The above include of locale.h is sufficient
Further details: https://sourceware.org/git/?p=glibc.git;a=commit;h=f0be25b6336db7492e47d2e8e72eb8af53b5506d */
#include <xlocale.h>
#endif
#endif
/// Various utilities for string conversions and date and time manipulation.
namespace utility
{
// Left over from VS2010 support, remains to avoid breaking.
typedef std::chrono::seconds seconds;
/// Functions for converting to/from std::chrono::seconds to xml string.
namespace timespan
{
/// <summary>
/// Converts a timespan/interval in seconds to xml duration string as specified by
/// http://www.w3.org/TR/xmlschema-2/#duration
/// </summary>
_ASYNCRTIMP utility::string_t __cdecl seconds_to_xml_duration(utility::seconds numSecs);
/// <summary>
/// Converts an xml duration to timespan/interval in seconds
/// http://www.w3.org/TR/xmlschema-2/#duration
/// </summary>
_ASYNCRTIMP utility::seconds __cdecl xml_duration_to_seconds(const utility::string_t& timespanString);
} // namespace timespan
/// Functions for Unicode string conversions.
namespace conversions
{
/// <summary>
/// Converts a UTF-16 string to a UTF-8 string.
/// </summary>
/// <param name="w">A two byte character UTF-16 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
_ASYNCRTIMP std::string __cdecl utf16_to_utf8(const utf16string& w);
/// <summary>
/// Converts a UTF-8 string to a UTF-16
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl utf8_to_utf16(const std::string& s);
/// <summary>
/// Converts a ASCII (us-ascii) string to a UTF-16 string.
/// </summary>
/// <param name="s">A single byte character us-ascii string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl usascii_to_utf16(const std::string& s);
/// <summary>
/// Converts a Latin1 (iso-8859-1) string to a UTF-16 string.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl latin1_to_utf16(const std::string& s);
/// <summary>
/// Converts a Latin1 (iso-8859-1) string to a UTF-8 string.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
_ASYNCRTIMP utf8string __cdecl latin1_to_utf8(const std::string& s);
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A platform dependent string type.</returns>
#ifdef _UTF16_STRINGS
_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string&& s);
#else
inline utility::string_t&& to_string_t(std::string&& s) { return std::move(s); }
#endif
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A two byte character UTF-16 string.</param>
/// <returns>A platform dependent string type.</returns>
#ifdef _UTF16_STRINGS
inline utility::string_t&& to_string_t(utf16string&& s) { return std::move(s); }
#else
_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string&& s);
#endif
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A platform dependent string type.</returns>
#ifdef _UTF16_STRINGS
_ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string& s);
#else
inline const utility::string_t& to_string_t(const std::string& s) { return s; }
#endif
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A two byte character UTF-16 string.</param>
/// <returns>A platform dependent string type.</returns>
#ifdef _UTF16_STRINGS
inline const utility::string_t& to_string_t(const utf16string& s) { return s; }
#else
_ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string& s);
#endif
/// <summary>
/// Converts to a UTF-16 from string.
/// </summary>
/// <param name="value">A single byte character UTF-8 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl to_utf16string(const std::string& value);
/// <summary>
/// Converts to a UTF-16 from string.
/// </summary>
/// <param name="value">A two byte character UTF-16 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
inline const utf16string& to_utf16string(const utf16string& value) { return value; }
/// <summary>
/// Converts to a UTF-16 from string.
/// </summary>
/// <param name="value">A two byte character UTF-16 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
inline utf16string&& to_utf16string(utf16string&& value) { return std::move(value); }
/// <summary>
/// Converts to a UTF-8 string.
/// </summary>
/// <param name="value">A single byte character UTF-8 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
inline std::string&& to_utf8string(std::string&& value) { return std::move(value); }
/// <summary>
/// Converts to a UTF-8 string.
/// </summary>
/// <param name="value">A single byte character UTF-8 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
inline const std::string& to_utf8string(const std::string& value) { return value; }
/// <summary>
/// Converts to a UTF-8 string.
/// </summary>
/// <param name="value">A two byte character UTF-16 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
_ASYNCRTIMP std::string __cdecl to_utf8string(const utf16string& value);
/// <summary>
/// Encode the given byte array into a base64 string
/// </summary>
_ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector<unsigned char>& data);
/// <summary>
/// Encode the given 8-byte integer into a base64 string
/// </summary>
_ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data);
/// <summary>
/// Decode the given base64 string to a byte array
/// </summary>
_ASYNCRTIMP std::vector<unsigned char> __cdecl from_base64(const utility::string_t& str);
template<typename Source>
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
"locale support is required.")
utility::string_t print_string(const Source& val, const std::locale& loc = std::locale())
{
utility::ostringstream_t oss;
oss.imbue(loc);
oss << val;
if (oss.bad())
{
throw std::bad_cast();
}
return oss.str();
}
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
"locale support is required.")
inline utility::string_t print_string(const utility::string_t& val) { return val; }
namespace details
{
#if defined(__ANDROID__)
template<class T>
inline std::string to_string(const T t)
{
std::ostringstream os;
os.imbue(std::locale::classic());
os << t;
return os.str();
}
#endif
template<class T>
inline utility::string_t to_string_t(const T t)
{
#ifdef _UTF16_STRINGS
using std::to_wstring;
return to_wstring(t);
#else
#if !defined(__ANDROID__)
using std::to_string;
#endif
return to_string(t);
#endif
}
template<typename Source>
utility::string_t print_string(const Source& val)
{
utility::ostringstream_t oss;
oss.imbue(std::locale::classic());
oss << val;
if (oss.bad())
{
throw std::bad_cast();
}
return oss.str();
}
inline const utility::string_t& print_string(const utility::string_t& val) { return val; }
template<typename Source>
utf8string print_utf8string(const Source& val)
{
return conversions::to_utf8string(print_string(val));
}
inline const utf8string& print_utf8string(const utf8string& val) { return val; }
template<typename Target>
Target scan_string(const utility::string_t& str)
{
Target t;
utility::istringstream_t iss(str);
iss.imbue(std::locale::classic());
iss >> t;
if (iss.bad())
{
throw std::bad_cast();
}
return t;
}
inline const utility::string_t& scan_string(const utility::string_t& str) { return str; }
} // namespace details
template<typename Target>
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
"locale support is required.")
Target scan_string(const utility::string_t& str, const std::locale& loc = std::locale())
{
Target t;
utility::istringstream_t iss(str);
iss.imbue(loc);
iss >> t;
if (iss.bad())
{
throw std::bad_cast();
}
return t;
}
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
"locale support is required.")
inline utility::string_t scan_string(const utility::string_t& str) { return str; }
} // namespace conversions
namespace details
{
/// <summary>
/// Cross platform RAII container for setting thread local locale.
/// </summary>
class scoped_c_thread_locale
{
public:
_ASYNCRTIMP scoped_c_thread_locale();
_ASYNCRTIMP ~scoped_c_thread_locale();
#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269
#ifdef _WIN32
typedef _locale_t xplat_locale;
#else
typedef locale_t xplat_locale;
#endif
static _ASYNCRTIMP xplat_locale __cdecl c_locale();
#endif
private:
#ifdef _WIN32
std::string m_prevLocale;
int m_prevThreadSetting;
#elif !(defined(ANDROID) || defined(__ANDROID__))
locale_t m_prevLocale;
#endif
scoped_c_thread_locale(const scoped_c_thread_locale&);
scoped_c_thread_locale& operator=(const scoped_c_thread_locale&);
};
/// <summary>
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
/// taking global lock for performance reasons.
/// </summary>
inline bool __cdecl is_alnum(const unsigned char uch) CPPREST_NOEXCEPT
{ // test if uch is an alnum character
// special casing char to avoid branches
// clang-format off
static CPPREST_CONSTEXPR bool is_alnum_table[UCHAR_MAX + 1] = {
/* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */
/* 0X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 3X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0-9 */
/* 4X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A-Z */
/* 5X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
/* 6X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a-z */
/* 7X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
/* non-ASCII values initialized to 0 */
};
// clang-format on
return (is_alnum_table[uch]);
}
/// <summary>
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
/// taking global lock for performance reasons.
/// </summary>
inline bool __cdecl is_alnum(const char ch) CPPREST_NOEXCEPT { return (is_alnum(static_cast<unsigned char>(ch))); }
/// <summary>
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
/// taking global lock for performance reasons.
/// </summary>
template<class Elem>
inline bool __cdecl is_alnum(Elem ch) CPPREST_NOEXCEPT
{
// assumes 'x' == L'x' for the ASCII range
typedef typename std::make_unsigned<Elem>::type UElem;
const auto uch = static_cast<UElem>(ch);
return (uch <= static_cast<UElem>('z') && is_alnum(static_cast<unsigned char>(uch)));
}
/// <summary>
/// Our own implementation of whitespace test instead of std::isspace to avoid
/// taking global lock for performance reasons.
/// The following characters are considered whitespace:
/// 0x09 == Horizontal Tab
/// 0x0A == Line Feed
/// 0x0B == Vertical Tab
/// 0x0C == Form Feed
/// 0x0D == Carrage Return
/// 0x20 == Space
/// </summary>
template<class Elem>
inline bool __cdecl is_space(Elem ch) CPPREST_NOEXCEPT
{
// assumes 'x' == L'x' for the ASCII range
typedef typename std::make_unsigned<Elem>::type UElem;
const auto uch = static_cast<UElem>(ch);
return uch == 0x20u || (uch >= 0x09u && uch <= 0x0Du);
}
/// <summary>
/// Simplistic implementation of make_unique. A better implementation would be based on variadic templates
/// and therefore not be compatible with Dev10.
/// </summary>
template<typename _Type>
std::unique_ptr<_Type> make_unique()
{
return std::unique_ptr<_Type>(new _Type());
}
template<typename _Type, typename _Arg1>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1)
{
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1)));
}
template<typename _Type, typename _Arg1, typename _Arg2>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2)
{
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2)));
}
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3)
{
return std::unique_ptr<_Type>(
new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3)));
}
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4)
{
return std::unique_ptr<_Type>(new _Type(
std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4)));
}
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4, typename _Arg5>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5)
{
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1),
std::forward<_Arg2>(arg2),
std::forward<_Arg3>(arg3),
std::forward<_Arg4>(arg4),
std::forward<_Arg5>(arg5)));
}
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4, typename _Arg5, typename _Arg6>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5, _Arg6&& arg6)
{
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1),
std::forward<_Arg2>(arg2),
std::forward<_Arg3>(arg3),
std::forward<_Arg4>(arg4),
std::forward<_Arg5>(arg5),
std::forward<_Arg6>(arg6)));
}
/// <summary>
/// Cross platform utility function for performing case insensitive string equality comparison.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if the strings are equivalent, false otherwise</returns>
_ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT;
/// <summary>
/// Cross platform utility function for performing case insensitive string equality comparison.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if the strings are equivalent, false otherwise</returns>
_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT;
/// <summary>
/// Cross platform utility function for performing case insensitive string less-than comparison.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise,
/// false.</returns>
_ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT;
/// <summary>
/// Cross platform utility function for performing case insensitive string less-than comparison.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise,
/// false.</returns>
_ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT;
/// <summary>
/// Convert a string to lowercase in place.
/// </summary>
/// <param name="target">The string to convert to lowercase.</param>
_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT;
/// <summary>
/// Convert a string to lowercase in place.
/// </summary>
/// <param name="target">The string to convert to lowercase.</param>
_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT;
#ifdef _WIN32
/// <summary>
/// Category error type for Windows OS errors.
/// </summary>
class windows_category_impl : public std::error_category
{
public:
virtual const char* name() const CPPREST_NOEXCEPT { return "windows"; }
virtual std::string message(int errorCode) const CPPREST_NOEXCEPT;
virtual std::error_condition default_error_condition(int errorCode) const CPPREST_NOEXCEPT;
};
/// <summary>
/// Gets the one global instance of the windows error category.
/// </summary>
/// </returns>An error category instance.</returns>
_ASYNCRTIMP const std::error_category& __cdecl windows_category();
#else
/// <summary>
/// Gets the one global instance of the linux error category.
/// </summary>
/// </returns>An error category instance.</returns>
_ASYNCRTIMP const std::error_category& __cdecl linux_category();
#endif
/// <summary>
/// Gets the one global instance of the current platform's error category.
/// </summary>
_ASYNCRTIMP const std::error_category& __cdecl platform_category();
/// <summary>
/// Creates an instance of std::system_error from a OS error code.
/// </summary>
inline std::system_error __cdecl create_system_error(unsigned long errorCode)
{
std::error_code code((int)errorCode, platform_category());
return std::system_error(code, code.message());
}
/// <summary>
/// Creates a std::error_code from a OS error code.
/// </summary>
inline std::error_code __cdecl create_error_code(unsigned long errorCode)
{
return std::error_code((int)errorCode, platform_category());
}
/// <summary>
/// Creates the corresponding error message from a OS error code.
/// </summary>
inline utility::string_t __cdecl create_error_message(unsigned long errorCode)
{
return utility::conversions::to_string_t(create_error_code(errorCode).message());
}
} // namespace details
class datetime
{
public:
typedef uint64_t interval_type;
/// <summary>
/// Defines the supported date and time string formats.
/// </summary>
enum date_format
{
RFC_1123,
ISO_8601
};
/// <summary>
/// Returns the current UTC time.
/// </summary>
static _ASYNCRTIMP datetime __cdecl utc_now();
/// <summary>
/// An invalid UTC timestamp value.
/// </summary>
enum : interval_type
{
utc_timestamp_invalid = static_cast<interval_type>(-1)
};
/// <summary>
/// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00.
/// If time is before epoch, utc_timestamp_invalid is returned.
/// </summary>
static interval_type utc_timestamp()
{
const auto seconds = utc_now().to_interval() / _secondTicks;
if (seconds >= 11644473600LL)
{
return seconds - 11644473600LL;
}
else
{
return utc_timestamp_invalid;
}
}
datetime() : m_interval(0) { }
/// <summary>
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 or ISO 8601 format.
/// </summary>
/// <returns>Returns a <c>datetime</c> of zero if not successful.</returns>
static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123);
/// <summary>
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 or ISO 8601 format.
/// </summary>
/// <returns>Returns <c>datetime::maximum()</c> if not successful.</returns>
static _ASYNCRTIMP datetime __cdecl from_string_maximum_error(const utility::string_t& timestring,
date_format format = RFC_1123);
/// <summary>
/// Returns a string representation of the <c>datetime</c>.
/// </summary>
_ASYNCRTIMP utility::string_t to_string(date_format format = RFC_1123) const;
/// <summary>
/// Returns the integral time value.
/// </summary>
interval_type to_interval() const { return m_interval; }
static datetime from_interval(interval_type interval) { return datetime(interval); }
datetime operator-(interval_type value) const { return datetime(m_interval - value); }
datetime operator+(interval_type value) const { return datetime(m_interval + value); }
bool operator==(datetime dt) const { return m_interval == dt.m_interval; }
bool operator!=(const datetime& dt) const { return !(*this == dt); }
bool operator>(const datetime& dt) const { return this->m_interval > dt.m_interval; }
bool operator<(const datetime& dt) const { return this->m_interval < dt.m_interval; }
bool operator>=(const datetime& dt) const { return this->m_interval >= dt.m_interval; }
bool operator<=(const datetime& dt) const { return this->m_interval <= dt.m_interval; }
static interval_type from_milliseconds(unsigned int milliseconds) { return milliseconds * _msTicks; }
static interval_type from_seconds(unsigned int seconds) { return seconds * _secondTicks; }
static interval_type from_minutes(unsigned int minutes) { return minutes * _minuteTicks; }
static interval_type from_hours(unsigned int hours) { return hours * _hourTicks; }
static interval_type from_days(unsigned int days) { return days * _dayTicks; }
bool is_initialized() const { return m_interval != 0; }
static datetime maximum() { return datetime(static_cast<interval_type>(-1)); }
private:
friend int operator-(datetime t1, datetime t2);
static const interval_type _msTicks = static_cast<interval_type>(10000);
static const interval_type _secondTicks = 1000 * _msTicks;
static const interval_type _minuteTicks = 60 * _secondTicks;
static const interval_type _hourTicks = 60 * 60 * _secondTicks;
static const interval_type _dayTicks = 24 * 60 * 60 * _secondTicks;
// Private constructor. Use static methods to create an instance.
datetime(interval_type interval) : m_interval(interval) { }
// Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns.
interval_type m_interval;
};
inline int operator-(datetime t1, datetime t2)
{
auto diff = (t1.m_interval - t2.m_interval);
// Round it down to seconds
diff /= 10 * 1000 * 1000;
return static_cast<int>(diff);
}
/// <summary>
/// Nonce string generator class.
/// </summary>
class nonce_generator
{
public:
/// <summary>
/// Define default nonce length.
/// </summary>
enum
{
default_length = 32
};
/// <summary>
/// Nonce generator constructor.
/// </summary>
/// <param name="length">Length of the generated nonce string.</param>
nonce_generator(int length = default_length)
: m_random(static_cast<unsigned int>(utility::datetime::utc_timestamp())), m_length(length)
{
}
/// <summary>
/// Generate a nonce string containing random alphanumeric characters (A-Za-z0-9).
/// Length of the generated string is set by length().
/// </summary>
/// <returns>The generated nonce string.</returns>
_ASYNCRTIMP utility::string_t generate();
/// <summary>
/// Get length of generated nonce string.
/// </summary>
/// <returns>Nonce string length.</returns>
int length() const { return m_length; }
/// <summary>
/// Set length of the generated nonce string.
/// </summary>
/// <param name="length">Lenght of nonce string.</param>
void set_length(int length) { m_length = length; }
private:
std::mt19937 m_random;
int m_length;
};
} // namespace utility

View File

@ -0,0 +1,392 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Protocol independent support for URIs.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/basic_types.h"
#include <map>
#include <string>
#include <utility>
#include <vector>
namespace web
{
namespace details
{
struct uri_components
{
uri_components() : m_path(_XPLATSTR("/")), m_port(-1) {}
uri_components(const uri_components&) = default;
uri_components& operator=(const uri_components&) = default;
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
uri_components(uri_components&& other) CPPREST_NOEXCEPT : m_scheme(std::move(other.m_scheme)),
m_host(std::move(other.m_host)),
m_user_info(std::move(other.m_user_info)),
m_path(std::move(other.m_path)),
m_query(std::move(other.m_query)),
m_fragment(std::move(other.m_fragment)),
m_port(other.m_port)
{
}
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
uri_components& operator=(uri_components&& other) CPPREST_NOEXCEPT
{
if (this != &other)
{
m_scheme = std::move(other.m_scheme);
m_host = std::move(other.m_host);
m_user_info = std::move(other.m_user_info);
m_path = std::move(other.m_path);
m_query = std::move(other.m_query);
m_fragment = std::move(other.m_fragment);
m_port = other.m_port;
}
return *this;
}
_ASYNCRTIMP utility::string_t join();
utility::string_t m_scheme;
utility::string_t m_host;
utility::string_t m_user_info;
utility::string_t m_path;
utility::string_t m_query;
utility::string_t m_fragment;
int m_port;
};
} // namespace details
/// <summary>
/// A single exception type to represent errors in parsing, encoding, and decoding URIs.
/// </summary>
class uri_exception : public std::exception
{
public:
uri_exception(std::string msg) : m_msg(std::move(msg)) {}
~uri_exception() CPPREST_NOEXCEPT {}
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
private:
std::string m_msg;
};
/// <summary>
/// A flexible, protocol independent URI implementation.
///
/// URI instances are immutable. Querying the various fields on an empty URI will return empty strings. Querying
/// various diagnostic members on an empty URI will return false.
/// </summary>
/// <remarks>
/// This implementation accepts both URIs ('http://msn.com/path') and URI relative-references
/// ('/path?query#frag').
///
/// This implementation does not provide any scheme-specific handling -- an example of this
/// would be the following: 'http://path1/path'. This is a valid URI, but it's not a valid
/// http-uri -- that is, it's syntactically correct but does not conform to the requirements
/// of the http scheme (http requires a host).
/// We could provide this by allowing a pluggable 'scheme' policy-class, which would provide
/// extra capability for validating and canonicalizing a URI according to scheme, and would
/// introduce a layer of type-safety for URIs of differing schemes, and thus differing semantics.
///
/// One issue with implementing a scheme-independent URI facility is that of comparing for equality.
/// For instance, these URIs are considered equal 'http://msn.com', 'http://msn.com:80'. That is --
/// the 'default' port can be either omitted or explicit. Since we don't have a way to map a scheme
/// to it's default port, we don't have a way to know these are equal. This is just one of a class of
/// issues with regard to scheme-specific behavior.
/// </remarks>
class uri
{
public:
/// <summary>
/// The various components of a URI. This enum is used to indicate which
/// URI component is being encoded to the encode_uri_component. This allows
/// specific encoding to be performed.
///
/// Scheme and port don't allow '%' so they don't need to be encoded.
/// </summary>
class components
{
public:
enum component
{
user_info,
host,
path,
query,
fragment,
full_uri
};
};
/// <summary>
/// Encodes a URI component according to RFC 3986.
/// Note if a full URI is specified instead of an individual URI component all
/// characters not in the unreserved set are escaped.
/// </summary>
/// <param name="raw">The URI as a string.</param>
/// <returns>The encoded string.</returns>
_ASYNCRTIMP static utility::string_t __cdecl encode_uri(const utility::string_t& raw,
uri::components::component = components::full_uri);
/// <summary>
/// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their
/// hexadecimal representation.
/// </summary>
/// <returns>The encoded string.</returns>
_ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t& data);
/// <summary>
/// Decodes an encoded string.
/// </summary>
/// <param name="encoded">The URI as a string.</param>
/// <returns>The decoded string.</returns>
_ASYNCRTIMP static utility::string_t __cdecl decode(const utility::string_t& encoded);
/// <summary>
/// Splits a path into its hierarchical components.
/// </summary>
/// <param name="path">The path as a string</param>
/// <returns>A <c>std::vector&lt;utility::string_t&gt;</c> containing the segments in the path.</returns>
_ASYNCRTIMP static std::vector<utility::string_t> __cdecl split_path(const utility::string_t& path);
/// <summary>
/// Splits a query into its key-value components.
/// </summary>
/// <param name="query">The query string</param>
/// <returns>A <c>std::map&lt;utility::string_t, utility::string_t&gt;</c> containing the key-value components of
/// the query.</returns>
_ASYNCRTIMP static std::map<utility::string_t, utility::string_t> __cdecl split_query(
const utility::string_t& query);
/// <summary>
/// Validates a string as a URI.
/// </summary>
/// <remarks>
/// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query').
/// </remarks>
/// <param name="uri_string">The URI string to be validated.</param>
/// <returns><c>true</c> if the given string represents a valid URI, <c>false</c> otherwise.</returns>
_ASYNCRTIMP static bool __cdecl validate(const utility::string_t& uri_string);
/// <summary>
/// Creates an empty uri
/// </summary>
uri() : m_uri(_XPLATSTR("/")) {}
/// <summary>
/// Creates a URI from the given encoded string. This will throw an exception if the string
/// does not contain a valid URI. Use uri::validate if processing user-input.
/// </summary>
/// <param name="uri_string">A pointer to an encoded string to create the URI instance.</param>
_ASYNCRTIMP uri(const utility::char_t* uri_string);
/// <summary>
/// Creates a URI from the given encoded string. This will throw an exception if the string
/// does not contain a valid URI. Use uri::validate if processing user-input.
/// </summary>
/// <param name="uri_string">An encoded URI string to create the URI instance.</param>
_ASYNCRTIMP uri(const utility::string_t& uri_string);
/// <summary>
/// Copy constructor.
/// </summary>
uri(const uri&) = default;
/// <summary>
/// Copy assignment operator.
/// </summary>
uri& operator=(const uri&) = default;
/// <summary>
/// Move constructor.
/// </summary>
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
uri(uri&& other) CPPREST_NOEXCEPT : m_uri(std::move(other.m_uri)), m_components(std::move(other.m_components)) {}
/// <summary>
/// Move assignment operator
/// </summary>
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
uri& operator=(uri&& other) CPPREST_NOEXCEPT
{
if (this != &other)
{
m_uri = std::move(other.m_uri);
m_components = std::move(other.m_components);
}
return *this;
}
/// <summary>
/// Get the scheme component of the URI as an encoded string.
/// </summary>
/// <returns>The URI scheme as a string.</returns>
const utility::string_t& scheme() const { return m_components.m_scheme; }
/// <summary>
/// Get the user information component of the URI as an encoded string.
/// </summary>
/// <returns>The URI user information as a string.</returns>
const utility::string_t& user_info() const { return m_components.m_user_info; }
/// <summary>
/// Get the host component of the URI as an encoded string.
/// </summary>
/// <returns>The URI host as a string.</returns>
const utility::string_t& host() const { return m_components.m_host; }
/// <summary>
/// Get the port component of the URI. Returns -1 if no port is specified.
/// </summary>
/// <returns>The URI port as an integer.</returns>
int port() const { return m_components.m_port; }
/// <summary>
/// Get the path component of the URI as an encoded string.
/// </summary>
/// <returns>The URI path as a string.</returns>
const utility::string_t& path() const { return m_components.m_path; }
/// <summary>
/// Get the query component of the URI as an encoded string.
/// </summary>
/// <returns>The URI query as a string.</returns>
const utility::string_t& query() const { return m_components.m_query; }
/// <summary>
/// Get the fragment component of the URI as an encoded string.
/// </summary>
/// <returns>The URI fragment as a string.</returns>
const utility::string_t& fragment() const { return m_components.m_fragment; }
/// <summary>
/// Creates a new uri object with the same authority portion as this one, omitting the resource and query portions.
/// </summary>
/// <returns>The new uri object with the same authority.</returns>
_ASYNCRTIMP uri authority() const;
/// <summary>
/// Gets the path, query, and fragment portion of this uri, which may be empty.
/// </summary>
/// <returns>The new URI object with the path, query and fragment portion of this URI.</returns>
_ASYNCRTIMP uri resource() const;
/// <summary>
/// An empty URI specifies no components, and serves as a default value
/// </summary>
bool is_empty() const { return this->m_uri.empty() || this->m_uri == _XPLATSTR("/"); }
/// <summary>
/// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine.
/// </summary>
/// <remarks>
/// Examples include "localhost", or "127.0.0.1". The only URIs for which this method returns true are "127.0.0.1", and "localhost",
/// all other URIs return false
/// </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() == _XPLATSTR("127.0.0.1")));
}
/// <summary>
/// A wildcard URI is one which refers to all hostnames that resolve to the local machine (using the * or +)
/// </summary>
/// <example>
/// http://*:80
/// </example>
bool is_host_wildcard() const
{
return !is_empty() && (this->host() == _XPLATSTR("*") || this->host() == _XPLATSTR("+"));
}
/// <summary>
/// A portable URI is one with a hostname that can be resolved globally (used from another machine).
/// </summary>
/// <returns><c>true</c> if this URI can be resolved globally (used from another machine), <c>false</c>
/// otherwise.</returns> <remarks> The hostname "localhost" is a reserved name that is guaranteed to resolve to the
/// local machine, and cannot be used for inter-machine communication. Likewise the hostnames "*" and "+" on Windows
/// represent wildcards, and do not map to a resolvable address.
/// </remarks>
bool is_host_portable() const { return !(is_empty() || is_host_loopback() || is_host_wildcard()); }
/// <summary>
/// A default port is one where the port is unspecified, and will be determined by the operating system.
/// The choice of default port may be dictated by the scheme (http -> 80) or not.
/// </summary>
/// <returns><c>true</c> if this URI instance has a default port, <c>false</c> otherwise.</returns>
bool is_port_default() const { return !is_empty() && this->port() == 0; }
/// <summary>
/// An "authority" URI is one with only a scheme, optional userinfo, hostname, and (optional) port.
/// </summary>
/// <returns><c>true</c> if this is an "authority" URI, <c>false</c> otherwise.</returns>
bool is_authority() const { return !is_empty() && is_path_empty() && query().empty() && fragment().empty(); }
/// <summary>
/// Returns whether the other URI has the same authority as this one
/// </summary>
/// <param name="other">The URI to compare the authority with.</param>
/// <returns><c>true</c> if both the URI's have the same authority, <c>false</c> otherwise.</returns>
bool has_same_authority(const uri& other) const { return !is_empty() && this->authority() == other.authority(); }
/// <summary>
/// Returns whether the path portion of this URI is empty
/// </summary>
/// <returns><c>true</c> if the path portion of this URI is empty, <c>false</c> otherwise.</returns>
bool is_path_empty() const { return path().empty() || path() == _XPLATSTR("/"); }
/// <summary>
/// Returns the full (encoded) URI as a string.
/// </summary>
/// <returns>The full encoded URI string.</returns>
utility::string_t to_string() const { return m_uri; }
/// <summary>
/// Returns an URI resolved against <c>this</c> as the base URI
/// according to RFC3986, Section 5 (https://tools.ietf.org/html/rfc3986#section-5).
/// </summary>
/// <param name="relativeUri">The relative URI to be resolved against <c>this</c> as base.</param>
/// <returns>The new resolved URI string.</returns>
_ASYNCRTIMP utility::string_t resolve_uri(const utility::string_t& relativeUri) const;
_ASYNCRTIMP bool operator==(const uri& other) const;
bool operator<(const uri& other) const { return m_uri < other.m_uri; }
bool operator!=(const uri& other) const { return !(this->operator==(other)); }
private:
friend class uri_builder;
/// <summary>
/// Creates a URI from the given URI components.
/// </summary>
/// <param name="components">A URI components object to create the URI instance.</param>
_ASYNCRTIMP uri(const details::uri_components& components);
// Used by uri_builder
static utility::string_t __cdecl encode_query_impl(const utf8string& raw);
utility::string_t m_uri;
details::uri_components m_components;
};
} // namespace web

View File

@ -0,0 +1,590 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* This file defines a basic STL-container-based stream buffer. Reading from the buffer will not remove any data
* from it and seeking is thus supported.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/astreambuf.h"
#include "cpprest/streams.h"
#include "pplx/pplxtasks.h"
#include <algorithm>
#include <iterator>
#include <queue>
#include <vector>
namespace Concurrency
{
namespace streams
{
// Forward declarations
template<typename _CollectionType>
class container_buffer;
namespace details
{
/// <summary>
/// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading
/// sequences of characters.
/// The class itself should not be used in application code, it is used by the stream definitions farther down in the
/// header file.
/// </summary>
/// <remarks> When closed, neither writing nor reading is supported any longer. <c>basic_container_buffer</c> does not
/// support simultaneous use of the buffer for reading and writing.</remarks>
template<typename _CollectionType>
class basic_container_buffer : public streams::details::streambuf_state_manager<typename _CollectionType::value_type>
{
public:
typedef typename _CollectionType::value_type _CharType;
typedef typename basic_streambuf<_CharType>::traits traits;
typedef typename basic_streambuf<_CharType>::int_type int_type;
typedef typename basic_streambuf<_CharType>::pos_type pos_type;
typedef typename basic_streambuf<_CharType>::off_type off_type;
/// <summary>
/// Returns the underlying data container
/// </summary>
_CollectionType& collection() { return m_data; }
/// <summary>
/// Destructor
/// </summary>
virtual ~basic_container_buffer()
{
// Invoke the synchronous versions since we need to
// purge the request queue before deleting the buffer
this->_close_read();
this->_close_write();
}
protected:
/// <summary>
/// can_seek is used to determine whether a stream buffer supports seeking.
/// </summary>
virtual bool can_seek() const { return this->is_open(); }
/// <summary>
/// <c>has_size<c/> is used to determine whether a stream buffer supports size().
/// </summary>
virtual bool has_size() const { return this->is_open(); }
/// <summary>
/// Gets the size of the stream, if known. Calls to <c>has_size</c> will determine whether
/// the result of <c>size</c> can be relied on.
/// </summary>
virtual utility::size64_t size() const { return utility::size64_t(m_data.size()); }
/// <summary>
/// Get the stream buffer size, if one has been set.
/// </summary>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
/// <summary>
/// Sets the stream buffer implementation to buffer or not buffer.
/// </summary>
/// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
/// will not have any effect on what is returned by subsequent calls to <see cref="::buffer_size method"
/// />.</remarks>
virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
/// <summary>
/// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
/// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
/// incurring the overhead of using tasks.
/// </summary>
virtual size_t in_avail() const
{
// See the comment in seek around the restriction that we do not allow read head to
// seek beyond the current write_end.
_ASSERTE(m_current_position <= m_data.size());
msl::safeint3::SafeInt<size_t> readhead(m_current_position);
msl::safeint3::SafeInt<size_t> writeend(m_data.size());
return (size_t)(writeend - readhead);
}
virtual pplx::task<bool> _sync() { return pplx::task_from_result(true); }
virtual pplx::task<int_type> _putc(_CharType ch)
{
int_type retVal = (this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof();
return pplx::task_from_result<int_type>(retVal);
}
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
{
return pplx::task_from_result<size_t>(this->write(ptr, count));
}
/// <summary>
/// Allocates a contiguous memory block and returns it.
/// </summary>
/// <param name="count">The number of characters to allocate.</param>
/// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
/// alloc/commit.</returns>
_CharType* _alloc(size_t count)
{
if (!this->can_write()) return nullptr;
// Allocate space
resize_for_write(m_current_position + count);
// Let the caller copy the data
return (_CharType*)&m_data[m_current_position];
}
/// <summary>
/// Submits a block already allocated by the stream buffer.
/// </summary>
/// <param name="count">The number of characters to be committed.</param>
void _commit(size_t actual)
{
// Update the write position and satisfy any pending reads
update_current_position(m_current_position + actual);
}
/// <summary>
/// Gets a pointer to the next already allocated contiguous block of data.
/// </summary>
/// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
/// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
/// <remarks>
/// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
/// there is no block to return immediately or that the stream buffer does not support the operation.
/// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
/// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
/// a subsequent read will not succeed.
/// </remarks>
virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
{
ptr = nullptr;
count = 0;
if (!this->can_read()) return false;
count = in_avail();
if (count > 0)
{
ptr = (_CharType*)&m_data[m_current_position];
return true;
}
else
{
// Can only be open for read OR write, not both. If there is no data then
// we have reached the end of the stream so indicate such with true.
return true;
}
}
/// <summary>
/// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
/// de-allocate the memory, if it so desires. Move the read position ahead by the count.
/// </summary>
/// <param name="ptr">A pointer to the block of data to be released.</param>
/// <param name="count">The number of characters that were read.</param>
virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
{
if (ptr != nullptr) update_current_position(m_current_position + count);
}
virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
return pplx::task_from_result(this->read(ptr, count));
}
size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return this->read(ptr, count); }
virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
return this->read(ptr, count, false);
}
virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result(this->read_byte(true)); }
virtual int_type _sbumpc() { return this->read_byte(true); }
virtual pplx::task<int_type> _getc() { return pplx::task_from_result(this->read_byte(false)); }
int_type _sgetc() { return this->read_byte(false); }
virtual pplx::task<int_type> _nextc()
{
this->read_byte(true);
return pplx::task_from_result(this->read_byte(false));
}
virtual pplx::task<int_type> _ungetc()
{
auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in);
if (pos == (pos_type)traits::eof()) return pplx::task_from_result(traits::eof());
return this->getc();
}
/// <summary>
/// Gets the current read or write position in the stream.
/// </summary>
/// <param name="direction">The I/O direction to seek (see remarks)</param>
/// <returns>The current position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the direction parameter defines whether to move the read or the write
/// cursor.</remarks>
virtual pos_type getpos(std::ios_base::openmode mode) const
{
if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
return static_cast<pos_type>(traits::eof());
return static_cast<pos_type>(m_current_position);
}
/// <summary>
/// Seeks to the given position.
/// </summary>
/// <param name="pos">The offset from the beginning of the stream.</param>
/// <param name="direction">The I/O direction to seek (see remarks).</param>
/// <returns>The position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors. For such streams, the direction parameter
/// defines whether to move the read or the write cursor.</remarks>
virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode)
{
pos_type beg(0);
// In order to support relative seeking from the end position we need to fix an end position.
// Technically, there is no end for the stream buffer as new writes would just expand the buffer.
// For now, we assume that the current write_end is the end of the buffer. We use this artificial
// end to restrict the read head from seeking beyond what is available.
pos_type end(m_data.size());
if (position >= beg)
{
auto pos = static_cast<size_t>(position);
// Read head
if ((mode & std::ios_base::in) && this->can_read())
{
if (position <= end)
{
// We do not allow reads to seek beyond the end or before the start position.
update_current_position(pos);
return static_cast<pos_type>(m_current_position);
}
}
// Write head
if ((mode & std::ios_base::out) && this->can_write())
{
// Allocate space
resize_for_write(pos);
// Nothing to really copy
// Update write head and satisfy read requests if any
update_current_position(pos);
return static_cast<pos_type>(m_current_position);
}
}
return static_cast<pos_type>(traits::eof());
}
/// <summary>
/// Seeks to a position given by a relative offset.
/// </summary>
/// <param name="offset">The relative position to seek to</param>
/// <param name="way">The starting point (beginning, end, current) for the seek.</param>
/// <param name="mode">The I/O direction to seek (see remarks)</param>
/// <returns>The position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
{
pos_type beg = 0;
pos_type cur = static_cast<pos_type>(m_current_position);
pos_type end = static_cast<pos_type>(m_data.size());
switch (way)
{
case std::ios_base::beg: return seekpos(beg + offset, mode);
case std::ios_base::cur: return seekpos(cur + offset, mode);
case std::ios_base::end: return seekpos(end + offset, mode);
default: return static_cast<pos_type>(traits::eof());
}
}
private:
template<typename _CollectionType1>
friend class streams::container_buffer;
/// <summary>
/// Constructor
/// </summary>
basic_container_buffer(std::ios_base::openmode mode)
: streambuf_state_manager<typename _CollectionType::value_type>(mode), m_current_position(0)
{
validate_mode(mode);
}
/// <summary>
/// Constructor
/// </summary>
basic_container_buffer(_CollectionType data, std::ios_base::openmode mode)
: streambuf_state_manager<typename _CollectionType::value_type>(mode)
, m_data(std::move(data))
, m_current_position((mode & std::ios_base::in) ? 0 : m_data.size())
{
validate_mode(mode);
}
static void validate_mode(std::ios_base::openmode mode)
{
// Disallow simultaneous use of the stream buffer for writing and reading.
if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
throw std::invalid_argument("this combination of modes on container stream not supported");
}
/// <summary>
/// Determine if the request can be satisfied.
/// </summary>
bool can_satisfy(size_t)
{
// We can always satisfy a read, at least partially, unless the
// read position is at the very end of the buffer.
return (in_avail() > 0);
}
/// <summary>
/// Reads a byte from the stream and returns it as int_type.
/// Note: This routine shall only be called if can_satisfy() returned true.
/// </summary>
int_type read_byte(bool advance = true)
{
_CharType value;
auto read_size = this->read(&value, 1, advance);
return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
}
/// <summary>
/// Reads up to count characters into ptr and returns the count of characters copied.
/// The return value (actual characters copied) could be <= count.
/// Note: This routine shall only be called if can_satisfy() returned true.
/// </summary>
size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
{
if (!can_satisfy(count)) return 0;
msl::safeint3::SafeInt<size_t> request_size(count);
msl::safeint3::SafeInt<size_t> read_size = request_size.Min(in_avail());
size_t newPos = m_current_position + read_size;
auto readBegin = std::begin(m_data) + m_current_position;
auto readEnd = std::begin(m_data) + newPos;
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
// Avoid warning C4996: Use checked iterators under SECURE_SCL
std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType*>(ptr, count));
#else
std::copy(readBegin, readEnd, ptr);
#endif // _WIN32
if (advance)
{
update_current_position(newPos);
}
return (size_t)read_size;
}
/// <summary>
/// Write count characters from the ptr into the stream buffer
/// </summary>
size_t write(const _CharType* ptr, size_t count)
{
if (!this->can_write() || (count == 0)) return 0;
auto newSize = m_current_position + count;
// Allocate space
resize_for_write(newSize);
// Copy the data
std::copy(ptr, ptr + count, std::begin(m_data) + m_current_position);
// Update write head and satisfy pending reads if any
update_current_position(newSize);
return count;
}
/// <summary>
/// Resize the underlying container to match the new write head
/// </summary>
void resize_for_write(size_t newPos)
{
// Resize the container if required
if (newPos > m_data.size())
{
m_data.resize(newPos);
}
}
/// <summary>
/// Updates the write head to the new position
/// </summary>
void update_current_position(size_t newPos)
{
// The new write head
m_current_position = newPos;
_ASSERTE(m_current_position <= m_data.size());
}
// The actual data store
_CollectionType m_data;
// Read/write head
size_t m_current_position;
};
} // namespace details
/// <summary>
/// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading
/// sequences of characters. Note that it cannot be used as a consumer producer buffer.
/// </summary>
/// <typeparam name="_CollectionType">
/// The type of the container.
/// </typeparam>
/// <remarks>
/// This is a reference-counted version of <c>basic_container_buffer</c>.
/// </remarks>
template<typename _CollectionType>
class container_buffer : public streambuf<typename _CollectionType::value_type>
{
public:
typedef typename _CollectionType::value_type char_type;
/// <summary>
/// Creates a container_buffer given a collection, copying its data into the buffer.
/// </summary>
/// <param name="data">The collection that is the starting point for the buffer</param>
/// <param name="mode">The I/O mode that the buffer should use (in / out)</param>
container_buffer(_CollectionType data, std::ios_base::openmode mode = std::ios_base::in)
: streambuf<typename _CollectionType::value_type>(
std::shared_ptr<details::basic_container_buffer<_CollectionType>>(
new streams::details::basic_container_buffer<_CollectionType>(std::move(data), mode)))
{
}
/// <summary>
/// Creates a container_buffer starting from an empty collection.
/// </summary>
/// <param name="mode">The I/O mode that the buffer should use (in / out)</param>
container_buffer(std::ios_base::openmode mode = std::ios_base::out)
: streambuf<typename _CollectionType::value_type>(
std::shared_ptr<details::basic_container_buffer<_CollectionType>>(
new details::basic_container_buffer<_CollectionType>(mode)))
{
}
_CollectionType& collection() const
{
auto listBuf = static_cast<details::basic_container_buffer<_CollectionType>*>(this->get_base().get());
return listBuf->collection();
}
};
/// <summary>
/// A static class to allow users to create input and out streams based off STL
/// collections. The sole purpose of this class to avoid users from having to know
/// anything about stream buffers.
/// </summary>
/// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
template<typename _CollectionType>
class container_stream
{
public:
typedef typename _CollectionType::value_type char_type;
typedef container_buffer<_CollectionType> buffer_type;
/// <summary>
/// Creates an input stream given an STL container.
/// </summary>
/// </param name="data">STL container to back the input stream.</param>
/// <returns>An input stream.</returns>
static concurrency::streams::basic_istream<char_type> open_istream(_CollectionType data)
{
return concurrency::streams::basic_istream<char_type>(buffer_type(std::move(data), std::ios_base::in));
}
/// <summary>
/// Creates an output stream using an STL container as the storage.
/// </summary>
/// <returns>An output stream.</returns>
static concurrency::streams::basic_ostream<char_type> open_ostream()
{
return concurrency::streams::basic_ostream<char_type>(buffer_type(std::ios_base::out));
}
};
/// <summary>
/// The stringstream allows an input stream to be constructed from std::string or std::wstring
/// For output streams the underlying string container could be retrieved using <c>buf-&gt;collection().</c>
/// </summary>
typedef container_stream<std::basic_string<char>> stringstream;
typedef stringstream::buffer_type stringstreambuf;
typedef container_stream<utility::string_t> wstringstream;
typedef wstringstream::buffer_type wstringstreambuf;
/// <summary>
/// The <c>bytestream</c> is a static class that allows an input stream to be constructed from any STL container.
/// </summary>
class bytestream
{
public:
/// <summary>
/// Creates a single byte character input stream given an STL container.
/// </summary>
/// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
/// <param name="data">STL container to back the input stream.</param>
/// <returns>An single byte character input stream.</returns>
template<typename _CollectionType>
static concurrency::streams::istream open_istream(_CollectionType data)
{
return concurrency::streams::istream(
streams::container_buffer<_CollectionType>(std::move(data), std::ios_base::in));
}
/// <summary>
/// Creates a single byte character output stream using an STL container as storage.
/// </summary>
/// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
/// <returns>A single byte character output stream.</returns>
template<typename _CollectionType>
static concurrency::streams::ostream open_ostream()
{
return concurrency::streams::ostream(streams::container_buffer<_CollectionType>());
}
};
} // namespace streams
} // namespace Concurrency

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,131 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Platform-dependent type definitions
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/details/cpprest_compat.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#ifndef _WIN32
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stdint.h>
#else
#include <cstdint>
#endif
#include "cpprest/details/SafeInt3.hpp"
namespace utility
{
#ifdef _WIN32
#define _UTF16_STRINGS
#endif
// We should be using a 64-bit size type for most situations that do
// not involve specifying the size of a memory allocation or buffer.
typedef uint64_t size64_t;
#ifndef _WIN32
typedef uint32_t HRESULT; // Needed for PPLX
#endif
#ifdef _UTF16_STRINGS
//
// On Windows, all strings are wide
//
typedef wchar_t char_t;
typedef std::wstring string_t;
#define _XPLATSTR(x) L##x
typedef std::wostringstream ostringstream_t;
typedef std::wofstream ofstream_t;
typedef std::wostream ostream_t;
typedef std::wistream istream_t;
typedef std::wifstream ifstream_t;
typedef std::wistringstream istringstream_t;
typedef std::wstringstream stringstream_t;
#define ucout std::wcout
#define ucin std::wcin
#define ucerr std::wcerr
#else
//
// On POSIX platforms, all strings are narrow
//
typedef char char_t;
typedef std::string string_t;
#define _XPLATSTR(x) x
typedef std::ostringstream ostringstream_t;
typedef std::ofstream ofstream_t;
typedef std::ostream ostream_t;
typedef std::istream istream_t;
typedef std::ifstream ifstream_t;
typedef std::istringstream istringstream_t;
typedef std::stringstream stringstream_t;
#define ucout std::cout
#define ucin std::cin
#define ucerr std::cerr
#endif // endif _UTF16_STRINGS
#ifndef _TURN_OFF_PLATFORM_STRING
// The 'U' macro can be used to create a string or character literal of the platform type, i.e. utility::char_t.
// If you are using a library causing conflicts with 'U' macro, it can be turned off by defining the macro
// '_TURN_OFF_PLATFORM_STRING' before including the C++ REST SDK header files, and e.g. use '_XPLATSTR' instead.
#define U(x) _XPLATSTR(x)
#endif // !_TURN_OFF_PLATFORM_STRING
} // namespace utility
typedef char utf8char;
typedef std::string utf8string;
typedef std::stringstream utf8stringstream;
typedef std::ostringstream utf8ostringstream;
typedef std::ostream utf8ostream;
typedef std::istream utf8istream;
typedef std::istringstream utf8istringstream;
#ifdef _UTF16_STRINGS
typedef wchar_t utf16char;
typedef std::wstring utf16string;
typedef std::wstringstream utf16stringstream;
typedef std::wostringstream utf16ostringstream;
typedef std::wostream utf16ostream;
typedef std::wistream utf16istream;
typedef std::wistringstream utf16istringstream;
#else
typedef char16_t utf16char;
typedef std::u16string utf16string;
typedef std::basic_stringstream<utf16char> utf16stringstream;
typedef std::basic_ostringstream<utf16char> utf16ostringstream;
typedef std::basic_ostream<utf16char> utf16ostream;
typedef std::basic_istream<utf16char> utf16istream;
typedef std::basic_istringstream<utf16char> utf16istringstream;
#endif
#if defined(_WIN32)
// Include on everything except Windows Desktop ARM, unless explicitly excluded.
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
#if defined(WINAPI_FAMILY)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && defined(_M_ARM)
#define CPPREST_EXCLUDE_WEBSOCKETS
#endif
#else
#if defined(_M_ARM)
#define CPPREST_EXCLUDE_WEBSOCKETS
#endif
#endif
#endif
#endif

View File

@ -0,0 +1,101 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Standard macros and definitions.
* This header has minimal dependency on windows headers and is safe for use in the public API
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#if defined(_WIN32)
#if _MSC_VER >= 1900
#define CPPREST_NOEXCEPT noexcept
#define CPPREST_CONSTEXPR constexpr
#else
#define CPPREST_NOEXCEPT
#define CPPREST_CONSTEXPR const
#endif // _MSC_VER >= 1900
#include <sal.h>
#else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv
#define __declspec(x) __attribute__((x))
#define novtable /* no novtable equivalent */
#define __assume(x) \
do \
{ \
if (!(x)) __builtin_unreachable(); \
} while (false)
#define CPPREST_NOEXCEPT noexcept
#define CPPREST_CONSTEXPR constexpr
#include <assert.h>
#define _ASSERTE(x) assert(x)
// No SAL on non Windows platforms
#include "cpprest/details/nosal.h"
#if !defined(__cdecl)
#if defined(cdecl)
#define __cdecl __attribute__((cdecl))
#else // ^^^ defined cdecl ^^^ // vvv !defined cdecl vvv
#define __cdecl
#endif // defined cdecl
#endif // not defined __cdecl
#if defined(__ANDROID__)
// This is needed to disable the use of __thread inside the boost library.
// Android does not support thread local storage -- if boost is included
// without this macro defined, it will create references to __tls_get_addr
// which (while able to link) will not be available at runtime and prevent
// the .so from loading.
#if not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#endif // not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#endif // defined(__ANDROID__)
#ifdef __clang__
#include <cstdio>
#endif // __clang__
#endif // _WIN32
#ifdef _NO_ASYNCRTIMP
#define _ASYNCRTIMP
#define _ASYNCRTIMP_TYPEINFO
#else // ^^^ _NO_ASYNCRTIMP ^^^ // vvv !_NO_ASYNCRTIMP vvv
#ifdef _ASYNCRT_EXPORT
#ifdef _WIN32
#define _ASYNCRTIMP __declspec(dllexport)
#else
#define _ASYNCRTIMP __attribute__((visibility("default")))
#endif
#else // ^^^ _ASYNCRT_EXPORT ^^^ // vvv !_ASYNCRT_EXPORT vvv
#ifdef _WIN32
#define _ASYNCRTIMP __declspec(dllimport)
#else
#define _ASYNCRTIMP
#endif
#endif // _ASYNCRT_EXPORT
#if defined(_WIN32)
#define _ASYNCRTIMP_TYPEINFO
#else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv
#define _ASYNCRTIMP_TYPEINFO __attribute__((visibility("default")))
#endif // _WIN32
#endif // _NO_ASYNCRTIMP
#ifdef CASABLANCA_DEPRECATION_NO_WARNINGS
#define CASABLANCA_DEPRECATED(x)
#else
#define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x))
#endif // CASABLANCA_DEPRECATION_NO_WARNINGS

View File

@ -0,0 +1,220 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* fileio.h
*
* Asynchronous I/O: stream buffer implementation details
*
* We're going to some lengths to avoid exporting C++ class member functions and implementation details across
* module boundaries, and the factoring requires that we keep the implementation details away from the main header
* files. The supporting functions, which are in this file, use C-like signatures to avoid as many issues as
* possible.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifdef _WIN32
#include <cstdint>
#endif
#include "cpprest/details/basic_types.h"
#include "pplx/pplxtasks.h"
namespace Concurrency
{
namespace streams
{
namespace details
{
/// <summary>
/// A record containing the essential private data members of a file stream,
/// in particular the parts that need to be shared between the public header
/// file and the implementation in the implementation file.
/// </summary>
struct _file_info
{
_ASYNCRTIMP _file_info(std::ios_base::openmode mode, size_t buffer_size)
: m_rdpos(0)
, m_wrpos(0)
, m_atend(false)
, m_buffer_size(buffer_size)
, m_buffer(nullptr)
, m_bufoff(0)
, m_bufsize(0)
, m_buffill(0)
, m_mode(mode)
{
}
// Positional data
size_t m_rdpos;
size_t m_wrpos;
bool m_atend;
// Input buffer
size_t m_buffer_size; // The intended size of the buffer to read into.
char* m_buffer;
size_t m_bufoff; // File position that the start of the buffer represents.
msl::safeint3::SafeInt<size_t> m_bufsize; // Buffer allocated size, as actually allocated.
size_t m_buffill; // Amount of file data actually in the buffer
std::ios_base::openmode m_mode;
pplx::extensibility::recursive_lock_t m_lock;
};
/// <summary>
/// This interface provides the necessary callbacks for completion events.
/// </summary>
class _filestream_callback
{
public:
virtual void on_opened(_In_ details::_file_info*) {}
virtual void on_closed() {}
virtual void on_error(const std::exception_ptr&) {}
virtual void on_completed(size_t) {}
protected:
virtual ~_filestream_callback() {}
};
} // namespace details
} // namespace streams
} // namespace Concurrency
extern "C"
{
/// <summary>
/// Open a file and create a streambuf instance to represent it.
/// </summary>
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
/// <param name="filename">The name of the file to open</param>
/// <param name="mode">A creation mode for the stream buffer</param>
/// <param name="prot">A file protection mode to use for the file stream (not supported on Linux)</param>
/// <returns><c>true</c> if the opening operation could be initiated, <c>false</c> otherwise.</returns>
/// <remarks>
/// True does not signal that the file will eventually be successfully opened, just that the process was started.
/// </remarks>
#if !defined(__cplusplus_winrt)
_ASYNCRTIMP bool __cdecl _open_fsb_str(_In_ concurrency::streams::details::_filestream_callback* callback,
const utility::char_t* filename,
std::ios_base::openmode mode,
int prot);
#endif
/// <summary>
/// Create a streambuf instance to represent a WinRT file.
/// </summary>
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
/// <param name="file">The file object</param>
/// <param name="mode">A creation mode for the stream buffer</param>
/// <returns><c>true</c> if the opening operation could be initiated, <c>false</c> otherwise.</returns>
/// <remarks>
/// True does not signal that the file will eventually be successfully opened, just that the process was started.
/// This is only available for WinRT.
/// </remarks>
#if defined(__cplusplus_winrt)
_ASYNCRTIMP bool __cdecl _open_fsb_stf_str(_In_ concurrency::streams::details::_filestream_callback* callback,
::Windows::Storage::StorageFile ^ file,
std::ios_base::openmode mode,
int prot);
#endif
/// <summary>
/// Close a file stream buffer.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
/// <returns><c>true</c> if the closing operation could be initiated, <c>false</c> otherwise.</returns>
/// <remarks>
/// True does not signal that the file will eventually be successfully closed, just that the process was started.
/// </remarks>
_ASYNCRTIMP bool __cdecl _close_fsb_nolock(_In_ concurrency::streams::details::_file_info** info,
_In_ concurrency::streams::details::_filestream_callback* callback);
_ASYNCRTIMP bool __cdecl _close_fsb(_In_ concurrency::streams::details::_file_info** info,
_In_ concurrency::streams::details::_filestream_callback* callback);
/// <summary>
/// Write data from a buffer into the file stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is
/// completed.</param> <param name="ptr">A pointer to a buffer where the data should be placed</param> <param
/// name="count">The size (in characters) of the buffer</param> <returns>0 if the read request is still outstanding,
/// -1 if the request failed, otherwise the size of the data read into the buffer</returns>
_ASYNCRTIMP size_t __cdecl _putn_fsb(_In_ concurrency::streams::details::_file_info* info,
_In_ concurrency::streams::details::_filestream_callback* callback,
const void* ptr,
size_t count,
size_t char_size);
/// <summary>
/// Read data from a file stream into a buffer
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is
/// completed.</param> <param name="ptr">A pointer to a buffer where the data should be placed</param> <param
/// name="count">The size (in characters) of the buffer</param> <returns>0 if the read request is still outstanding,
/// -1 if the request failed, otherwise the size of the data read into the buffer</returns>
_ASYNCRTIMP size_t __cdecl _getn_fsb(_In_ concurrency::streams::details::_file_info* info,
_In_ concurrency::streams::details::_filestream_callback* callback,
_Out_writes_(count) void* ptr,
_In_ size_t count,
size_t char_size);
/// <summary>
/// Flush all buffered data to the underlying file.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is
/// completed.</param> <returns><c>true</c> if the request was initiated</returns>
_ASYNCRTIMP bool __cdecl _sync_fsb(_In_ concurrency::streams::details::_file_info* info,
_In_ concurrency::streams::details::_filestream_callback* callback);
/// <summary>
/// Get the size of the underlying file.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <returns>The file size</returns>
_ASYNCRTIMP utility::size64_t __cdecl _get_size(_In_ concurrency::streams::details::_file_info* info,
size_t char_size);
/// <summary>
/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="pos">The new position (offset from the start) in the file stream</param>
/// <returns><c>true</c> if the request was initiated</returns>
_ASYNCRTIMP size_t __cdecl _seekrdpos_fsb(_In_ concurrency::streams::details::_file_info* info,
size_t pos,
size_t char_size);
/// <summary>
/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="pos">The new position (offset from the start) in the file stream</param>
/// <returns><c>true</c> if the request was initiated</returns>
_ASYNCRTIMP size_t __cdecl _seekrdtoend_fsb(_In_ concurrency::streams::details::_file_info* info,
int64_t offset,
size_t char_size);
/// <summary>
/// Adjust the internal buffers and pointers when the application seeks to a new write location in the stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="pos">The new position (offset from the start) in the file stream</param>
/// <returns><c>true</c> if the request was initiated</returns>
_ASYNCRTIMP size_t __cdecl _seekwrpos_fsb(_In_ concurrency::streams::details::_file_info* info,
size_t pos,
size_t char_size);
}

View File

@ -0,0 +1,199 @@
#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(MultiStatus, 207, _XPLATSTR("Multi-Status"))
DAT(AlreadyReported, 208, _XPLATSTR("Already Reported"))
DAT(IMUsed, 226, _XPLATSTR("IM Used"))
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(PermanentRedirect, 308, _XPLATSTR("Permanent 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(MisdirectedRequest, 421, _XPLATSTR("Misdirected Request"))
DAT(UnprocessableEntity, 422, _XPLATSTR("Unprocessable Entity"))
DAT(Locked, 423, _XPLATSTR("Locked"))
DAT(FailedDependency, 424, _XPLATSTR("Failed Dependency"))
DAT(UpgradeRequired, 426, _XPLATSTR("Upgrade Required"))
DAT(PreconditionRequired, 428, _XPLATSTR("Precondition Required"))
DAT(TooManyRequests, 429, _XPLATSTR("Too Many Requests"))
DAT(RequestHeaderFieldsTooLarge, 431, _XPLATSTR("Request Header Fields Too Large"))
DAT(UnavailableForLegalReasons, 451, _XPLATSTR("Unavailable For Legal Reasons"))
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"))
DAT(VariantAlsoNegotiates, 506, _XPLATSTR("Variant Also Negotiates"))
DAT(InsufficientStorage, 507, _XPLATSTR("Insufficient Storage"))
DAT(LoopDetected, 508, _XPLATSTR("Loop Detected"))
DAT(NotExtended, 510, _XPLATSTR("Not Extended"))
DAT(NetworkAuthenticationRequired, 511, _XPLATSTR("Network Authentication Required"))
#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(access_control_allow_origin, "Access-Control-Allow-Origin")
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(client_credentials, "client_credentials")
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,48 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Implementation Details of the http.h layer of messaging
*
* Functions and types for interoperating with http.h from modern C++
* This file includes windows definitions and should not be included in a public header
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/details/basic_types.h"
#include "cpprest/http_msg.h"
namespace web
{
namespace http
{
namespace details
{
namespace chunked_encoding
{
// Transfer-Encoding: chunked support
static const size_t additional_encoding_space = 12;
static const size_t data_offset = additional_encoding_space - 2;
// Add the data necessary for properly sending data with transfer-encoding: chunked.
//
// There are up to 12 additional bytes needed for each chunk:
//
// The last chunk requires 5 bytes, and is fixed.
// All other chunks require up to 8 bytes for the length, and four for the two CRLF
// delimiters.
//
_ASYNCRTIMP size_t __cdecl add_chunked_delimiters(_Out_writes_(buffer_size) uint8_t* data,
_In_ size_t buffer_size,
size_t bytes_read);
} // namespace chunked_encoding
} // namespace details
} // namespace http
} // namespace web

View File

@ -0,0 +1,72 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: interface to implement HTTP server to service http_listeners.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
#error "Error: http server APIs are not supported in XP"
#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA
#include "cpprest/http_listener.h"
namespace web
{
namespace http
{
namespace experimental
{
namespace details
{
/// <summary>
/// Interface http listeners interact with for receiving and responding to http requests.
/// </summary>
class http_server
{
public:
/// <summary>
/// Release any held resources.
/// </summary>
virtual ~http_server() {};
/// <summary>
/// Start listening for incoming requests.
/// </summary>
virtual pplx::task<void> start() = 0;
/// <summary>
/// Registers an http listener.
/// </summary>
virtual pplx::task<void> register_listener(
_In_ web::http::experimental::listener::details::http_listener_impl* pListener) = 0;
/// <summary>
/// Unregisters an http listener.
/// </summary>
virtual pplx::task<void> unregister_listener(
_In_ web::http::experimental::listener::details::http_listener_impl* pListener) = 0;
/// <summary>
/// Stop processing and listening for incoming requests.
/// </summary>
virtual pplx::task<void> stop() = 0;
/// <summary>
/// Asynchronously sends the specified http response.
/// </summary>
/// <param name="response">The http_response to send.</param>
/// <returns>A operation which is completed once the response has been sent.</returns>
virtual pplx::task<void> respond(http::http_response response) = 0;
};
} // namespace details
} // namespace experimental
} // namespace http
} // namespace web

View File

@ -0,0 +1,93 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: exposes the entry points to the http server transport apis.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
#error "Error: http server APIs are not supported in XP"
#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA
#include "cpprest/http_listener.h"
#include <memory>
namespace web
{
namespace http
{
namespace experimental
{
namespace details
{
class http_server;
/// <summary>
/// Singleton class used to register for http requests and send responses.
///
/// The lifetime is tied to http listener registration. When the first listener registers an instance is created
/// and when the last one unregisters the receiver stops and is destroyed. It can be started back up again if
/// listeners are again registered.
/// </summary>
class http_server_api
{
public:
/// <summary>
/// Returns whether or not any listeners are registered.
/// </summary>
static bool __cdecl has_listener();
/// <summary>
/// Registers a HTTP server API.
/// </summary>
static void __cdecl register_server_api(std::unique_ptr<http_server> server_api);
/// <summary>
/// Clears the http server API.
/// </summary>
static void __cdecl unregister_server_api();
/// <summary>
/// Registers a listener for HTTP requests and starts receiving.
/// </summary>
static pplx::task<void> __cdecl register_listener(
_In_ web::http::experimental::listener::details::http_listener_impl* pListener);
/// <summary>
/// Unregisters the given listener and stops listening for HTTP requests.
/// </summary>
static pplx::task<void> __cdecl unregister_listener(
_In_ web::http::experimental::listener::details::http_listener_impl* pListener);
/// <summary>
/// Gets static HTTP server API. Could be null if no registered listeners.
/// </summary>
static http_server* __cdecl server_api();
private:
/// Used to lock access to the server api registration
static pplx::extensibility::critical_section_t s_lock;
/// Registers a server API set -- this assumes the lock has already been taken
static void unsafe_register_server_api(std::unique_ptr<http_server> server_api);
// Static instance of the HTTP server API.
static std::unique_ptr<http_server> s_server_api;
/// Number of registered listeners;
static pplx::details::atomic_long s_registrations;
// Static only class. No creation.
http_server_api();
};
} // namespace details
} // namespace experimental
} // namespace http
} // namespace web

View File

@ -0,0 +1,77 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
***/
#pragma once
// selected MS SAL annotations
#ifdef _In_
#undef _In_
#endif
#define _In_
#ifdef _Inout_
#undef _Inout_
#endif
#define _Inout_
#ifdef _Out_
#undef _Out_
#endif
#define _Out_
#ifdef _In_z_
#undef _In_z_
#endif
#define _In_z_
#ifdef _Out_z_
#undef _Out_z_
#endif
#define _Out_z_
#ifdef _Inout_z_
#undef _Inout_z_
#endif
#define _Inout_z_
#ifdef _In_opt_
#undef _In_opt_
#endif
#define _In_opt_
#ifdef _Out_opt_
#undef _Out_opt_
#endif
#define _Out_opt_
#ifdef _Inout_opt_
#undef _Inout_opt_
#endif
#define _Inout_opt_
#ifdef _Out_writes_
#undef _Out_writes_
#endif
#define _Out_writes_(x)
#ifdef _Out_writes_opt_
#undef _Out_writes_opt_
#endif
#define _Out_writes_opt_(x)
#ifdef _In_reads_
#undef _In_reads_
#endif
#define _In_reads_(x)
#ifdef _Inout_updates_bytes_
#undef _Inout_updates_bytes_
#endif
#define _Inout_updates_bytes_(x)

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,225 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* utility classes used by the different web:: clients
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/asyncrt_utils.h"
#include "cpprest/uri.h"
namespace web
{
namespace details
{
class zero_memory_deleter
{
public:
_ASYNCRTIMP void operator()(::utility::string_t* data) const;
};
typedef std::unique_ptr<::utility::string_t, zero_memory_deleter> plaintext_string;
#ifdef _WIN32
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
#ifdef __cplusplus_winrt
class winrt_encryption
{
public:
winrt_encryption() = default;
_ASYNCRTIMP winrt_encryption(const std::wstring& data);
_ASYNCRTIMP plaintext_string decrypt() const;
private:
::pplx::task<Windows::Storage::Streams::IBuffer ^> m_buffer;
};
#else // ^^^ __cplusplus_winrt ^^^ // vvv !__cplusplus_winrt vvv
class win32_encryption
{
public:
win32_encryption() = default;
_ASYNCRTIMP win32_encryption(const std::wstring& data);
_ASYNCRTIMP ~win32_encryption();
_ASYNCRTIMP plaintext_string decrypt() const;
private:
std::vector<char> m_buffer;
size_t m_numCharacters;
};
#endif // __cplusplus_winrt
#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
#endif // _WIN32
} // namespace details
/// <summary>
/// Represents a set of user credentials (user name and password) to be used
/// for authentication.
/// </summary>
class credentials
{
public:
/// <summary>
/// Constructs an empty set of credentials without a user name or password.
/// </summary>
credentials() {}
/// <summary>
/// Constructs credentials from given user name and password.
/// </summary>
/// <param name="username">User name as a string.</param>
/// <param name="password">Password as a string.</param>
credentials(utility::string_t username, const utility::string_t& password)
: m_username(std::move(username)), m_password(password)
{
}
/// <summary>
/// The user name associated with the credentials.
/// </summary>
/// <returns>A string containing the user name.</returns>
const utility::string_t& username() const { return m_username; }
/// <summary>
/// The password for the user name associated with the credentials.
/// </summary>
/// <returns>A string containing the password.</returns>
CASABLANCA_DEPRECATED(
"This API is deprecated for security reasons to avoid unnecessary password copies stored in plaintext.")
utility::string_t password() const
{
#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
return utility::string_t(*m_password.decrypt());
#else
return m_password;
#endif
}
/// <summary>
/// Checks if credentials have been set
/// </summary>
/// <returns><c>true</c> if user name and password is set, <c>false</c> otherwise.</returns>
bool is_set() const { return !m_username.empty(); }
details::plaintext_string _internal_decrypt() const
{
// Encryption APIs not supported on XP
#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
return m_password.decrypt();
#else
return details::plaintext_string(new ::utility::string_t(m_password));
#endif
}
private:
::utility::string_t m_username;
#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
#if defined(__cplusplus_winrt)
details::winrt_encryption m_password;
#else
details::win32_encryption m_password;
#endif
#else
::utility::string_t m_password;
#endif
};
/// <summary>
/// web_proxy represents the concept of the web proxy, which can be auto-discovered,
/// disabled, or specified explicitly by the user.
/// </summary>
class web_proxy
{
enum web_proxy_mode_internal
{
use_default_,
use_auto_discovery_,
disabled_,
user_provided_
};
public:
enum web_proxy_mode
{
use_default = use_default_,
use_auto_discovery = use_auto_discovery_,
disabled = disabled_
};
/// <summary>
/// Constructs a proxy with the default settings.
/// </summary>
web_proxy() : m_address(), m_mode(use_default_) {}
/// <summary>
/// Creates a proxy with specified mode.
/// </summary>
/// <param name="mode">Mode to use.</param>
web_proxy(web_proxy_mode mode) : m_address(), m_mode(static_cast<web_proxy_mode_internal>(mode)) {}
/// <summary>
/// Creates a proxy explicitly with provided address.
/// </summary>
/// <param name="address">Proxy URI to use.</param>
web_proxy(uri address) : m_address(address), m_mode(user_provided_) {}
/// <summary>
/// Gets this proxy's URI address. Returns an empty URI if not explicitly set by user.
/// </summary>
/// <returns>A reference to this proxy's URI.</returns>
const uri& address() const { return m_address; }
/// <summary>
/// Gets the credentials used for authentication with this proxy.
/// </summary>
/// <returns>Credentials to for this proxy.</returns>
const web::credentials& credentials() const { return m_credentials; }
/// <summary>
/// Sets the credentials to use for authentication with this proxy.
/// </summary>
/// <param name="cred">Credentials to use for this proxy.</param>
void set_credentials(web::credentials cred)
{
if (m_mode == disabled_)
{
throw std::invalid_argument("Cannot attach credentials to a disabled proxy");
}
m_credentials = std::move(cred);
}
/// <summary>
/// Checks if this proxy was constructed with default settings.
/// </summary>
/// <returns>True if default, false otherwise.</param>
bool is_default() const { return m_mode == use_default_; }
/// <summary>
/// Checks if using a proxy is disabled.
/// </summary>
/// <returns>True if disabled, false otherwise.</returns>
bool is_disabled() const { return m_mode == disabled_; }
/// <summary>
/// Checks if the auto discovery protocol, WPAD, is to be used.
/// </summary>
/// <returns>True if auto discovery enabled, false otherwise.</returns>
bool is_auto_discovery() const { return m_mode == use_auto_discovery_; }
/// <summary>
/// Checks if a proxy address is explicitly specified by the user.
/// </summary>
/// <returns>True if a proxy address was explicitly specified, false otherwise.</returns>
bool is_specified() const { return m_mode == user_provided_; }
private:
web::uri m_address;
web_proxy_mode_internal m_mode;
web::credentials m_credentials;
};
} // namespace web

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,766 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Client-side APIs.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_HTTP_CLIENT_H
#define CASA_HTTP_CLIENT_H
#if defined(__cplusplus_winrt)
#if !defined(__WRL_NO_DEFAULT_LIB__)
#define __WRL_NO_DEFAULT_LIB__
#endif
#include <msxml6.h>
#include <wrl.h>
namespace web
{
namespace http
{
namespace client
{
typedef IXMLHTTPRequest2* native_handle;
}
} // namespace http
} // namespace web
#else
namespace web
{
namespace http
{
namespace client
{
typedef void* native_handle;
}
} // namespace http
} // namespace web
#endif // __cplusplus_winrt
#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/basic_types.h"
#include "cpprest/details/web_utilities.h"
#include "cpprest/http_msg.h"
#include "cpprest/json.h"
#include "cpprest/uri.h"
#include "pplx/pplxtasks.h"
#include <limits>
#include <memory>
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
#include "cpprest/oauth1.h"
#endif
#include "cpprest/oauth2.h"
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#endif
#include "boost/asio/ssl.hpp"
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif
/// The web namespace contains functionality common to multiple protocols like HTTP and WebSockets.
namespace web
{
/// Declarations and functionality for the HTTP protocol.
namespace http
{
/// HTTP client side library.
namespace client
{
// credentials and web_proxy class has been moved from web::http::client namespace to web namespace.
// The below using declarations ensure we don't break existing code.
// Please use the web::credentials and web::web_proxy class going forward.
using web::credentials;
using web::web_proxy;
/// <summary>
/// HTTP client configuration class, used to set the possible configuration options
/// used to create an http_client instance.
/// </summary>
class http_client_config
{
public:
http_client_config()
: m_guarantee_order(false)
, m_timeout(std::chrono::seconds(30))
, m_chunksize(0)
, m_request_compressed(false)
#if !defined(__cplusplus_winrt)
, m_validate_certificates(true)
#endif
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
, m_tlsext_sni_enabled(true)
#endif
#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
, m_buffer_request(false)
#endif
, m_max_redirects(10)
, m_https_to_http_redirects(false)
{
}
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
/// <summary>
/// Get OAuth 1.0 configuration.
/// </summary>
/// <returns>Shared pointer to OAuth 1.0 configuration.</returns>
const std::shared_ptr<oauth1::experimental::oauth1_config> oauth1() const { return m_oauth1; }
/// <summary>
/// Set OAuth 1.0 configuration.
/// </summary>
/// <param name="config">OAuth 1.0 configuration to set.</param>
void set_oauth1(oauth1::experimental::oauth1_config config)
{
m_oauth1 = std::make_shared<oauth1::experimental::oauth1_config>(std::move(config));
}
#endif
/// <summary>
/// Get OAuth 2.0 configuration.
/// </summary>
/// <returns>Shared pointer to OAuth 2.0 configuration.</returns>
const std::shared_ptr<oauth2::experimental::oauth2_config> oauth2() const { return m_oauth2; }
/// <summary>
/// Set OAuth 2.0 configuration.
/// </summary>
/// <param name="config">OAuth 2.0 configuration to set.</param>
void set_oauth2(oauth2::experimental::oauth2_config config)
{
m_oauth2 = std::make_shared<oauth2::experimental::oauth2_config>(std::move(config));
}
/// <summary>
/// Get the web proxy object
/// </summary>
/// <returns>A reference to the web proxy object.</returns>
const web_proxy& proxy() const { return m_proxy; }
/// <summary>
/// Set the web proxy object
/// </summary>
/// <param name="proxy">A reference to the web proxy object.</param>
void set_proxy(web_proxy proxy) { m_proxy = std::move(proxy); }
/// <summary>
/// Get the client credentials
/// </summary>
/// <returns>A reference to the client credentials.</returns>
const http::client::credentials& credentials() const { return m_credentials; }
/// <summary>
/// Set the client credentials
/// </summary>
/// <param name="cred">A reference to the client credentials.</param>
void set_credentials(const http::client::credentials& cred) { m_credentials = cred; }
/// <summary>
/// Get the 'guarantee order' property
/// </summary>
/// <returns>The value of the property.</returns>
bool guarantee_order() const { return m_guarantee_order; }
/// <summary>
/// Set the 'guarantee order' property
/// </summary>
/// <param name="guarantee_order">The value of the property.</param>
CASABLANCA_DEPRECATED(
"Confusing API will be removed in future releases. If you need to order HTTP requests use task continuations.")
void set_guarantee_order(bool guarantee_order) { m_guarantee_order = guarantee_order; }
/// <summary>
/// Get the timeout
/// </summary>
/// <returns>The timeout (in seconds) used for each send and receive operation on the client.</returns>
utility::seconds timeout() const { return std::chrono::duration_cast<utility::seconds>(m_timeout); }
/// <summary>
/// Get the timeout
/// </summary>
/// <returns>The timeout (in whatever duration) used for each send and receive operation on the client.</returns>
template<class T>
T timeout() const
{
return std::chrono::duration_cast<T>(m_timeout);
}
/// <summary>
/// Set the timeout
/// </summary>
/// <param name="timeout">The timeout (duration from microseconds range and up) used for each send and receive
/// operation on the client.</param>
template<class T>
void set_timeout(const T& timeout)
{
m_timeout = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
}
/// <summary>
/// Get the client chunk size.
/// </summary>
/// <returns>The internal buffer size used by the http client when sending and receiving data from the
/// network.</returns>
size_t chunksize() const { return m_chunksize == 0 ? 64 * 1024 : m_chunksize; }
/// <summary>
/// Sets the client chunk size.
/// </summary>
/// <param name="size">The internal buffer size used by the http client when sending and receiving data from the
/// network.</param> <remarks>This is a hint -- an implementation may disregard the setting and use some other chunk
/// size.</remarks>
void set_chunksize(size_t size) { m_chunksize = size; }
/// <summary>
/// Returns true if the default chunk size is in use.
/// <remarks>If true, implementations are allowed to choose whatever size is best.</remarks>
/// </summary>
/// <returns>True if default, false if set by user.</returns>
bool is_default_chunksize() const { return m_chunksize == 0; }
/// <summary>
/// Checks if requesting a compressed response using Content-Encoding is turned on, the default is off.
/// </summary>
/// <returns>True if a content-encoded compressed response is allowed, false otherwise</returns>
bool request_compressed_response() const { return m_request_compressed; }
/// <summary>
/// Request that the server respond with a compressed body using Content-Encoding; to use Transfer-Encoding, do not
/// set this, and specify a vector of <see cref="web::http::details::compression::decompress_factory" /> pointers
/// to the set_decompress_factories method of the <see cref="web::http::http_request" /> object for the request.
/// If true and the server does not support compression, this will have no effect.
/// The response body is internally decompressed before the consumer receives the data.
/// </summary>
/// <param name="request_compressed">True to turn on content-encoded response body compression, false
/// otherwise.</param> <remarks>Please note there is a performance cost due to copying the request data. Currently
/// only supported on Windows and OSX.</remarks>
void set_request_compressed_response(bool request_compressed) { m_request_compressed = request_compressed; }
#if !defined(__cplusplus_winrt)
/// <summary>
/// Gets the server certificate validation property.
/// </summary>
/// <returns>True if certificates are to be verified, false otherwise.</returns>
bool validate_certificates() const { return m_validate_certificates; }
/// <summary>
/// Sets the server certificate validation property.
/// </summary>
/// <param name="validate_certs">False to turn ignore all server certificate validation errors, true
/// otherwise.</param> <remarks>Note ignoring certificate errors can be dangerous and should be done with
/// caution.</remarks>
void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
#endif
#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
/// <summary>
/// Checks if request data buffering is turned on, the default is off.
/// </summary>
/// <returns>True if buffering is enabled, false otherwise</returns>
bool buffer_request() const { return m_buffer_request; }
/// <summary>
/// Sets the request buffering property.
/// If true, in cases where the request body/stream doesn't support seeking the request data will be buffered.
/// This can help in situations where an authentication challenge might be expected.
/// </summary>
/// <param name="buffer_request">True to turn on buffer, false otherwise.</param>
/// <remarks>Please note there is a performance cost due to copying the request data.</remarks>
void set_buffer_request(bool buffer_request) { m_buffer_request = buffer_request; }
#endif
/// <summary>
/// Get the maximum number of redirects to follow automatically.
/// A value of 0 indicates that no automatic redirection is performed.
/// </summary>
/// <returns>The maximum number of redirects to follow automatically.</returns>
/// <remarks>This is a hint -- an implementation may enforce a lower value.</remarks>
size_t max_redirects() const { return m_max_redirects; }
/// <summary>
/// Set the maximum number of redirects to follow automatically.
/// A value of 0 indicates that no automatic redirection is performed.
/// </summary>
/// <param name="max_redirects">The maximum number of redirects to follow automatically.</param>
/// <remarks>This is a hint -- an implementation may enforce a lower value.</remarks>
void set_max_redirects(size_t max_redirects) { m_max_redirects = max_redirects; }
/// <summary>
/// Checks if HTTPS to HTTP redirects are automatically followed.
/// </summary>
/// <returns>True if HTTPS to HTTP redirects are automatically followed, false otherwise.</returns>
bool https_to_http_redirects() const { return m_https_to_http_redirects; }
/// <summary>
/// Sets if HTTPS to HTTP redirects are automatically followed.
/// </summary>
/// <param name="https_to_http_redirects">True if HTTPS to HTTP redirects are to be automatically
/// followed, false otherwise.</param>
void set_https_to_http_redirects(bool https_to_http_redirects)
{
m_https_to_http_redirects = https_to_http_redirects;
}
/// <summary>
/// Sets a callback to enable custom setting of platform specific options.
/// </summary>
/// <remarks>
/// The native_handle is the following type depending on the underlying platform:
/// Windows Desktop, WinHTTP - HINTERNET (session)
/// </remarks>
/// <param name="callback">A user callback allowing for customization of the session</param>
void set_nativesessionhandle_options(const std::function<void(native_handle)>& callback)
{
m_set_user_nativesessionhandle_options = callback;
}
/// <summary>
/// Invokes a user's callback to allow for customization of the session.
/// </summary>
/// <remarks>Internal Use Only</remarks>
/// <param name="handle">A internal implementation handle.</param>
void _invoke_nativesessionhandle_options(native_handle handle) const
{
if (m_set_user_nativesessionhandle_options) m_set_user_nativesessionhandle_options(handle);
}
/// <summary>
/// Sets a callback to enable custom setting of platform specific options.
/// </summary>
/// <remarks>
/// The native_handle is the following type depending on the underlying platform:
/// Windows Desktop, WinHTTP - HINTERNET
/// Windows Runtime, WinRT - IXMLHTTPRequest2 *
/// All other platforms, Boost.Asio:
/// https - boost::asio::ssl::stream<boost::asio::ip::tcp::socket &> *
/// http - boost::asio::ip::tcp::socket *
/// </remarks>
/// <param name="callback">A user callback allowing for customization of the request</param>
void set_nativehandle_options(const std::function<void(native_handle)>& callback)
{
m_set_user_nativehandle_options = callback;
}
/// <summary>
/// Invokes a user's callback to allow for customization of the request.
/// </summary>
/// <param name="handle">A internal implementation handle.</param>
void invoke_nativehandle_options(native_handle handle) const
{
if (m_set_user_nativehandle_options) m_set_user_nativehandle_options(handle);
}
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
/// <summary>
/// Sets a callback to enable custom setting of the ssl context, at construction time.
/// </summary>
/// <param name="callback">A user callback allowing for customization of the ssl context at construction
/// time.</param>
void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& callback)
{
m_ssl_context_callback = callback;
}
/// <summary>
/// Gets the user's callback to allow for customization of the ssl context.
/// </summary>
const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
{
return m_ssl_context_callback;
}
/// <summary>
/// Gets the TLS extension server name indication (SNI) status.
/// </summary>
/// <returns>True if TLS server name indication is enabled, false otherwise.</returns>
bool is_tlsext_sni_enabled() const { return m_tlsext_sni_enabled; }
/// <summary>
/// Sets the TLS extension server name indication (SNI) status.
/// </summary>
/// <param name="tlsext_sni_enabled">False to disable the TLS (ClientHello) extension for server name indication,
/// true otherwise.</param> <remarks>Note: This setting is enabled by default as it is required in most virtual
/// hosting scenarios.</remarks>
void set_tlsext_sni_enabled(bool tlsext_sni_enabled) { m_tlsext_sni_enabled = tlsext_sni_enabled; }
#endif
private:
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
std::shared_ptr<oauth1::experimental::oauth1_config> m_oauth1;
#endif
std::shared_ptr<oauth2::experimental::oauth2_config> m_oauth2;
web_proxy m_proxy;
http::client::credentials m_credentials;
// Whether or not to guarantee ordering, i.e. only using one underlying TCP connection.
bool m_guarantee_order;
std::chrono::microseconds m_timeout;
size_t m_chunksize;
bool m_request_compressed;
#if !defined(__cplusplus_winrt)
// IXmlHttpRequest2 doesn't allow configuration of certificate verification.
bool m_validate_certificates;
#endif
std::function<void(native_handle)> m_set_user_nativehandle_options;
std::function<void(native_handle)> m_set_user_nativesessionhandle_options;
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
bool m_tlsext_sni_enabled;
#endif
#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
bool m_buffer_request;
#endif
size_t m_max_redirects;
bool m_https_to_http_redirects;
};
class http_pipeline;
/// <summary>
/// HTTP client class, used to maintain a connection to an HTTP service for an extended session.
/// </summary>
class http_client
{
public:
/// <summary>
/// Creates a new http_client connected to specified uri.
/// </summary>
/// <param name="base_uri">A string representation of the base uri to be used for all requests. Must start with
/// either "http://" or "https://"</param>
_ASYNCRTIMP http_client(const uri& base_uri);
/// <summary>
/// Creates a new http_client connected to specified uri.
/// </summary>
/// <param name="base_uri">A string representation of the base uri to be used for all requests. Must start with
/// either "http://" or "https://"</param> <param name="client_config">The http client configuration object
/// containing the possible configuration options to initialize the <c>http_client</c>. </param>
_ASYNCRTIMP http_client(const uri& base_uri, const http_client_config& client_config);
/// <summary>
/// Note the destructor doesn't necessarily close the connection and release resources.
/// The connection is reference counted with the http_responses.
/// </summary>
_ASYNCRTIMP ~http_client() CPPREST_NOEXCEPT;
/// <summary>
/// Gets the base URI.
/// </summary>
/// <returns>
/// A base URI initialized in constructor
/// </returns>
_ASYNCRTIMP const uri& base_uri() const;
/// <summary>
/// Get client configuration object
/// </summary>
/// <returns>A reference to the client configuration object.</returns>
_ASYNCRTIMP const http_client_config& client_config() const;
/// <summary>
/// Adds an HTTP pipeline stage to the client.
/// </summary>
/// <param name="handler">A function object representing the pipeline stage.</param>
_ASYNCRTIMP void add_handler(const std::function<pplx::task<http_response> __cdecl(
http_request, std::shared_ptr<http::http_pipeline_stage>)>& handler);
/// <summary>
/// Adds an HTTP pipeline stage to the client.
/// </summary>
/// <param name="stage">A shared pointer to a pipeline stage.</param>
_ASYNCRTIMP void add_handler(const std::shared_ptr<http::http_pipeline_stage>& stage);
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="request">Request to send.</param>
/// <param name="token">Cancellation token for cancellation of this request operation.</param>
/// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
_ASYNCRTIMP pplx::task<http_response> request(
http_request request, const pplx::cancellation_token& token = pplx::cancellation_token::none());
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="token">Cancellation token for cancellation of this request operation.</param>
/// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="token">Cancellation token for cancellation of this request operation.</param>
/// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(path_query_fragment);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body_data">The data to be used as the message body, represented using the json
/// object library.</param> <param name="token">Cancellation token for cancellation of this request
/// operation.</param> <returns>An asynchronous operation that is completed once a response from the request is
/// received.</returns>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const json::value& body_data,
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(path_query_fragment);
msg.set_body(body_data);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes the
/// character encoding of the string is UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
/// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
/// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
/// once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utf8string& path_query_fragment,
const utf8string& body_data,
const utf8string& content_type = "text/plain; charset=utf-8",
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
msg.set_body(body_data, content_type);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes the
/// character encoding of the string is UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
/// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
/// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
/// once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utf8string& path_query_fragment,
utf8string&& body_data,
const utf8string& content_type = "text/plain; charset=utf-8",
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
msg.set_body(std::move(body_data), content_type);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes the
/// character encoding of the string is UTF-16 will perform conversion to UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
/// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
/// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
/// once a response from the request is received.</returns>
pplx::task<http_response> request(
const method& mtd,
const utf16string& path_query_fragment,
const utf16string& body_data,
const utf16string& content_type = utility::conversions::to_utf16string("text/plain"),
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
msg.set_body(body_data, content_type);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes the
/// character encoding of the string is UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
/// operation that is completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utf8string& path_query_fragment,
const utf8string& body_data,
const pplx::cancellation_token& token)
{
return request(mtd, path_query_fragment, body_data, "text/plain; charset=utf-8", token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes the
/// character encoding of the string is UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
/// operation that is completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utf8string& path_query_fragment,
utf8string&& body_data,
const pplx::cancellation_token& token)
{
http_request msg(mtd);
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
msg.set_body(std::move(body_data), "text/plain; charset=utf-8");
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request with a string body. Assumes
/// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
/// operation that is completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utf16string& path_query_fragment,
const utf16string& body_data,
const pplx::cancellation_token& token)
{
return request(
mtd, path_query_fragment, body_data, ::utility::conversions::to_utf16string("text/plain"), token);
}
#if !defined(__cplusplus_winrt)
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
/// name="content_type">A string holding the MIME type of the message body.</param> <param name="token">Cancellation
/// token for cancellation of this request operation.</param> <returns>A task that is completed once a response from
/// the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const concurrency::streams::istream& body,
const utility::string_t& content_type = _XPLATSTR("application/octet-stream"),
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(path_query_fragment);
msg.set_body(body, content_type);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>A task that is
/// completed once a response from the request is received.</returns>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const concurrency::streams::istream& body,
const pplx::cancellation_token& token)
{
return request(mtd, path_query_fragment, body, _XPLATSTR("application/octet-stream"), token);
}
#endif // __cplusplus_winrt
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
/// name="content_length">Size of the message body.</param> <param name="content_type">A string holding the MIME
/// type of the message body.</param> <param name="token">Cancellation token for cancellation of this request
/// operation.</param> <returns>A task that is completed once a response from the request is received.</returns>
/// <remarks>Winrt requires to provide content_length.</remarks>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const concurrency::streams::istream& body,
size_t content_length,
const utility::string_t& content_type = _XPLATSTR("application/octet-stream"),
const pplx::cancellation_token& token = pplx::cancellation_token::none())
{
http_request msg(mtd);
msg.set_request_uri(path_query_fragment);
msg.set_body(body, content_length, content_type);
return request(msg, token);
}
/// <summary>
/// Asynchronously sends an HTTP request.
/// </summary>
/// <param name="mtd">HTTP request method.</param>
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
/// name="content_length">Size of the message body.</param> <param name="token">Cancellation token for cancellation
/// of this request operation.</param> <returns>A task that is completed once a response from the request is
/// received.</returns> <remarks>Winrt requires to provide content_length.</remarks>
pplx::task<http_response> request(const method& mtd,
const utility::string_t& path_query_fragment,
const concurrency::streams::istream& body,
size_t content_length,
const pplx::cancellation_token& token)
{
return request(mtd, path_query_fragment, body, content_length, _XPLATSTR("application/octet-stream"), token);
}
private:
std::shared_ptr<::web::http::client::http_pipeline> m_pipeline;
};
namespace details
{
#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
extern const utility::char_t* get_with_body_err_msg;
#endif
} // namespace details
} // namespace client
} // namespace http
} // namespace web
#endif

View File

@ -0,0 +1,326 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Compression and decompression interfaces
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
namespace web
{
namespace http
{
namespace compression
{
/// <summary>
/// Hint as to whether a compress or decompress call is meant to be the last for a particular HTTP request or reply
/// </summary>
enum operation_hint
{
is_last, // Used for the expected last compress() call, or for an expected single decompress() call
has_more // Used when further compress() calls will be made, or when multiple decompress() calls may be required
};
/// <summary>
/// Result structure for asynchronous compression and decompression operations
/// </summary>
struct operation_result
{
size_t input_bytes_processed; // From the input buffer
size_t output_bytes_produced; // To the output buffer
bool done; // For compress, set when 'last' is true and there was enough space to complete compression;
// for decompress, set if the end of the decompression stream has been reached
};
/// <summary>
/// Compression interface for use with HTTP requests
/// </summary>
class compress_provider
{
public:
virtual const utility::string_t& algorithm() const = 0;
virtual size_t compress(const uint8_t* input,
size_t input_size,
uint8_t* output,
size_t output_size,
operation_hint hint,
size_t& input_bytes_processed,
bool& done) = 0;
virtual pplx::task<operation_result> compress(
const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint) = 0;
virtual void reset() = 0;
virtual ~compress_provider() = default;
};
/// <summary>
/// Decompression interface for use with HTTP requests
/// </summary>
class decompress_provider
{
public:
virtual const utility::string_t& algorithm() const = 0;
virtual size_t decompress(const uint8_t* input,
size_t input_size,
uint8_t* output,
size_t output_size,
operation_hint hint,
size_t& input_bytes_processed,
bool& done) = 0;
virtual pplx::task<operation_result> decompress(
const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint) = 0;
virtual void reset() = 0;
virtual ~decompress_provider() = default;
};
/// <summary>
/// Factory interface for compressors for use with received HTTP requests
/// </summary>
class compress_factory
{
public:
virtual const utility::string_t& algorithm() const = 0;
virtual std::unique_ptr<compress_provider> make_compressor() const = 0;
virtual ~compress_factory() = default;
};
/// <summary>
/// Factory interface for decompressors for use with HTTP requests
/// </summary>
class decompress_factory
{
public:
virtual const utility::string_t& algorithm() const = 0;
virtual uint16_t weight() const = 0;
virtual std::unique_ptr<decompress_provider> make_decompressor() const = 0;
virtual ~decompress_factory() = default;
};
/// <summary>
/// Built-in compression support
/// </summary>
namespace builtin
{
/// <summary>
/// Test whether cpprestsdk was built with built-in compression support
/// <returns>True if cpprestsdk was built with built-in compression support, and false if not.</returns>
/// </summary>
_ASYNCRTIMP bool supported();
/// <summary>
// String constants for each built-in compression algorithm, for convenient use with the factory functions
/// </summary>
namespace algorithm
{
#if defined(_MSC_VER) && _MSC_VER < 1900
const utility::char_t* const GZIP = _XPLATSTR("gzip");
const utility::char_t* const DEFLATE = _XPLATSTR("deflate");
const utility::char_t* const BROTLI = _XPLATSTR("br");
#else // ^^^ VS2013 and before ^^^ // vvv VS2015+, and everything else vvv
constexpr const utility::char_t* const GZIP = _XPLATSTR("gzip");
constexpr const utility::char_t* const DEFLATE = _XPLATSTR("deflate");
constexpr const utility::char_t* const BROTLI = _XPLATSTR("br");
#endif
/// <summary>
/// Test whether cpprestsdk was built with built-in compression support and
/// the supplied string matches a supported built-in algorithm
/// <param name="algorithm">The name of the algorithm to test for built-in support.</param>
/// <returns>True if cpprestsdk was built with built-in compression support and
/// the supplied string matches a supported built-in algorithm, and false if not.</returns>
/// <summary>
_ASYNCRTIMP bool supported(const utility::string_t& algorithm);
} // namespace algorithm
/// <summary>
/// Factory function to instantiate a built-in compression provider with default parameters by compression algorithm
/// name.
/// </summary>
/// <param name="algorithm">The name of the algorithm for which to instantiate a provider.</param>
/// <returns>
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
/// </returns>
_ASYNCRTIMP std::unique_ptr<compress_provider> make_compressor(const utility::string_t& algorithm);
/// <summary>
/// Factory function to instantiate a built-in decompression provider with default parameters by compression algorithm
/// name.
/// </summary>
/// <param name="algorithm">The name of the algorithm for which to instantiate a provider.</param>
/// <returns>
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
/// </returns>
_ASYNCRTIMP std::unique_ptr<decompress_provider> make_decompressor(const utility::string_t& algorithm);
/// <summary>
/// Factory function to obtain a pointer to a built-in compression provider factory by compression algorithm name.
/// </summary>
/// <param name="algorithm">The name of the algorithm for which to find a factory.</param>
/// <returns>
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
/// </returns>
_ASYNCRTIMP std::shared_ptr<compress_factory> get_compress_factory(const utility::string_t& algorithm);
/// <summary>
/// Factory function to obtain a pointer to a built-in decompression provider factory by compression algorithm name.
/// </summary>
/// <param name="algorithm">The name of the algorithm for which to find a factory.</param>
/// <returns>
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
/// </returns>
_ASYNCRTIMP std::shared_ptr<decompress_factory> get_decompress_factory(const utility::string_t& algorithm);
/// <summary>
// Factory function to instantiate a built-in gzip compression provider with caller-selected parameters.
/// </summary>
/// <returns>
/// A caller-owned pointer to a gzip compression provider, or to nullptr if the library was built without built-in
/// compression support.
/// </returns>
_ASYNCRTIMP std::unique_ptr<compress_provider> make_gzip_compressor(int compressionLevel,
int method,
int strategy,
int memLevel);
/// <summary>
// Factory function to instantiate a built-in deflate compression provider with caller-selected parameters.
/// </summary>
/// <returns>
/// A caller-owned pointer to a deflate compression provider, or to nullptr if the library was built without built-in
/// compression support..
/// </returns>
_ASYNCRTIMP std::unique_ptr<compress_provider> make_deflate_compressor(int compressionLevel,
int method,
int strategy,
int memLevel);
/// <summary>
// Factory function to instantiate a built-in Brotli compression provider with caller-selected parameters.
/// </summary>
/// <returns>
/// A caller-owned pointer to a Brotli compression provider, or to nullptr if the library was built without built-in
/// compression support.
/// </returns>
_ASYNCRTIMP std::unique_ptr<compress_provider> make_brotli_compressor(
uint32_t window, uint32_t quality, uint32_t mode, uint32_t block, uint32_t nomodel, uint32_t hint);
} // namespace builtin
/// <summary>
/// Factory function to instantiate a compression provider factory by compression algorithm name.
/// </summary>
/// <param name="algorithm">The name of the algorithm supported by the factory. Must match that returned by the
/// <c>web::http::compression::compress_provider</c> type instantiated by the factory's make_compressor function.
/// The supplied string is copied, and thus need not remain valid once the call returns.</param>
/// <param name="make_compressor">A factory function to be used to instantiate a compressor matching the factory's
/// reported algorithm.</param>
/// <returns>
/// A pointer to a generic provider factory implementation configured with the supplied parameters.
/// </returns>
/// <remarks>
/// This method may be used to conveniently instantiate a factory object for a caller-selected <c>compress_provider</c>.
/// That provider may be of the caller's own design, or it may be one of the built-in types. As such, this method may
/// be helpful when a caller wishes to build vectors containing a mix of custom and built-in providers.
/// </remarks>
_ASYNCRTIMP std::shared_ptr<compress_factory> make_compress_factory(
const utility::string_t& algorithm, std::function<std::unique_ptr<compress_provider>()> make_compressor);
/// <summary>
/// Factory function to instantiate a decompression provider factory by compression algorithm name.
/// </summary>
/// <param name="algorithm">The name of the algorithm supported by the factory. Must match that returned by the
/// <c>web::http::compression::decompress_provider</c> type instantiated by the factory's make_decompressor function.
/// The supplied string is copied, and thus need not remain valid once the call returns.</param>
/// <param name="weight">A numeric weight for the compression algorithm, times 1000, for use as a "quality value" when
/// requesting that the server send a compressed response. Valid values are between 0 and 1000, inclusive, where higher
/// values indicate more preferred algorithms, and 0 indicates that the algorithm is not allowed; values greater than
/// 1000 are treated as 1000.</param>
/// <param name="make_decompressor">A factory function to be used to instantiate a decompressor matching the factory's
/// reported algorithm.</param>
/// <returns>
/// A pointer to a generic provider factory implementation configured with the supplied parameters.
/// </returns>
/// <remarks>
/// This method may be used to conveniently instantiate a factory object for a caller-selected
/// <c>decompress_provider</c>. That provider may be of the caller's own design, or it may be one of the built-in
/// types. As such, this method may be helpful when a caller wishes to change the weights of built-in provider types,
/// to use custom providers without explicitly implementing a <c>decompress_factory</c>, or to build vectors containing
/// a mix of custom and built-in providers.
/// </remarks>
_ASYNCRTIMP std::shared_ptr<decompress_factory> make_decompress_factory(
const utility::string_t& algorithm,
uint16_t weight,
std::function<std::unique_ptr<decompress_provider>()> make_decompressor);
namespace details
{
/// <summary>
/// Header type enum for use with compressor and decompressor header parsing and building functions
/// </summary>
enum header_types
{
transfer_encoding,
content_encoding,
te,
accept_encoding
};
/// <summary>
/// Factory function to instantiate an appropriate compression provider, if any.
/// </summary>
/// <param name="encoding">A TE or Accept-Encoding header to interpret.</param>
/// <param name="type">Specifies the type of header whose contents are in the encoding parameter; valid values are
/// <c>header_type::te</c> and <c>header_type::accept_encoding</c>.</param>
/// <param name="preferred">A compressor object of the caller's preferred (possibly custom) type, which is used if
/// possible.</param>
/// <param name="factories">A collection of factory objects for use in construction of an appropriate compressor, if
/// any. If empty or not supplied, the set of supported built-in compressors is used.</param>
/// <returns>
/// A pointer to a compressor object that is acceptable per the supplied header, or to nullptr if no matching
/// algorithm is found.
/// </returns>
_ASYNCRTIMP std::unique_ptr<compress_provider> get_compressor_from_header(
const utility::string_t& encoding,
header_types type,
const std::vector<std::shared_ptr<compress_factory>>& factories = std::vector<std::shared_ptr<compress_factory>>());
/// <summary>
/// Factory function to instantiate an appropriate decompression provider, if any.
/// </summary>
/// <param name="encoding">A Transfer-Encoding or Content-Encoding header to interpret.</param>
/// <param name="type">Specifies the type of header whose contents are in the encoding parameter; valid values are
/// <c>header_type::transfer_encoding</c> and <c>header_type::content_encoding</c>.</param>
/// <param name="factories">A collection of factory objects for use in construction of an appropriate decompressor,
/// if any. If empty or not supplied, the set of supported built-in compressors is used.</param>
/// <returns>
/// A pointer to a decompressor object that is acceptable per the supplied header, or to nullptr if no matching
/// algorithm is found.
/// </returns>
_ASYNCRTIMP std::unique_ptr<decompress_provider> get_decompressor_from_header(
const utility::string_t& encoding,
header_types type,
const std::vector<std::shared_ptr<decompress_factory>>& factories =
std::vector<std::shared_ptr<decompress_factory>>());
/// <summary>
/// Helper function to compose a TE or Accept-Encoding header with supported, and possibly ranked, compression
/// algorithms.
/// </summary>
/// <param name="type">Specifies the type of header to be built; valid values are <c>header_type::te</c> and
/// <c>header_type::accept_encoding</c>.</param>
/// <param name="factories">A collection of factory objects for use in header construction. If empty or not
/// supplied, the set of supported built-in compressors is used.</param>
/// <returns>
/// A well-formed header, without the header name, specifying the acceptable ranked compression types.
/// </returns>
_ASYNCRTIMP utility::string_t build_supported_header(header_types type,
const std::vector<std::shared_ptr<decompress_factory>>& factories =
std::vector<std::shared_ptr<decompress_factory>>());
} // namespace details
} // namespace compression
} // namespace http
} // namespace web

View File

@ -0,0 +1,322 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/asyncrt_utils.h"
#include <map>
#include <memory>
#include <string>
#include <system_error>
#include <vector>
namespace web
{
namespace http
{
/// <summary>
/// Binds an individual reference to a string value.
/// </summary>
/// <typeparam name="key_type">The type of string value.</typeparam>
/// <typeparam name="_t">The type of the value to bind to.</typeparam>
/// <param name="text">The string value.</param>
/// <param name="ref">The value to bind to.</param>
/// <returns><c>true</c> if the binding succeeds, <c>false</c> otherwise.</returns>
template<typename key_type, typename _t>
CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, std::istringstream instead.")
bool bind(const key_type& text, _t& ref) // const
{
utility::istringstream_t iss(text);
iss >> ref;
if (iss.fail() || !iss.eof())
{
return false;
}
return true;
}
/// <summary>
/// Binds an individual reference to a string value.
/// This specialization is need because <c>istringstream::&gt;&gt;</c> delimits on whitespace.
/// </summary>
/// <typeparam name="key_type">The type of the string value.</typeparam>
/// <param name="text">The string value.</param>
/// <param name="ref">The value to bind to.</param>
/// <returns><c>true</c> if the binding succeeds, <c>false</c> otherwise.</returns>
template<typename key_type>
CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release.")
bool bind(const key_type& text, utility::string_t& ref) // const
{
ref = text;
return true;
}
namespace details
{
template<typename key_type, typename _t>
bool bind_impl(const key_type& text, _t& ref)
{
utility::istringstream_t iss(text);
iss.imbue(std::locale::classic());
iss >> ref;
if (iss.fail() || !iss.eof())
{
return false;
}
return true;
}
template<typename key_type>
bool bind_impl(const key_type& text, utf16string& ref)
{
ref = utility::conversions::to_utf16string(text);
return true;
}
template<typename key_type>
bool bind_impl(const key_type& text, std::string& ref)
{
ref = utility::conversions::to_utf8string(text);
return true;
}
} // namespace details
/// <summary>
/// Represents HTTP headers, acts like a map.
/// </summary>
class http_headers
{
public:
/// Function object to perform case insensitive comparison of wstrings.
struct _case_insensitive_cmp
{
bool operator()(const utility::string_t& str1, const utility::string_t& str2) const
{
return utility::details::str_iless(str1, str2);
}
};
private:
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp> inner_container;
public:
/// <summary>
/// STL-style typedefs
/// </summary>
typedef inner_container::key_type key_type;
typedef inner_container::key_compare key_compare;
typedef inner_container::allocator_type allocator_type;
typedef inner_container::size_type size_type;
typedef inner_container::difference_type difference_type;
typedef inner_container::pointer pointer;
typedef inner_container::const_pointer const_pointer;
typedef inner_container::reference reference;
typedef inner_container::const_reference const_reference;
typedef inner_container::iterator iterator;
typedef inner_container::const_iterator const_iterator;
typedef inner_container::reverse_iterator reverse_iterator;
typedef inner_container::const_reverse_iterator const_reverse_iterator;
/// <summary>
/// Constructs an empty set of HTTP headers.
/// </summary>
http_headers() {}
/// <summary>
/// Copy constructor.
/// </summary>
/// <param name="other">An <c>http_headers</c> object to copy from.</param>
http_headers(const http_headers& other) : m_headers(other.m_headers) {}
/// <summary>
/// Assignment operator.
/// </summary>
/// <param name="other">An <c>http_headers</c> object to copy from.</param>
http_headers& operator=(const http_headers& other)
{
if (this != &other)
{
m_headers = other.m_headers;
}
return *this;
}
/// <summary>
/// Move constructor.
/// </summary>
/// <param name="other">An <c>http_headers</c> object to move.</param>
http_headers(http_headers&& other) : m_headers(std::move(other.m_headers)) {}
/// <summary>
/// Move assignment operator.
/// </summary>
/// <param name="other">An <c>http_headers</c> object to move.</param>
http_headers& operator=(http_headers&& other)
{
if (this != &other)
{
m_headers = std::move(other.m_headers);
}
return *this;
}
/// <summary>
/// Adds a header field using the '&lt;&lt;' operator.
/// </summary>
/// <param name="name">The name of the header field.</param>
/// <param name="value">The value of the header field.</param>
/// <remarks>If the header field exists, the value will be combined as comma separated string.</remarks>
template<typename _t1>
void add(const key_type& name, const _t1& value)
{
auto printedValue = utility::conversions::details::print_string(value);
auto& mapVal = m_headers[name];
if (mapVal.empty())
{
mapVal = std::move(printedValue);
}
else
{
mapVal.append(_XPLATSTR(", ")).append(std::move(printedValue));
}
}
/// <summary>
/// Removes a header field.
/// </summary>
/// <param name="name">The name of the header field.</param>
void remove(const key_type& name) { m_headers.erase(name); }
/// <summary>
/// Removes all elements from the headers.
/// </summary>
void clear() { m_headers.clear(); }
/// <summary>
/// Checks if there is a header with the given key.
/// </summary>
/// <param name="name">The name of the header field.</param>
/// <returns><c>true</c> if there is a header with the given name, <c>false</c> otherwise.</returns>
bool has(const key_type& name) const { return m_headers.find(name) != m_headers.end(); }
/// <summary>
/// Returns the number of header fields.
/// </summary>
/// <returns>Number of header fields.</returns>
size_type size() const { return m_headers.size(); }
/// <summary>
/// Tests to see if there are any header fields.
/// </summary>
/// <returns><c>true</c> if there are no headers, <c>false</c> otherwise.</returns>
bool empty() const { return m_headers.empty(); }
/// <summary>
/// Returns a reference to header field with given name, if there is no header field one is inserted.
/// </summary>
utility::string_t& operator[](const key_type& name) { return m_headers[name]; }
/// <summary>
/// Checks if a header field exists with given name and returns an iterator if found. Otherwise
/// and iterator to end is returned.
/// </summary>
/// <param name="name">The name of the header field.</param>
/// <returns>An iterator to where the HTTP header is found.</returns>
iterator find(const key_type& name) { return m_headers.find(name); }
const_iterator find(const key_type& name) const { return m_headers.find(name); }
/// <summary>
/// Attempts to match a header field with the given name using the '>>' operator.
/// </summary>
/// <param name="name">The name of the header field.</param>
/// <param name="value">The value of the header field.</param>
/// <returns><c>true</c> if header field was found and successfully stored in value parameter.</returns>
template<typename _t1>
bool match(const key_type& name, _t1& value) const
{
auto iter = m_headers.find(name);
if (iter == m_headers.end())
{
return false;
}
return web::http::details::bind_impl(iter->second, value) || iter->second.empty();
}
/// <summary>
/// Returns an iterator referring to the first header field.
/// </summary>
/// <returns>An iterator to the beginning of the HTTP headers</returns>
iterator begin() { return m_headers.begin(); }
const_iterator begin() const { return m_headers.begin(); }
/// <summary>
/// Returns an iterator referring to the past-the-end header field.
/// </summary>
/// <returns>An iterator to the element past the end of the HTTP headers.</returns>
iterator end() { return m_headers.end(); }
const_iterator end() const { return m_headers.end(); }
/// <summary>
/// Gets the content length of the message.
/// </summary>
/// <returns>The length of the content.</returns>
_ASYNCRTIMP utility::size64_t content_length() const;
/// <summary>
/// Sets the content length of the message.
/// </summary>
/// <param name="length">The length of the content.</param>
_ASYNCRTIMP void set_content_length(utility::size64_t length);
/// <summary>
/// Gets the content type of the message.
/// </summary>
/// <returns>The content type of the body.</returns>
_ASYNCRTIMP utility::string_t content_type() const;
/// <summary>
/// Sets the content type of the message.
/// </summary>
/// <param name="type">The content type of the body.</param>
_ASYNCRTIMP void set_content_type(utility::string_t type);
/// <summary>
/// Gets the cache control header of the message.
/// </summary>
/// <returns>The cache control header value.</returns>
_ASYNCRTIMP utility::string_t cache_control() const;
/// <summary>
/// Sets the cache control header of the message.
/// </summary>
/// <param name="control">The cache control header value.</param>
_ASYNCRTIMP void set_cache_control(utility::string_t control);
/// <summary>
/// Gets the date header of the message.
/// </summary>
/// <returns>The date header value.</returns>
_ASYNCRTIMP utility::string_t date() const;
/// <summary>
/// Sets the date header of the message.
/// </summary>
/// <param name="date">The date header value.</param>
_ASYNCRTIMP void set_date(const utility::datetime& date);
private:
// Headers are stored in a map with case insensitive key.
inner_container m_headers;
};
} // namespace http
} // namespace web

View File

@ -0,0 +1,342 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: HTTP listener (server-side) APIs
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_HTTP_LISTENER_H
#define CASA_HTTP_LISTENER_H
#include "cpprest/http_msg.h"
#include <functional>
#include <limits>
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
#include <boost/asio/ssl.hpp>
#endif
#if !defined(_WIN32) || (_WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)) || \
defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
namespace web
{
namespace http
{
/// HTTP listener is currently in beta.
namespace experimental
{
/// HTTP server side library.
namespace listener
{
/// <summary>
/// Configuration class used to set various options when constructing and http_listener instance.
/// </summary>
class http_listener_config
{
public:
/// <summary>
/// Create an http_listener configuration with default options.
/// </summary>
http_listener_config() : m_timeout(utility::seconds(120)), m_backlog(0) {}
/// <summary>
/// Copy constructor.
/// </summary>
/// <param name="other">http_listener_config to copy.</param>
http_listener_config(const http_listener_config& other)
: m_timeout(other.m_timeout)
, m_backlog(other.m_backlog)
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
, m_ssl_context_callback(other.m_ssl_context_callback)
#endif
{
}
/// <summary>
/// Move constructor.
/// <summary>
/// <param name="other">http_listener_config to move from.</param>
http_listener_config(http_listener_config&& other)
: m_timeout(std::move(other.m_timeout))
, m_backlog(std::move(other.m_backlog))
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
, m_ssl_context_callback(std::move(other.m_ssl_context_callback))
#endif
{
}
/// <summary>
/// Assignment operator.
/// </summary>
/// <returns>http_listener_config instance.</returns>
http_listener_config& operator=(const http_listener_config& rhs)
{
if (this != &rhs)
{
m_timeout = rhs.m_timeout;
m_backlog = rhs.m_backlog;
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
m_ssl_context_callback = rhs.m_ssl_context_callback;
#endif
}
return *this;
}
/// <summary>
/// Assignment operator.
/// </summary>
/// <returns>http_listener_config instance.</returns>
http_listener_config& operator=(http_listener_config&& rhs)
{
if (this != &rhs)
{
m_timeout = std::move(rhs.m_timeout);
m_backlog = std::move(rhs.m_backlog);
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
m_ssl_context_callback = std::move(rhs.m_ssl_context_callback);
#endif
}
return *this;
}
/// <summary>
/// Get the timeout
/// </summary>
/// <returns>The timeout (in seconds).</returns>
utility::seconds timeout() const { return m_timeout; }
/// <summary>
/// Set the timeout
/// </summary>
/// <param name="timeout">The timeout (in seconds) used for each send and receive operation on the client.</param>
void set_timeout(utility::seconds timeout) { m_timeout = std::move(timeout); }
/// <summary>
/// Get the listen backlog
/// </summary>
/// <returns>The maximum length of the queue of pending connections, or zero for the implementation
/// default.</returns> <remarks>The implementation may not honour this value.</remarks>
int backlog() const { return m_backlog; }
/// <summary>
/// Set the listen backlog
/// </summary>
/// <param name="backlog">The maximum length of the queue of pending connections, or zero for the implementation
/// default.</param> <remarks>The implementation may not honour this value.</remarks>
void set_backlog(int backlog) { m_backlog = backlog; }
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
/// <summary>
/// Get the callback of ssl context
/// </summary>
/// <returns>The function defined by the user of http_listener_config to configure a ssl context.</returns>
const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
{
return m_ssl_context_callback;
}
/// <summary>
/// Set the callback of ssl context
/// </summary>
/// <param name="ssl_context_callback">The function to configure a ssl context which will setup https
/// connections.</param>
void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& ssl_context_callback)
{
m_ssl_context_callback = ssl_context_callback;
}
#endif
private:
utility::seconds m_timeout;
int m_backlog;
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
#endif
};
namespace details
{
/// <summary>
/// Internal class for pointer to implementation design pattern.
/// </summary>
class http_listener_impl
{
public:
http_listener_impl() : m_closed(true), m_close_task(pplx::task_from_result()) {}
_ASYNCRTIMP http_listener_impl(http::uri address);
_ASYNCRTIMP http_listener_impl(http::uri address, http_listener_config config);
_ASYNCRTIMP pplx::task<void> open();
_ASYNCRTIMP pplx::task<void> close();
/// <summary>
/// Handler for all requests. The HTTP host uses this to dispatch a message to the pipeline.
/// </summary>
/// <remarks>Only HTTP server implementations should call this API.</remarks>
_ASYNCRTIMP void handle_request(http::http_request msg);
const http::uri& uri() const { return m_uri; }
const http_listener_config& configuration() const { return m_config; }
// Handlers
std::function<void(http::http_request)> m_all_requests;
std::map<http::method, std::function<void(http::http_request)>> m_supported_methods;
private:
// Default implementation for TRACE and OPTIONS.
void handle_trace(http::http_request message);
void handle_options(http::http_request message);
// Gets a comma separated string containing the methods supported by this listener.
utility::string_t get_supported_methods() const;
http::uri m_uri;
http_listener_config m_config;
// Used to record that the listener is closed.
bool m_closed;
pplx::task<void> m_close_task;
};
} // namespace details
/// <summary>
/// A class for listening and processing HTTP requests at a specific URI.
/// </summary>
class http_listener
{
public:
/// <summary>
/// Create a listener from a URI.
/// </summary>
/// <remarks>The listener will not have been opened when returned.</remarks>
/// <param name="address">URI at which the listener should accept requests.</param>
http_listener(http::uri address)
: m_impl(utility::details::make_unique<details::http_listener_impl>(std::move(address)))
{
}
/// <summary>
/// Create a listener with specified URI and configuration.
/// </summary>
/// <param name="address">URI at which the listener should accept requests.</param>
/// <param name="config">Configuration to create listener with.</param>
http_listener(http::uri address, http_listener_config config)
: m_impl(utility::details::make_unique<details::http_listener_impl>(std::move(address), std::move(config)))
{
}
/// <summary>
/// Default constructor.
/// </summary>
/// <remarks>The resulting listener cannot be used for anything, but is useful to initialize a variable
/// that will later be overwritten with a real listener instance.</remarks>
http_listener() : m_impl(utility::details::make_unique<details::http_listener_impl>()) {}
/// <summary>
/// Destructor frees any held resources.
/// </summary>
/// <remarks>Call close() before allowing a listener to be destroyed.</remarks>
~http_listener()
{
if (m_impl)
{
// As a safe guard close the listener if not already done.
// Users are required to call close, but this is just a safeguard.
try
{
m_impl->close().wait();
}
catch (...)
{
}
}
}
/// <summary>
/// Asynchronously open the listener, i.e. start accepting requests.
/// </summary>
/// <returns>A task that will be completed once this listener is actually opened, accepting requests.</returns>
pplx::task<void> open() { return m_impl->open(); }
/// <summary>
/// Asynchronously stop accepting requests and close all connections.
/// </summary>
/// <returns>A task that will be completed once this listener is actually closed, no longer accepting
/// requests.</returns> <remarks> This function will stop accepting requests and wait for all outstanding handler
/// calls to finish before completing the task. Waiting on the task returned from close() within a handler and
/// blocking waiting for its result will result in a deadlock.
///
/// Call close() before allowing a listener to be destroyed.
/// </remarks>
pplx::task<void> close() { return m_impl->close(); }
/// <summary>
/// Add a general handler to support all requests.
/// </summary>
/// <param name="handler">Function object to be called for all requests.</param>
void support(const std::function<void(http_request)>& handler) { m_impl->m_all_requests = handler; }
/// <summary>
/// Add support for a specific HTTP method.
/// </summary>
/// <param name="method">An HTTP method.</param>
/// <param name="handler">Function object to be called for all requests for the given HTTP method.</param>
void support(const http::method& method, const std::function<void(http_request)>& handler)
{
m_impl->m_supported_methods[method] = handler;
}
/// <summary>
/// Get the URI of the listener.
/// </summary>
/// <returns>The URI this listener is for.</returns>
const http::uri& uri() const { return m_impl->uri(); }
/// <summary>
/// Get the configuration of this listener.
/// </summary>
/// <returns>Configuration this listener was constructed with.</returns>
const http_listener_config& configuration() const { return m_impl->configuration(); }
/// <summary>
/// Move constructor.
/// </summary>
/// <param name="other">http_listener instance to construct this one from.</param>
http_listener(http_listener&& other) : m_impl(std::move(other.m_impl)) {}
/// <summary>
/// Move assignment operator.
/// </summary>
/// <param name="other">http_listener to replace this one with.</param>
http_listener& operator=(http_listener&& other)
{
if (this != &other)
{
m_impl = std::move(other.m_impl);
}
return *this;
}
private:
// No copying of listeners.
http_listener(const http_listener& other);
http_listener& operator=(const http_listener& other);
std::unique_ptr<details::http_listener_impl> m_impl;
};
} // namespace listener
} // namespace experimental
} // namespace http
} // namespace web
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,554 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Adapter classes for async and STD stream buffers, used to connect std-based and async-based APIs.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/astreambuf.h"
#include "cpprest/streams.h"
#include "pplx/pplxtasks.h"
#if defined(_WIN32)
#pragma warning(push)
#pragma warning(disable : 4250)
#endif
namespace Concurrency
{
namespace streams
{
template<typename CharType>
class stdio_ostream;
template<typename CharType>
class stdio_istream;
namespace details
{
/// <summary>
/// The basic_stdio_buffer class serves to support interoperability with STL stream buffers.
/// Sitting atop a std::streambuf, which does all the I/O, instances of this class may read
/// and write data to standard iostreams. The class itself should not be used in application
/// code, it is used by the stream definitions farther down in the header file.
/// </summary>
template<typename _CharType>
class basic_stdio_buffer : public streambuf_state_manager<_CharType>
{
typedef concurrency::streams::char_traits<_CharType> traits;
typedef typename traits::int_type int_type;
typedef typename traits::pos_type pos_type;
typedef typename traits::off_type off_type;
/// <summary>
/// Private constructor
/// </summary>
basic_stdio_buffer(_In_ std::basic_streambuf<_CharType>* streambuf, std::ios_base::openmode mode)
: streambuf_state_manager<_CharType>(mode), m_buffer(streambuf)
{
}
public:
/// <summary>
/// Destructor
/// </summary>
virtual ~basic_stdio_buffer()
{
this->_close_read();
this->_close_write();
}
private:
//
// The functions overridden below here are documented elsewhere.
// See astreambuf.h for further information.
//
virtual bool can_seek() const { return this->is_open(); }
virtual bool has_size() const { return false; }
virtual size_t in_avail() const { return (size_t)m_buffer->in_avail(); }
virtual size_t buffer_size(std::ios_base::openmode) const { return 0; }
virtual void set_buffer_size(size_t, std::ios_base::openmode) { return; }
virtual pplx::task<bool> _sync() { return pplx::task_from_result(m_buffer->pubsync() == 0); }
virtual pplx::task<int_type> _putc(_CharType ch) { return pplx::task_from_result(m_buffer->sputc(ch)); }
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t size)
{
return pplx::task_from_result((size_t)m_buffer->sputn(ptr, size));
}
size_t _sgetn(_Out_writes_(size) _CharType* ptr, _In_ size_t size) const { return m_buffer->sgetn(ptr, size); }
virtual size_t _scopy(_Out_writes_(size) _CharType*, _In_ size_t size)
{
(void)(size);
return (size_t)-1;
}
virtual pplx::task<size_t> _getn(_Out_writes_(size) _CharType* ptr, _In_ size_t size)
{
return pplx::task_from_result((size_t)m_buffer->sgetn(ptr, size));
}
virtual int_type _sbumpc() { return m_buffer->sbumpc(); }
virtual int_type _sgetc() { return m_buffer->sgetc(); }
virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result<int_type>(m_buffer->sbumpc()); }
virtual pplx::task<int_type> _getc() { return pplx::task_from_result<int_type>(m_buffer->sgetc()); }
virtual pplx::task<int_type> _nextc() { return pplx::task_from_result<int_type>(m_buffer->snextc()); }
virtual pplx::task<int_type> _ungetc() { return pplx::task_from_result<int_type>(m_buffer->sungetc()); }
virtual pos_type getpos(std::ios_base::openmode mode) const
{
return m_buffer->pubseekoff(0, std::ios_base::cur, mode);
}
virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode) { return m_buffer->pubseekpos(pos, mode); }
virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode mode)
{
return m_buffer->pubseekoff(off, dir, mode);
}
virtual _CharType* _alloc(size_t) { return nullptr; }
virtual void _commit(size_t) {}
virtual bool acquire(_CharType*&, size_t&) { return false; }
virtual void release(_CharType*, size_t) {}
template<typename CharType>
friend class concurrency::streams::stdio_ostream;
template<typename CharType>
friend class concurrency::streams::stdio_istream;
std::basic_streambuf<_CharType>* m_buffer;
};
} // namespace details
/// <summary>
/// stdio_ostream represents an async ostream derived from a standard synchronous stream, as
/// defined by the "std" namespace. It is constructed from a reference to a standard stream, which
/// must be valid for the lifetime of the asynchronous stream.
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the <c>stdio_ostream</c>.
/// </typeparam>
/// <remarks>
/// Since std streams are not reference-counted, great care must be taken by an application to make
/// sure that the std stream does not get destroyed until all uses of the asynchronous stream are
/// done and have been serviced.
/// </remarks>
template<typename CharType>
class stdio_ostream : public basic_ostream<CharType>
{
public:
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source output stream.
/// </typeparam>
/// <param name="stream">The synchronous stream that this is using for its I/O</param>
template<typename AlterCharType>
stdio_ostream(std::basic_ostream<AlterCharType>& stream)
: basic_ostream<CharType>(
streams::streambuf<AlterCharType>(std::shared_ptr<details::basic_stdio_buffer<AlterCharType>>(
new details::basic_stdio_buffer<AlterCharType>(stream.rdbuf(), std::ios_base::out))))
{
}
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="other">The source object</param>
stdio_ostream(const stdio_ostream& other) : basic_ostream<CharType>(other) {}
/// <summary>
/// Assignment operator
/// </summary>
/// <param name="other">The source object</param>
/// <returns>A reference to the output stream object that contains the result of the assignment.</returns>
stdio_ostream& operator=(const stdio_ostream& other)
{
basic_ostream<CharType>::operator=(other);
return *this;
}
};
/// <summary>
/// stdio_istream represents an async istream derived from a standard synchronous stream, as
/// defined by the "std" namespace. It is constructed from a reference to a standard stream, which
/// must be valid for the lifetime of the asynchronous stream.
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the <c>stdio_istream</c>.
/// </typeparam>
/// <remarks>
/// Since std streams are not reference-counted, great care must be taken by an application to make
/// sure that the std stream does not get destroyed until all uses of the asynchronous stream are
/// done and have been serviced.
/// </remarks>
template<typename CharType>
class stdio_istream : public basic_istream<CharType>
{
public:
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source <c>istream</c>
/// </typeparam>
/// <param name="stream">The synchronous stream that this is using for its I/O</param>
template<typename AlterCharType>
stdio_istream(std::basic_istream<AlterCharType>& stream)
: basic_istream<CharType>(
streams::streambuf<AlterCharType>(std::shared_ptr<details::basic_stdio_buffer<AlterCharType>>(
new details::basic_stdio_buffer<AlterCharType>(stream.rdbuf(), std::ios_base::in))))
{
}
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="other">The source object</param>
stdio_istream(const stdio_istream& other) : basic_istream<CharType>(other) {}
/// <summary>
/// Assignment operator
/// </summary>
/// <param name="other">The source object</param>
/// <returns>A reference to the input stream object that contains the result of the assignment.</returns>
stdio_istream& operator=(const stdio_istream& other)
{
basic_istream<CharType>::operator=(other);
return *this;
}
};
namespace details
{
/// <summary>
/// IO streams stream buffer implementation used to interface with an async streambuffer underneath.
/// Used for implementing the standard synchronous streams that provide interop between std:: and concurrency::streams::
/// </summary>
template<typename CharType>
class basic_async_streambuf : public std::basic_streambuf<CharType>
{
public:
typedef concurrency::streams::char_traits<CharType> traits;
typedef typename traits::int_type int_type;
typedef typename traits::pos_type pos_type;
typedef typename traits::off_type off_type;
basic_async_streambuf(const streams::streambuf<CharType>& async_buf) : m_buffer(async_buf) {}
protected:
//
// The following are the functions in std::basic_streambuf that we need to override.
//
/// <summary>
/// Writes one byte to the stream buffer.
/// </summary>
int_type overflow(int_type ch)
{
try
{
return m_buffer.putc(CharType(ch)).get();
}
catch (...)
{
return traits::eof();
}
}
/// <summary>
/// Gets one byte from the stream buffer without moving the read position.
/// </summary>
int_type underflow()
{
try
{
return m_buffer.getc().get();
}
catch (...)
{
return traits::eof();
}
}
/// <summary>
/// Gets one byte from the stream buffer and move the read position one character.
/// </summary>
int_type uflow()
{
try
{
return m_buffer.bumpc().get();
}
catch (...)
{
return traits::eof();
}
}
/// <summary>
/// Gets a number of characters from the buffer and place it into the provided memory block.
/// </summary>
std::streamsize xsgetn(_Out_writes_(count) CharType* ptr, _In_ std::streamsize count)
{
size_t cnt = size_t(count);
size_t read_so_far = 0;
try
{
while (read_so_far < cnt)
{
size_t rd = m_buffer.getn(ptr + read_so_far, cnt - read_so_far).get();
read_so_far += rd;
if (rd == 0) break;
}
return read_so_far;
}
catch (...)
{
return 0;
}
}
/// <summary>
/// Writes a given number of characters from the provided block into the stream buffer.
/// </summary>
std::streamsize xsputn(const CharType* ptr, std::streamsize count)
{
try
{
return m_buffer.putn_nocopy(ptr, static_cast<size_t>(count)).get();
}
catch (...)
{
return 0;
}
}
/// <summary>
/// Synchronizes with the underlying medium.
/// </summary>
int sync() // must be int as per std::basic_streambuf
{
try
{
m_buffer.sync().wait();
}
catch (...)
{
}
return 0;
}
/// <summary>
/// Seeks to the given offset relative to the beginning, end, or current position.
/// </summary>
pos_type seekoff(off_type offset,
std::ios_base::seekdir dir,
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
try
{
if (dir == std::ios_base::cur && offset == 0) // Special case for getting the current position.
return m_buffer.getpos(mode);
return m_buffer.seekoff(offset, dir, mode);
}
catch (...)
{
return (pos_type(-1));
}
}
/// <summary>
/// Seeks to the given offset relative to the beginning of the stream.
/// </summary>
pos_type seekpos(pos_type pos, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
try
{
return m_buffer.seekpos(pos, mode);
}
catch (...)
{
return (pos_type(-1));
}
}
private:
concurrency::streams::streambuf<CharType> m_buffer;
};
} // namespace details
/// <summary>
/// A concrete STL ostream which relies on an asynchronous stream for its I/O.
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the stream.
/// </typeparam>
template<typename CharType>
class async_ostream : public std::basic_ostream<CharType>
{
public:
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source ostream.
/// </typeparam>
/// <param name="astream">The asynchronous stream whose stream buffer should be used for I/O</param>
template<typename AlterCharType>
async_ostream(const streams::basic_ostream<AlterCharType>& astream)
: std::basic_ostream<CharType>(&m_strbuf), m_strbuf(astream.streambuf())
{
}
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source <c>streambuf</c>.
/// </typeparam>
/// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
template<typename AlterCharType>
async_ostream(const streams::streambuf<AlterCharType>& strbuf)
: std::basic_ostream<CharType>(&m_strbuf), m_strbuf(strbuf)
{
}
private:
details::basic_async_streambuf<CharType> m_strbuf;
};
/// <summary>
/// A concrete STL istream which relies on an asynchronous stream for its I/O.
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the stream.
/// </typeparam>
template<typename CharType>
class async_istream : public std::basic_istream<CharType>
{
public:
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source istream.
/// </typeparam>
/// <param name="astream">The asynchronous stream whose stream buffer should be used for I/O</param>
template<typename AlterCharType>
async_istream(const streams::basic_istream<AlterCharType>& astream)
: std::basic_istream<CharType>(&m_strbuf), m_strbuf(astream.streambuf())
{
}
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="AlterCharType">
/// The data type of the basic element of the source <c>streambuf</c>.
/// </typeparam>
/// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
template<typename AlterCharType>
async_istream(const streams::streambuf<AlterCharType>& strbuf)
: std::basic_istream<CharType>(&m_strbuf), m_strbuf(strbuf)
{
}
private:
details::basic_async_streambuf<CharType> m_strbuf;
};
/// <summary>
/// A concrete STL istream which relies on an asynchronous stream buffer for its I/O.
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the stream.
/// </typeparam>
template<typename CharType>
class async_iostream : public std::basic_iostream<CharType>
{
public:
/// <summary>
/// Constructor
/// </summary>
/// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
async_iostream(const streams::streambuf<CharType>& strbuf)
: std::basic_iostream<CharType>(&m_strbuf), m_strbuf(strbuf)
{
}
private:
details::basic_async_streambuf<CharType> m_strbuf;
};
#if defined(__cplusplus_winrt)
/// <summary>
/// Static class containing factory functions for WinRT streams implemented on top of Casablanca async streams.
/// </summary>
/// <remarks>WinRT streams are defined in terms of single-byte characters only.</remarks>
class winrt_stream
{
public:
/// <summary>
/// Creates a WinRT <c>IInputStream</c> reference from an asynchronous stream buffer.
/// </summary>
/// <param name="buffer">A stream buffer based on a single-byte character.</param>
/// <returns>A reference to a WinRT <c>IInputStream</c>.</returns>
/// <remarks>
/// The stream buffer passed in must allow reading.
/// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
/// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can pass data to a WinRT component.
/// </remarks>
_ASYNCRTIMP static Windows::Storage::Streams::IInputStream ^
__cdecl create_input_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
/// <summary>
/// Creates a WinRT <c>IOutputStream</c> reference from an asynchronous stream buffer.
/// </summary>
/// <param name="buffer">A stream buffer based on a single-byte character.</param>
/// <returns>A reference to a WinRT <c>IOutputStream</c>.</returns>
/// <remarks>
/// The stream buffer passed in must allow writing.
/// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
/// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can retrieve data from a WinRT
/// component.
/// </remarks>
_ASYNCRTIMP static Windows::Storage::Streams::IOutputStream ^
__cdecl create_output_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
/// <summary>
/// Creates a WinRT <c>IRandomAccessStream reference from an asynchronous input stream.
/// </summary>
/// <param name="buffer">A stream based on a single-byte character.</param>
/// <returns>A reference to a WinRT <c>IRandomAccessStream</c>.</returns>
/// <remarks>
/// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
/// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can pass data to and retrieve data
/// from a WinRT component.
/// </remarks>
_ASYNCRTIMP static Windows::Storage::Streams::IRandomAccessStream ^
__cdecl create_random_access_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
};
#endif
} // namespace streams
} // namespace Concurrency
#if defined(_WIN32)
#pragma warning(pop)
#endif

1836
vendor/cpprestsdk/include/cpprest/json.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,576 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Oauth 1.0
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_OAUTH1_H
#define CASA_OAUTH1_H
#include "cpprest/details/web_utilities.h"
#include "cpprest/http_msg.h"
namespace web
{
namespace http
{
namespace client
{
// Forward declaration to avoid circular include dependency.
class http_client_config;
} // namespace client
/// oAuth 1.0 library.
namespace oauth1
{
namespace details
{
class oauth1_handler;
// State currently used by oauth1_config to authenticate request.
// The state varies for every request (due to timestamp and nonce).
// The state also contains extra transmitted protocol parameters during
// authorization flow (i.e. 'oauth_callback' or 'oauth_verifier').
class oauth1_state
{
public:
oauth1_state(utility::string_t timestamp,
utility::string_t nonce,
utility::string_t extra_key = utility::string_t(),
utility::string_t extra_value = utility::string_t())
: m_timestamp(std::move(timestamp))
, m_nonce(std::move(nonce))
, m_extra_key(std::move(extra_key))
, m_extra_value(std::move(extra_value))
{
}
const utility::string_t& timestamp() const { return m_timestamp; }
void set_timestamp(utility::string_t timestamp) { m_timestamp = std::move(timestamp); }
const utility::string_t& nonce() const { return m_nonce; }
void set_nonce(utility::string_t nonce) { m_nonce = std::move(nonce); }
const utility::string_t& extra_key() const { return m_extra_key; }
void set_extra_key(utility::string_t key) { m_extra_key = std::move(key); }
const utility::string_t& extra_value() const { return m_extra_value; }
void set_extra_value(utility::string_t value) { m_extra_value = std::move(value); }
private:
utility::string_t m_timestamp;
utility::string_t m_nonce;
utility::string_t m_extra_key;
utility::string_t m_extra_value;
};
// Constant strings for OAuth 1.0.
typedef utility::string_t oauth1_string;
class oauth1_strings
{
public:
#define _OAUTH1_STRINGS
#define DAT(a_, b_) _ASYNCRTIMP static const oauth1_string a_;
#include "cpprest/details/http_constants.dat"
#undef _OAUTH1_STRINGS
#undef DAT
};
} // namespace details
/// oAuth functionality is currently in beta.
namespace experimental
{
/// <summary>
/// Constant strings for OAuth 1.0 signature methods.
/// </summary>
typedef utility::string_t oauth1_method;
class oauth1_methods
{
public:
#define _OAUTH1_METHODS
#define DAT(a, b) _ASYNCRTIMP static const oauth1_method a;
#include "cpprest/details/http_constants.dat"
#undef _OAUTH1_METHODS
#undef DAT
};
/// <summary>
/// Exception type for OAuth 1.0 errors.
/// </summary>
class oauth1_exception : public std::exception
{
public:
oauth1_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {}
~oauth1_exception() CPPREST_NOEXCEPT {}
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
private:
std::string m_msg;
};
/// <summary>
/// OAuth 1.0 token and associated information.
/// </summary>
class oauth1_token
{
public:
/// <summary>
/// Constructs an initially empty invalid access token.
/// </summary>
oauth1_token() {}
/// <summary>
/// Constructs a OAuth1 token from a given access token and secret.
/// </summary>
/// <param name="access_token">Access token string.</param>
/// <param name="secret">Token secret string.</param>
oauth1_token(utility::string_t access_token, utility::string_t secret)
: m_token(std::move(access_token)), m_secret(std::move(secret))
{
}
/// <summary>
/// Get access token validity state.
/// If true, token is a valid access token.
/// </summary>
/// <returns>Access token validity state of the token.</returns>
bool is_valid_access_token() const { return !(access_token().empty() || secret().empty()); }
/// <summary>
/// Get access token.
/// </summary>
/// <returns>The access token string.</returns>
const utility::string_t& access_token() const { return m_token; }
/// <summary>
/// Set access token.
/// </summary>
/// <param name="access_token">Access token string to set.</param>
void set_access_token(utility::string_t&& access_token) { m_token = std::move(access_token); }
/// <summary>
/// Set access token.
/// </summary>
/// <param name="access_token">Access token string to set.</param>
void set_access_token(const utility::string_t& access_token) { m_token = access_token; }
/// <summary>
/// Get token secret.
/// </summary>
/// <returns>Token secret string.</returns>
const utility::string_t& secret() const { return m_secret; }
/// <summary>
/// Set token secret.
/// </summary>
/// <param name="secret">Token secret string to set.</param>
void set_secret(utility::string_t&& secret) { m_secret = std::move(secret); }
/// <summary>
/// Set token secret.
/// </summary>
/// <param name="secret">Token secret string to set.</param>
void set_secret(const utility::string_t& secret) { m_secret = secret; }
/// <summary>
/// Retrieves any additional parameters.
/// </summary>
/// <returns>A map containing the additional parameters.</returns>
const std::map<utility::string_t, utility::string_t>& additional_parameters() const
{
return m_additional_parameters;
}
/// <summary>
/// Sets a specific parameter additional parameter.
/// </summary>
/// <param name="paramName">Parameter name.</param>
/// <param name="paramValue">Parameter value.</param>
void set_additional_parameter(utility::string_t&& paramName, utility::string_t&& paramValue)
{
m_additional_parameters[std::move(paramName)] = std::move(paramValue);
}
/// <summary>
/// Sets a specific parameter additional parameter.
/// </summary>
/// <param name="paramName">Parameter name.</param>
/// <param name="paramValue">Parameter value.</param>
void set_additional_parameter(const utility::string_t& paramName, const utility::string_t& paramValue)
{
m_additional_parameters[paramName] = paramValue;
}
/// <summary>
/// Clears all additional parameters.
/// </summary>
void clear_additional_parameters() { m_additional_parameters.clear(); }
private:
friend class oauth1_config;
utility::string_t m_token;
utility::string_t m_secret;
std::map<utility::string_t, utility::string_t> m_additional_parameters;
};
/// <summary>
/// OAuth 1.0 configuration class.
/// </summary>
class oauth1_config
{
public:
oauth1_config(utility::string_t consumer_key,
utility::string_t consumer_secret,
utility::string_t temp_endpoint,
utility::string_t auth_endpoint,
utility::string_t token_endpoint,
utility::string_t callback_uri,
oauth1_method method,
utility::string_t realm = utility::string_t())
: m_consumer_key(std::move(consumer_key))
, m_consumer_secret(std::move(consumer_secret))
, m_temp_endpoint(std::move(temp_endpoint))
, m_auth_endpoint(std::move(auth_endpoint))
, m_token_endpoint(std::move(token_endpoint))
, m_callback_uri(std::move(callback_uri))
, m_realm(std::move(realm))
, m_method(std::move(method))
, m_is_authorization_completed(false)
{
}
/// <summary>
/// Builds an authorization URI to be loaded in a web browser/view.
/// The URI is built with auth_endpoint() as basis.
/// The method creates a task for HTTP request to first obtain a
/// temporary token. The authorization URI build based on this token.
/// </summary>
/// <returns>Authorization URI to be loaded in a web browser/view.</returns>
_ASYNCRTIMP pplx::task<utility::string_t> build_authorization_uri();
/// <summary>
/// Fetch an access token based on redirected URI.
/// The URI is expected to contain 'oauth_verifier'
/// parameter, which is then used to fetch an access token using the
/// token_from_verifier() method.
/// See: http://tools.ietf.org/html/rfc5849#section-2.2
/// The received 'oauth_token' is parsed and verified to match the current token().
/// When access token is successfully obtained, set_token() is called, and config is
/// ready for use by oauth1_handler.
/// </summary>
/// <param name="redirected_uri">The URI where web browser/view was redirected after resource owner's
/// authorization.</param> <returns>Task that fetches the access token based on redirected URI.</returns>
_ASYNCRTIMP pplx::task<void> token_from_redirected_uri(const web::http::uri& redirected_uri);
/// <summary>
/// Creates a task with HTTP request to fetch an access token from the token endpoint.
/// The request exchanges a verifier code to an access token.
/// If successful, the resulting token is set as active via set_token().
/// See: http://tools.ietf.org/html/rfc5849#section-2.3
/// </summary>
/// <param name="verifier">Verifier received via redirect upon successful authorization.</param>
/// <returns>Task that fetches the access token based on the verifier.</returns>
pplx::task<void> token_from_verifier(utility::string_t verifier)
{
return _request_token(_generate_auth_state(details::oauth1_strings::verifier, std::move(verifier)), false);
}
/// <summary>
/// Creates a task with HTTP request to fetch an access token from the token endpoint.
/// If successful, the resulting token is set as active via set_token().
/// </summary>
/// <returns>Task that fetches the access token based on the verifier.</returns>
pplx::task<void> refresh_token(const utility::string_t& key)
{
return _request_token(_generate_auth_state(key, m_token.additional_parameters().at(key)), false);
}
/// <summary>
/// Get consumer key used in authorization and authentication.
/// </summary>
/// <returns>Consumer key string.</returns>
const utility::string_t& consumer_key() const { return m_consumer_key; }
/// <summary>
/// Set consumer key used in authorization and authentication.
/// </summary>
/// <param name="key">Consumer key string to set.</param>
void set_consumer_key(utility::string_t key) { m_consumer_key = std::move(key); }
/// <summary>
/// Get consumer secret used in authorization and authentication.
/// </summary>
/// <returns>Consumer secret string.</returns>
const utility::string_t& consumer_secret() const { return m_consumer_secret; }
/// <summary>
/// Set consumer secret used in authorization and authentication.
/// </summary>
/// <param name="secret">Consumer secret string to set.</param>
void set_consumer_secret(utility::string_t secret) { m_consumer_secret = std::move(secret); }
/// <summary>
/// Get temporary token endpoint URI string.
/// </summary>
/// <returns>Temporary token endpoint URI string.</returns>
const utility::string_t& temp_endpoint() const { return m_temp_endpoint; }
/// <summary>
/// Set temporary token endpoint URI string.
/// </summary>
/// <param name="temp_endpoint">Temporary token endpoint URI string to set.</param>
void set_temp_endpoint(utility::string_t temp_endpoint) { m_temp_endpoint = std::move(temp_endpoint); }
/// <summary>
/// Get authorization endpoint URI string.
/// </summary>
/// <returns>Authorization endpoint URI string.</returns>
const utility::string_t& auth_endpoint() const { return m_auth_endpoint; }
/// <summary>
/// Set authorization endpoint URI string.
/// </summary>
/// <param name="auth_endpoint">Authorization endpoint URI string to set.</param>
void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); }
/// <summary>
/// Get token endpoint URI string.
/// </summary>
/// <returns>Token endpoint URI string.</returns>
const utility::string_t& token_endpoint() const { return m_token_endpoint; }
/// <summary>
/// Set token endpoint URI string.
/// </summary>
/// <param name="token_endpoint">Token endpoint URI string to set.</param>
void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); }
/// <summary>
/// Get callback URI string.
/// </summary>
/// <returns>Callback URI string.</returns>
const utility::string_t& callback_uri() const { return m_callback_uri; }
/// <summary>
/// Set callback URI string.
/// </summary>
/// <param name="callback_uri">Callback URI string to set.</param>
void set_callback_uri(utility::string_t callback_uri) { m_callback_uri = std::move(callback_uri); }
/// <summary>
/// Get token.
/// </summary>
/// <returns>Token.</returns>
_ASYNCRTIMP const oauth1_token& token() const;
/// <summary>
/// Set token.
/// </summary>
/// <param name="token">Token to set.</param>
void set_token(oauth1_token token)
{
m_token = std::move(token);
m_is_authorization_completed = true;
}
/// <summary>
/// Get signature method.
/// </summary>
/// <returns>Signature method.</returns>
const oauth1_method& method() const { return m_method; }
/// <summary>
/// Set signature method.
/// </summary>
/// <param name="method">Signature method.</param>
void set_method(oauth1_method method) { m_method = std::move(method); }
/// <summary>
/// Get authentication realm.
/// </summary>
/// <returns>Authentication realm string.</returns>
const utility::string_t& realm() const { return m_realm; }
/// <summary>
/// Set authentication realm.
/// </summary>
/// <param name="realm">Authentication realm string to set.</param>
void set_realm(utility::string_t realm) { m_realm = std::move(realm); }
/// <summary>
/// Returns enabled state of the configuration.
/// The oauth1_handler will perform OAuth 1.0 authentication only if
/// this method returns true.
/// Return value is true if access token is valid (=fetched or manually set)
/// and both consumer_key() and consumer_secret() are set (=non-empty).
/// </summary>
/// <returns>The configuration enabled state.</returns>
bool is_enabled() const
{
return token().is_valid_access_token() && !(consumer_key().empty() || consumer_secret().empty());
}
// Builds signature base string according to:
// http://tools.ietf.org/html/rfc5849#section-3.4.1.1
_ASYNCRTIMP utility::string_t _build_signature_base_string(http_request request, details::oauth1_state state) const;
// Builds HMAC-SHA1 signature according to:
// http://tools.ietf.org/html/rfc5849#section-3.4.2
utility::string_t _build_hmac_sha1_signature(http_request request, details::oauth1_state state) const
{
auto text(_build_signature_base_string(std::move(request), std::move(state)));
auto digest(_hmac_sha1(_build_key(), std::move(text)));
auto signature(utility::conversions::to_base64(std::move(digest)));
return signature;
}
// Builds PLAINTEXT signature according to:
// http://tools.ietf.org/html/rfc5849#section-3.4.4
utility::string_t _build_plaintext_signature() const { return _build_key(); }
details::oauth1_state _generate_auth_state(utility::string_t extra_key, utility::string_t extra_value)
{
return details::oauth1_state(
_generate_timestamp(), _generate_nonce(), std::move(extra_key), std::move(extra_value));
}
details::oauth1_state _generate_auth_state()
{
return details::oauth1_state(_generate_timestamp(), _generate_nonce());
}
/// <summary>
/// Gets map of parameters to sign.
/// </summary>
/// <returns>Map of parameters.</returns>
const std::map<utility::string_t, utility::string_t>& parameters() const { return m_parameters_to_sign; }
/// <summary>
/// Adds a key value parameter.
/// </summary>
/// <param name="key">Key as a string value.</param>
/// <param name="value">Value as a string value.</param>
void add_parameter(const utility::string_t& key, const utility::string_t& value)
{
m_parameters_to_sign[key] = value;
}
/// <summary>
/// Adds a key value parameter.
/// </summary>
/// <param name="key">Key as a string value.</param>
/// <param name="value">Value as a string value.</param>
void add_parameter(utility::string_t&& key, utility::string_t&& value)
{
m_parameters_to_sign[std::move(key)] = std::move(value);
}
/// <summary>
/// Sets entire map or parameters replacing all previously values.
/// </summary>
/// <param name="parameters">Map of values.</param>
void set_parameters(const std::map<utility::string_t, utility::string_t>& parameters)
{
m_parameters_to_sign.clear();
m_parameters_to_sign = parameters;
}
/// <summary>
/// Clears all parameters.
/// </summary>
void clear_parameters() { m_parameters_to_sign.clear(); }
/// <summary>
/// Get the web proxy object
/// </summary>
/// <returns>A reference to the web proxy object.</returns>
const web_proxy& proxy() const { return m_proxy; }
/// <summary>
/// Set the web proxy object that will be used by token_from_code and token_from_refresh
/// </summary>
/// <param name="proxy">A reference to the web proxy object.</param>
void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
private:
friend class web::http::client::http_client_config;
friend class web::http::oauth1::details::oauth1_handler;
oauth1_config() : m_is_authorization_completed(false) {}
utility::string_t _generate_nonce() { return m_nonce_generator.generate(); }
static utility::string_t _generate_timestamp()
{
return utility::conversions::details::to_string_t(utility::datetime::utc_timestamp());
}
_ASYNCRTIMP static std::vector<unsigned char> __cdecl _hmac_sha1(const utility::string_t& key,
const utility::string_t& data);
static utility::string_t _build_base_string_uri(const uri& u);
utility::string_t _build_normalized_parameters(web::http::uri u, const details::oauth1_state& state) const;
utility::string_t _build_signature(http_request request, details::oauth1_state state) const;
utility::string_t _build_key() const
{
return uri::encode_data_string(consumer_secret()) + _XPLATSTR("&") + uri::encode_data_string(m_token.secret());
}
void _authenticate_request(http_request& req) { _authenticate_request(req, _generate_auth_state()); }
_ASYNCRTIMP void _authenticate_request(http_request& req, details::oauth1_state state);
_ASYNCRTIMP pplx::task<void> _request_token(details::oauth1_state state, bool is_temp_token_request);
utility::string_t m_consumer_key;
utility::string_t m_consumer_secret;
oauth1_token m_token;
utility::string_t m_temp_endpoint;
utility::string_t m_auth_endpoint;
utility::string_t m_token_endpoint;
utility::string_t m_callback_uri;
utility::string_t m_realm;
oauth1_method m_method;
std::map<utility::string_t, utility::string_t> m_parameters_to_sign;
web::web_proxy m_proxy;
utility::nonce_generator m_nonce_generator;
bool m_is_authorization_completed;
};
} // namespace experimental
namespace details
{
class oauth1_handler : public http_pipeline_stage
{
public:
oauth1_handler(std::shared_ptr<experimental::oauth1_config> cfg) : m_config(std::move(cfg)) {}
virtual pplx::task<http_response> propagate(http_request request) override
{
if (m_config)
{
m_config->_authenticate_request(request);
}
return next_stage()->propagate(request);
}
private:
std::shared_ptr<experimental::oauth1_config> m_config;
};
} // namespace details
} // namespace oauth1
} // namespace http
} // namespace web
#endif

View File

@ -0,0 +1,555 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Oauth 2.0
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_OAUTH2_H
#define CASA_OAUTH2_H
#include "cpprest/details/web_utilities.h"
#include "cpprest/http_msg.h"
namespace web
{
namespace http
{
namespace client
{
// Forward declaration to avoid circular include dependency.
class http_client_config;
} // namespace client
/// oAuth 2.0 library.
namespace oauth2
{
namespace details
{
class oauth2_handler;
// Constant strings for OAuth 2.0.
typedef utility::string_t oauth2_string;
class oauth2_strings
{
public:
#define _OAUTH2_STRINGS
#define DAT(a_, b_) _ASYNCRTIMP static const oauth2_string a_;
#include "cpprest/details/http_constants.dat"
#undef _OAUTH2_STRINGS
#undef DAT
};
} // namespace details
/// oAuth functionality is currently in beta.
namespace experimental
{
/// <summary>
/// Exception type for OAuth 2.0 errors.
/// </summary>
class oauth2_exception : public std::exception
{
public:
oauth2_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {}
~oauth2_exception() CPPREST_NOEXCEPT {}
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
private:
std::string m_msg;
};
/// <summary>
/// OAuth 2.0 token and associated information.
/// </summary>
class oauth2_token
{
public:
/// <summary>
/// Value for undefined expiration time in expires_in().
/// </summary>
enum
{
undefined_expiration = -1
};
oauth2_token(utility::string_t access_token = utility::string_t())
: m_access_token(std::move(access_token)), m_expires_in(undefined_expiration)
{
}
/// <summary>
/// Get access token validity state.
/// If true, access token is a valid.
/// </summary>
/// <returns>Access token validity state.</returns>
bool is_valid_access_token() const { return !access_token().empty(); }
/// <summary>
/// Get access token.
/// </summary>
/// <returns>Access token string.</returns>
const utility::string_t& access_token() const { return m_access_token; }
/// <summary>
/// Set access token.
/// </summary>
/// <param name="access_token">Access token string to set.</param>
void set_access_token(utility::string_t access_token) { m_access_token = std::move(access_token); }
/// <summary>
/// Get refresh token.
/// </summary>
/// <returns>Refresh token string.</returns>
const utility::string_t& refresh_token() const { return m_refresh_token; }
/// <summary>
/// Set refresh token.
/// </summary>
/// <param name="refresh_token">Refresh token string to set.</param>
void set_refresh_token(utility::string_t refresh_token) { m_refresh_token = std::move(refresh_token); }
/// <summary>
/// Get token type.
/// </summary>
/// <returns>Token type string.</returns>
const utility::string_t& token_type() const { return m_token_type; }
/// <summary>
/// Set token type.
/// </summary>
/// <param name="token_type">Token type string to set.</param>
void set_token_type(utility::string_t token_type) { m_token_type = std::move(token_type); }
/// <summary>
/// Get token scope.
/// </summary>
/// <returns>Token scope string.</returns>
const utility::string_t& scope() const { return m_scope; }
/// <summary>
/// Set token scope.
/// </summary>
/// <param name="scope">Token scope string to set.</param>
void set_scope(utility::string_t scope) { m_scope = std::move(scope); }
/// <summary>
/// Get the lifetime of the access token in seconds.
/// For example, 3600 means the access token will expire in one hour from
/// the time when access token response was generated by the authorization server.
/// Value of undefined_expiration means expiration time is either
/// unset or that it was not returned by the server with the access token.
/// </summary>
/// <returns>Lifetime of the access token in seconds or undefined_expiration if not set.</returns>
int64_t expires_in() const { return m_expires_in; }
/// <summary>
/// Set lifetime of access token (in seconds).
/// </summary>
/// <param name="expires_in">Lifetime of access token in seconds.</param>
void set_expires_in(int64_t expires_in) { m_expires_in = expires_in; }
private:
utility::string_t m_access_token;
utility::string_t m_refresh_token;
utility::string_t m_token_type;
utility::string_t m_scope;
int64_t m_expires_in;
};
/// <summary>
/// OAuth 2.0 configuration.
///
/// Encapsulates functionality for:
/// - Authenticating requests with an access token.
/// - Performing the OAuth 2.0 authorization code grant authorization flow.
/// See: http://tools.ietf.org/html/rfc6749#section-4.1
/// - Performing the OAuth 2.0 implicit grant authorization flow.
/// See: http://tools.ietf.org/html/rfc6749#section-4.2
///
/// Performing OAuth 2.0 authorization:
/// 1. Set service and client/app parameters:
/// - Client/app key & secret (as provided by the service).
/// - The service authorization endpoint and token endpoint.
/// - Your client/app redirect URI.
/// - Use set_state() to assign a unique state string for the authorization
/// session (default: "").
/// - If needed, use set_bearer_auth() to control bearer token passing in either
/// query or header (default: header). See: http://tools.ietf.org/html/rfc6750#section-2
/// - If needed, use set_access_token_key() to set "non-standard" access token
/// key (default: "access_token").
/// - If needed, use set_implicit_grant() to enable implicit grant flow.
/// 2. Build authorization URI with build_authorization_uri() and open this in web browser/control.
/// 3. The resource owner should then clicks "Yes" to authorize your client/app, and
/// as a result the web browser/control is redirected to redirect_uri().
/// 5. Capture the redirected URI either in web control or by HTTP listener.
/// 6. Pass the redirected URI to token_from_redirected_uri() to obtain access token.
/// - The method ensures redirected URI contains same state() as set in step 1.
/// - In implicit_grant() is false, this will create HTTP request to fetch access token
/// from the service. Otherwise access token is already included in the redirected URI.
///
/// Usage for issuing authenticated requests:
/// 1. Perform authorization as above to obtain the access token or use an existing token.
/// - Some services provide option to generate access tokens for testing purposes.
/// 2. Pass the resulting oauth2_config with the access token to http_client_config::set_oauth2().
/// 3. Construct http_client with this http_client_config. As a result, all HTTP requests
/// by that client will be OAuth 2.0 authenticated.
///
/// </summary>
class oauth2_config
{
public:
oauth2_config(utility::string_t client_key,
utility::string_t client_secret,
utility::string_t auth_endpoint,
utility::string_t token_endpoint,
utility::string_t redirect_uri,
utility::string_t scope = utility::string_t(),
utility::string_t user_agent = utility::string_t())
: m_client_key(std::move(client_key))
, m_client_secret(std::move(client_secret))
, m_auth_endpoint(std::move(auth_endpoint))
, m_token_endpoint(std::move(token_endpoint))
, m_redirect_uri(std::move(redirect_uri))
, m_scope(std::move(scope))
, m_user_agent(std::move(user_agent))
, m_implicit_grant(false)
, m_bearer_auth(true)
, m_http_basic_auth(true)
, m_access_token_key(details::oauth2_strings::access_token)
{
}
/// <summary>
/// Builds an authorization URI to be loaded in the web browser/view.
/// The URI is built with auth_endpoint() as basis.
/// The implicit_grant() affects the built URI by selecting
/// either authorization code or implicit grant flow.
/// You can set generate_state to generate a new random state string.
/// </summary>
/// <param name="generate_state">If true, a new random state() string is generated
/// which replaces the current state(). If false, state() is unchanged and used as-is.</param>
/// <returns>Authorization URI string.</returns>
_ASYNCRTIMP utility::string_t build_authorization_uri(bool generate_state);
/// <summary>
/// Fetch an access token (and possibly a refresh token) based on redirected URI.
/// Behavior depends on the implicit_grant() setting.
/// If implicit_grant() is false, the URI is parsed for 'code'
/// parameter, and then token_from_code() is called with this code.
/// See: http://tools.ietf.org/html/rfc6749#section-4.1
/// Otherwise, redirect URI fragment part is parsed for 'access_token'
/// parameter, which directly contains the token(s).
/// See: http://tools.ietf.org/html/rfc6749#section-4.2
/// In both cases, the 'state' parameter is parsed and is verified to match state().
/// </summary>
/// <param name="redirected_uri">The URI where web browser/view was redirected after resource owner's
/// authorization.</param> <returns>Task that fetches the token(s) based on redirected URI.</returns>
_ASYNCRTIMP pplx::task<void> token_from_redirected_uri(const web::http::uri& redirected_uri);
/// <summary>
/// Fetches an access token (and possibly a refresh token) from the token endpoint.
/// The task creates an HTTP request to the token_endpoint() which exchanges
/// the authorization code for the token(s).
/// This also sets the refresh token if one was returned.
/// See: http://tools.ietf.org/html/rfc6749#section-4.1.3
/// </summary>
/// <param name="authorization_code">Code received via redirect upon successful authorization.</param>
/// <returns>Task that fetches token(s) based on the authorization code.</returns>
pplx::task<void> token_from_code(utility::string_t authorization_code)
{
uri_builder ub;
ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::authorization_code, false);
ub.append_query(details::oauth2_strings::code, uri::encode_data_string(std::move(authorization_code)), false);
ub.append_query(details::oauth2_strings::redirect_uri, uri::encode_data_string(redirect_uri()), false);
return _request_token(ub);
}
/// <summary>
/// Fetches a new access token (and possibly a new refresh token) using the refresh token.
/// The task creates a HTTP request to the token_endpoint().
/// If successful, resulting access token is set as active via set_token().
/// See: http://tools.ietf.org/html/rfc6749#section-6
/// This also sets a new refresh token if one was returned.
/// </summary>
/// <returns>Task that fetches the token(s) using the refresh token.</returns>
pplx::task<void> token_from_refresh()
{
uri_builder ub;
ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::refresh_token, false);
ub.append_query(
details::oauth2_strings::refresh_token, uri::encode_data_string(token().refresh_token()), false);
return _request_token(ub);
}
/// <summary>
/// Fetches an access token from the token endpoint using client credentials grant type.
/// The task creates an HTTP request to the token_endpoint() using
/// client authentication as the authorization grant.
/// See: http://tools.ietf.org/html/rfc6749#section-4.4
/// </summary>
/// <returns>Task that fetches token(s) using client credentials.</returns>
pplx::task<void> token_from_client_credentials()
{
uri_builder ub;
ub.append_query(
details::oauth2_strings::grant_type, details::oauth2_strings::client_credentials, false);
return _request_token(ub);
}
/// <summary>
/// Returns enabled state of the configuration.
/// The oauth2_handler will perform OAuth 2.0 authentication only if
/// this method returns true.
/// Return value is true if access token is valid (=fetched or manually set).
/// </summary>
/// <returns>The configuration enabled state.</returns>
bool is_enabled() const { return token().is_valid_access_token(); }
/// <summary>
/// Get client key.
/// </summary>
/// <returns>Client key string.</returns>
const utility::string_t& client_key() const { return m_client_key; }
/// <summary>
/// Set client key.
/// </summary>
/// <param name="client_key">Client key string to set.</param>
void set_client_key(utility::string_t client_key) { m_client_key = std::move(client_key); }
/// <summary>
/// Get client secret.
/// </summary>
/// <returns>Client secret string.</returns>
const utility::string_t& client_secret() const { return m_client_secret; }
/// <summary>
/// Set client secret.
/// </summary>
/// <param name="client_secret">Client secret string to set.</param>
void set_client_secret(utility::string_t client_secret) { m_client_secret = std::move(client_secret); }
/// <summary>
/// Get authorization endpoint URI string.
/// </summary>
/// <returns>Authorization endpoint URI string.</returns>
const utility::string_t& auth_endpoint() const { return m_auth_endpoint; }
/// <summary>
/// Set authorization endpoint URI string.
/// </summary>
/// <param name="auth_endpoint">Authorization endpoint URI string to set.</param>
void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); }
/// <summary>
/// Get token endpoint URI string.
/// </summary>
/// <returns>Token endpoint URI string.</returns>
const utility::string_t& token_endpoint() const { return m_token_endpoint; }
/// <summary>
/// Set token endpoint URI string.
/// </summary>
/// <param name="token_endpoint">Token endpoint URI string to set.</param>
void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); }
/// <summary>
/// Get redirect URI string.
/// </summary>
/// <returns>Redirect URI string.</returns>
const utility::string_t& redirect_uri() const { return m_redirect_uri; }
/// <summary>
/// Set redirect URI string.
/// </summary>
/// <param name="redirect_uri">Redirect URI string to set.</param>
void set_redirect_uri(utility::string_t redirect_uri) { m_redirect_uri = std::move(redirect_uri); }
/// <summary>
/// Get scope used in authorization for token.
/// </summary>
/// <returns>Scope string used in authorization.</returns>
const utility::string_t& scope() const { return m_scope; }
/// <summary>
/// Set scope for authorization for token.
/// </summary>
/// <param name="scope">Scope string for authorization for token.</param>
void set_scope(utility::string_t scope) { m_scope = std::move(scope); }
/// <summary>
/// Get client state string used in authorization.
/// </summary>
/// <returns>Client state string used in authorization.</returns>
const utility::string_t& state() { return m_state; }
/// <summary>
/// Set client state string for authorization for token.
/// The state string is used in authorization for security reasons
/// (to uniquely identify authorization sessions).
/// If desired, suitably secure state string can be automatically generated
/// by build_authorization_uri().
/// A good state string consist of 30 or more random alphanumeric characters.
/// </summary>
/// <param name="state">Client authorization state string to set.</param>
void set_state(utility::string_t state) { m_state = std::move(state); }
/// <summary>
/// Get token.
/// </summary>
/// <returns>Token.</returns>
const oauth2_token& token() const { return m_token; }
/// <summary>
/// Set token.
/// </summary>
/// <param name="token">Token to set.</param>
void set_token(oauth2_token token) { m_token = std::move(token); }
/// <summary>
/// Get implicit grant setting for authorization.
/// </summary>
/// <returns>Implicit grant setting for authorization.</returns>
bool implicit_grant() const { return m_implicit_grant; }
/// <summary>
/// Set implicit grant setting for authorization.
/// False means authorization code grant is used for authorization.
/// True means implicit grant is used.
/// Default: False.
/// </summary>
/// <param name="implicit_grant">The implicit grant setting to set.</param>
void set_implicit_grant(bool implicit_grant) { m_implicit_grant = implicit_grant; }
/// <summary>
/// Get bearer token authentication setting.
/// </summary>
/// <returns>Bearer token authentication setting.</returns>
bool bearer_auth() const { return m_bearer_auth; }
/// <summary>
/// Set bearer token authentication setting.
/// This must be selected based on what the service accepts.
/// True means access token is passed in the request header. (http://tools.ietf.org/html/rfc6750#section-2.1)
/// False means access token in passed in the query parameters. (http://tools.ietf.org/html/rfc6750#section-2.3)
/// Default: True.
/// </summary>
/// <param name="bearer_auth">The bearer token authentication setting to set.</param>
void set_bearer_auth(bool bearer_auth) { m_bearer_auth = bearer_auth; }
/// <summary>
/// Get HTTP Basic authentication setting for token endpoint.
/// </summary>
/// <returns>HTTP Basic authentication setting for token endpoint.</returns>
bool http_basic_auth() const { return m_http_basic_auth; }
/// <summary>
/// Set HTTP Basic authentication setting for token endpoint.
/// This setting must be selected based on what the service accepts.
/// True means HTTP Basic authentication is used for the token endpoint.
/// False means client key & secret are passed in the HTTP request body.
/// Default: True.
/// </summary>
/// <param name="http_basic_auth">The HTTP Basic authentication setting to set.</param>
void set_http_basic_auth(bool http_basic_auth) { m_http_basic_auth = http_basic_auth; }
/// <summary>
/// Get access token key.
/// </summary>
/// <returns>Access token key string.</returns>
const utility::string_t& access_token_key() const { return m_access_token_key; }
/// <summary>
/// Set access token key.
/// If the service requires a "non-standard" key you must set it here.
/// Default: "access_token".
/// </summary>
void set_access_token_key(utility::string_t access_token_key) { m_access_token_key = std::move(access_token_key); }
/// <summary>
/// Get the web proxy object
/// </summary>
/// <returns>A reference to the web proxy object.</returns>
const web_proxy& proxy() const { return m_proxy; }
/// <summary>
/// Set the web proxy object that will be used by token_from_code and token_from_refresh
/// </summary>
/// <param name="proxy">A reference to the web proxy object.</param>
void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
/// <summary>
/// Get user agent to be used in oauth2 flows.
/// </summary>
/// <returns>User agent string.</returns>
const utility::string_t& user_agent() const { return m_user_agent; }
/// <summary>
/// Set user agent to be used in oauth2 flows.
/// If none is provided a default user agent is provided.
/// </summary>
void set_user_agent(utility::string_t user_agent) { m_user_agent = std::move(user_agent); }
private:
friend class web::http::client::http_client_config;
friend class web::http::oauth2::details::oauth2_handler;
oauth2_config() : m_implicit_grant(false), m_bearer_auth(true), m_http_basic_auth(true) {}
_ASYNCRTIMP pplx::task<void> _request_token(uri_builder& request_body);
oauth2_token _parse_token_from_json(const json::value& token_json);
void _authenticate_request(http_request& req) const
{
if (bearer_auth())
{
req.headers().add(header_names::authorization, _XPLATSTR("Bearer ") + token().access_token());
}
else
{
uri_builder ub(req.request_uri());
ub.append_query(access_token_key(), token().access_token());
req.set_request_uri(ub.to_uri());
}
}
utility::string_t m_client_key;
utility::string_t m_client_secret;
utility::string_t m_auth_endpoint;
utility::string_t m_token_endpoint;
utility::string_t m_redirect_uri;
utility::string_t m_scope;
utility::string_t m_state;
utility::string_t m_user_agent;
web::web_proxy m_proxy;
bool m_implicit_grant;
bool m_bearer_auth;
bool m_http_basic_auth;
utility::string_t m_access_token_key;
oauth2_token m_token;
utility::nonce_generator m_state_generator;
};
} // namespace experimental
namespace details
{
class oauth2_handler : public http_pipeline_stage
{
public:
oauth2_handler(std::shared_ptr<experimental::oauth2_config> cfg) : m_config(std::move(cfg)) {}
virtual pplx::task<http_response> propagate(http_request request) override
{
if (m_config)
{
m_config->_authenticate_request(request);
}
return next_stage()->propagate(request);
}
private:
std::shared_ptr<experimental::oauth2_config> m_config;
};
} // namespace details
} // namespace oauth2
} // namespace http
} // namespace web
#endif

View File

@ -0,0 +1,656 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* This file defines a basic memory-based stream buffer, which allows consumer / producer pairs to communicate
* data via a buffer.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_PRODUCER_CONSUMER_STREAMS_H
#define CASA_PRODUCER_CONSUMER_STREAMS_H
#include "cpprest/astreambuf.h"
#include "pplx/pplxtasks.h"
#include <algorithm>
#include <iterator>
#include <queue>
#include <vector>
namespace Concurrency
{
namespace streams
{
namespace details
{
/// <summary>
/// The basic_producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and
/// reading sequences of characters. It can be used as a consumer/producer buffer.
/// </summary>
template<typename _CharType>
class basic_producer_consumer_buffer : public streams::details::streambuf_state_manager<_CharType>
{
public:
typedef typename ::concurrency::streams::char_traits<_CharType> traits;
typedef typename basic_streambuf<_CharType>::int_type int_type;
typedef typename basic_streambuf<_CharType>::pos_type pos_type;
typedef typename basic_streambuf<_CharType>::off_type off_type;
/// <summary>
/// Constructor
/// </summary>
basic_producer_consumer_buffer(size_t alloc_size)
: streambuf_state_manager<_CharType>(std::ios_base::out | std::ios_base::in)
, m_mode(std::ios_base::in)
, m_alloc_size(alloc_size)
, m_allocBlock(nullptr)
, m_total(0)
, m_total_read(0)
, m_total_written(0)
, m_synced(0)
{
}
/// <summary>
/// Destructor
/// </summary>
virtual ~basic_producer_consumer_buffer()
{
// Note: there is no need to call 'wait()' on the result of close(),
// since we happen to know that close() will return without actually
// doing anything asynchronously. Should the implementation of _close_write()
// change in that regard, this logic may also have to change.
this->_close_read();
this->_close_write();
_ASSERTE(m_requests.empty());
m_blocks.clear();
}
/// <summary>
/// <c>can_seek<c/> is used to determine whether a stream buffer supports seeking.
/// </summary>
virtual bool can_seek() const { return false; }
/// <summary>
/// <c>has_size<c/> is used to determine whether a stream buffer supports size().
/// </summary>
virtual bool has_size() const { return false; }
/// <summary>
/// Get the stream buffer size, if one has been set.
/// </summary>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
/// <summary>
/// Sets the stream buffer implementation to buffer or not buffer.
/// </summary>
/// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
/// will not have any effect on what is returned by subsequent calls to <see cref="::buffer_size method"
/// />.</remarks>
virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
/// <summary>
/// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
/// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
/// incurring the overhead of using tasks.
/// </summary>
virtual size_t in_avail() const { return m_total; }
/// <summary>
/// Gets the current read or write position in the stream.
/// </summary>
/// <param name="direction">The I/O direction to seek (see remarks)</param>
/// <returns>The current position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the direction parameter defines whether to move the read or the write
/// cursor.</remarks>
virtual pos_type getpos(std::ios_base::openmode mode) const
{
if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
return static_cast<pos_type>(traits::eof());
if (mode == std::ios_base::in)
return (pos_type)m_total_read;
else if (mode == std::ios_base::out)
return (pos_type)m_total_written;
else
return (pos_type)traits::eof();
}
// Seeking is not supported
virtual pos_type seekpos(pos_type, std::ios_base::openmode) { return (pos_type)traits::eof(); }
virtual pos_type seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode)
{
return (pos_type)traits::eof();
}
/// <summary>
/// Allocates a contiguous memory block and returns it.
/// </summary>
/// <param name="count">The number of characters to allocate.</param>
/// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
/// alloc/commit.</returns>
virtual _CharType* _alloc(size_t count)
{
if (!this->can_write())
{
return nullptr;
}
// We always allocate a new block even if the count could be satisfied by
// the current write block. While this does lead to wasted space it allows for
// easier book keeping
_ASSERTE(!m_allocBlock);
m_allocBlock = std::make_shared<_block>(count);
return m_allocBlock->wbegin();
}
/// <summary>
/// Submits a block already allocated by the stream buffer.
/// </summary>
/// <param name="count">The number of characters to be committed.</param>
virtual void _commit(size_t count)
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
// The count does not reflect the actual size of the block.
// Since we do not allow any more writes to this block it would suffice.
// If we ever change the algorithm to reuse blocks then this needs to be revisited.
_ASSERTE((bool)m_allocBlock);
m_allocBlock->update_write_head(count);
m_blocks.push_back(m_allocBlock);
m_allocBlock = nullptr;
update_write_head(count);
}
/// <summary>
/// Gets a pointer to the next already allocated contiguous block of data.
/// </summary>
/// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
/// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
/// <remarks>
/// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
/// there is no block to return immediately or that the stream buffer does not support the operation.
/// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
/// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
/// a subsequent read will not succeed.
/// </remarks>
virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
{
count = 0;
ptr = nullptr;
if (!this->can_read()) return false;
pplx::extensibility::scoped_critical_section_t l(m_lock);
if (m_blocks.empty())
{
// If the write head has been closed then have reached the end of the
// stream (return true), otherwise more data could be written later (return false).
return !this->can_write();
}
else
{
auto block = m_blocks.front();
count = block->rd_chars_left();
ptr = block->rbegin();
_ASSERTE(ptr != nullptr);
return true;
}
}
/// <summary>
/// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
/// de-allocate the memory, if it so desires. Move the read position ahead by the count.
/// </summary>
/// <param name="ptr">A pointer to the block of data to be released.</param>
/// <param name="count">The number of characters that were read.</param>
virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
{
if (ptr == nullptr) return;
pplx::extensibility::scoped_critical_section_t l(m_lock);
auto block = m_blocks.front();
_ASSERTE(block->rd_chars_left() >= count);
block->m_read += count;
update_read_head(count);
}
protected:
virtual pplx::task<bool> _sync()
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
m_synced = in_avail();
fulfill_outstanding();
return pplx::task_from_result(true);
}
virtual pplx::task<int_type> _putc(_CharType ch)
{
return pplx::task_from_result((this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof());
}
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
{
return pplx::task_from_result<size_t>(this->write(ptr, count));
}
virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
pplx::task_completion_event<size_t> tce;
enqueue_request(_request(count, [this, ptr, count, tce]() {
// VS 2010 resolves read to a global function. Explicit
// invocation through the "this" pointer fixes the issue.
tce.set(this->read(ptr, count));
}));
return pplx::create_task(tce);
}
virtual size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
return can_satisfy(count) ? this->read(ptr, count) : (size_t)traits::requires_async();
}
virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
return can_satisfy(count) ? this->read(ptr, count, false) : (size_t)traits::requires_async();
}
virtual pplx::task<int_type> _bumpc()
{
pplx::task_completion_event<int_type> tce;
enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(true)); }));
return pplx::create_task(tce);
}
virtual int_type _sbumpc()
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
return can_satisfy(1) ? this->read_byte(true) : traits::requires_async();
}
virtual pplx::task<int_type> _getc()
{
pplx::task_completion_event<int_type> tce;
enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(false)); }));
return pplx::create_task(tce);
}
int_type _sgetc()
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
return can_satisfy(1) ? this->read_byte(false) : traits::requires_async();
}
virtual pplx::task<int_type> _nextc()
{
pplx::task_completion_event<int_type> tce;
enqueue_request(_request(1, [this, tce]() {
this->read_byte(true);
tce.set(this->read_byte(false));
}));
return pplx::create_task(tce);
}
virtual pplx::task<int_type> _ungetc() { return pplx::task_from_result<int_type>(traits::eof()); }
private:
/// <summary>
/// Close the stream buffer for writing
/// </summary>
pplx::task<void> _close_write()
{
// First indicate that there could be no more writes.
// Fulfill outstanding relies on that to flush all the
// read requests.
this->m_stream_can_write = false;
{
pplx::extensibility::scoped_critical_section_t l(this->m_lock);
// This runs on the thread that called close.
this->fulfill_outstanding();
}
return pplx::task_from_result();
}
/// <summary>
/// Updates the write head by an offset specified by count
/// </summary>
/// <remarks>This should be called with the lock held</remarks>
void update_write_head(size_t count)
{
m_total += count;
m_total_written += count;
fulfill_outstanding();
}
/// <summary>
/// Writes count characters from ptr into the stream buffer
/// </summary>
size_t write(const _CharType* ptr, size_t count)
{
if (!this->can_write() || (count == 0)) return 0;
// If no one is going to read, why bother?
// Just pretend to be writing!
if (!this->can_read()) return count;
pplx::extensibility::scoped_critical_section_t l(m_lock);
// Allocate a new block if necessary
if (m_blocks.empty() || m_blocks.back()->wr_chars_left() < count)
{
msl::safeint3::SafeInt<size_t> alloc = m_alloc_size.Max(count);
m_blocks.push_back(std::make_shared<_block>(alloc));
}
// The block at the back is always the write head
auto last = m_blocks.back();
auto countWritten = last->write(ptr, count);
_ASSERTE(countWritten == count);
update_write_head(countWritten);
return countWritten;
}
/// <summary>
/// Fulfill pending requests
/// </summary>
/// <remarks>This should be called with the lock held</remarks>
void fulfill_outstanding()
{
while (!m_requests.empty())
{
auto req = m_requests.front();
// If we cannot satisfy the request then we need
// to wait for the producer to write data
if (!can_satisfy(req.size())) return;
// We have enough data to satisfy this request
req.complete();
// Remove it from the request queue
m_requests.pop();
}
}
/// <summary>
/// Represents a memory block
/// </summary>
class _block
{
public:
_block(size_t size) : m_read(0), m_pos(0), m_size(size), m_data(new _CharType[size]) {}
~_block() { delete[] m_data; }
// Read head
size_t m_read;
// Write head
size_t m_pos;
// Allocation size (of m_data)
size_t m_size;
// The data store
_CharType* m_data;
// Pointer to the read head
_CharType* rbegin() { return m_data + m_read; }
// Pointer to the write head
_CharType* wbegin() { return m_data + m_pos; }
// Read up to count characters from the block
size_t read(_Out_writes_(count) _CharType* dest, _In_ size_t count, bool advance = true)
{
msl::safeint3::SafeInt<size_t> avail(rd_chars_left());
auto countRead = static_cast<size_t>(avail.Min(count));
_CharType* beg = rbegin();
_CharType* end = rbegin() + countRead;
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
// Avoid warning C4996: Use checked iterators under SECURE_SCL
std::copy(beg, end, stdext::checked_array_iterator<_CharType*>(dest, count));
#else
std::copy(beg, end, dest);
#endif // _WIN32
if (advance)
{
m_read += countRead;
}
return countRead;
}
// Write count characters into the block
size_t write(const _CharType* src, size_t count)
{
msl::safeint3::SafeInt<size_t> avail(wr_chars_left());
auto countWritten = static_cast<size_t>(avail.Min(count));
const _CharType* srcEnd = src + countWritten;
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
// Avoid warning C4996: Use checked iterators under SECURE_SCL
std::copy(src, srcEnd, stdext::checked_array_iterator<_CharType*>(wbegin(), static_cast<size_t>(avail)));
#else
std::copy(src, srcEnd, wbegin());
#endif // _WIN32
update_write_head(countWritten);
return countWritten;
}
void update_write_head(size_t count) { m_pos += count; }
size_t rd_chars_left() const { return m_pos - m_read; }
size_t wr_chars_left() const { return m_size - m_pos; }
private:
// Copy is not supported
_block(const _block&);
_block& operator=(const _block&);
};
/// <summary>
/// Represents a request on the stream buffer - typically reads
/// </summary>
class _request
{
public:
typedef std::function<void()> func_type;
_request(size_t count, const func_type& func) : m_func(func), m_count(count) {}
void complete() { m_func(); }
size_t size() const { return m_count; }
private:
func_type m_func;
size_t m_count;
};
void enqueue_request(_request req)
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
if (can_satisfy(req.size()))
{
// We can immediately fulfill the request.
req.complete();
}
else
{
// We must wait for data to arrive.
m_requests.push(req);
}
}
/// <summary>
/// Determine if the request can be satisfied.
/// </summary>
bool can_satisfy(size_t count) { return (m_synced > 0) || (this->in_avail() >= count) || !this->can_write(); }
/// <summary>
/// Reads a byte from the stream and returns it as int_type.
/// Note: This routine shall only be called if can_satisfy() returned true.
/// </summary>
/// <remarks>This should be called with the lock held</remarks>
int_type read_byte(bool advance = true)
{
_CharType value;
auto read_size = this->read(&value, 1, advance);
return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
}
/// <summary>
/// Reads up to count characters into ptr and returns the count of characters copied.
/// The return value (actual characters copied) could be <= count.
/// Note: This routine shall only be called if can_satisfy() returned true.
/// </summary>
/// <remarks>This should be called with the lock held</remarks>
size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
{
_ASSERTE(can_satisfy(count));
size_t read = 0;
for (auto iter = begin(m_blocks); iter != std::end(m_blocks); ++iter)
{
auto block = *iter;
auto read_from_block = block->read(ptr + read, count - read, advance);
read += read_from_block;
_ASSERTE(count >= read);
if (read == count) break;
}
if (advance)
{
update_read_head(read);
}
return read;
}
/// <summary>
/// Updates the read head by the specified offset
/// </summary>
/// <remarks>This should be called with the lock held</remarks>
void update_read_head(size_t count)
{
m_total -= count;
m_total_read += count;
if (m_synced > 0) m_synced = (m_synced > count) ? (m_synced - count) : 0;
// The block at the front is always the read head.
// Purge empty blocks so that the block at the front reflects the read head
while (!m_blocks.empty())
{
// If front block is not empty - we are done
if (m_blocks.front()->rd_chars_left() > 0) break;
// The block has no more data to be read. Release the block
m_blocks.pop_front();
}
}
// The in/out mode for the buffer
std::ios_base::openmode m_mode;
// Default block size
msl::safeint3::SafeInt<size_t> m_alloc_size;
// Block used for alloc/commit
std::shared_ptr<_block> m_allocBlock;
// Total available data
size_t m_total;
size_t m_total_read;
size_t m_total_written;
// Keeps track of the number of chars that have been flushed but still
// remain to be consumed by a read operation.
size_t m_synced;
// The producer-consumer buffer is intended to be used concurrently by a reader
// and a writer, who are not coordinating their accesses to the buffer (coordination
// being what the buffer is for in the first place). Thus, we have to protect
// against some of the internal data elements against concurrent accesses
// and the possibility of inconsistent states. A simple non-recursive lock
// should be sufficient for those purposes.
pplx::extensibility::critical_section_t m_lock;
// Memory blocks
std::deque<std::shared_ptr<_block>> m_blocks;
// Queue of requests
std::queue<_request> m_requests;
};
} // namespace details
/// <summary>
/// The producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and reading
/// sequences of bytes. It can be used as a consumer/producer buffer.
/// </summary>
/// <typeparam name="_CharType">
/// The data type of the basic element of the <c>producer_consumer_buffer</c>.
/// </typeparam>
/// <remarks>
/// This is a reference-counted version of basic_producer_consumer_buffer.</remarks>
template<typename _CharType>
class producer_consumer_buffer : public streambuf<_CharType>
{
public:
typedef _CharType char_type;
/// <summary>
/// Create a producer_consumer_buffer.
/// </summary>
/// <param name="alloc_size">The internal default block size.</param>
producer_consumer_buffer(size_t alloc_size = 512)
: streambuf<_CharType>(std::make_shared<details::basic_producer_consumer_buffer<_CharType>>(alloc_size))
{
}
};
} // namespace streams
} // namespace Concurrency
#endif

View File

@ -0,0 +1,598 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* This file defines a stream buffer that is based on a raw pointer and block size. Unlike a vector-based
* stream buffer, the buffer cannot be expanded or contracted, it has a fixed capacity.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_RAWPTR_STREAMS_H
#define CASA_RAWPTR_STREAMS_H
#include "cpprest/astreambuf.h"
#include "cpprest/streams.h"
#include "pplx/pplxtasks.h"
#include <algorithm>
#include <iterator>
#include <queue>
#include <vector>
namespace Concurrency
{
namespace streams
{
// Forward declarations
template<typename _CharType>
class rawptr_buffer;
namespace details
{
/// <summary>
/// The basic_rawptr_buffer class serves as a memory-based steam buffer that supports both writing and reading
/// sequences of characters to and from a fixed-size block.
/// </summary>
template<typename _CharType>
class basic_rawptr_buffer : public streams::details::streambuf_state_manager<_CharType>
{
public:
typedef _CharType char_type;
typedef typename basic_streambuf<_CharType>::traits traits;
typedef typename basic_streambuf<_CharType>::int_type int_type;
typedef typename basic_streambuf<_CharType>::pos_type pos_type;
typedef typename basic_streambuf<_CharType>::off_type off_type;
/// <summary>
/// Constructor
/// </summary>
basic_rawptr_buffer()
: streambuf_state_manager<_CharType>(std::ios_base::in | std::ios_base::out)
, m_data(nullptr)
, m_current_position(0)
, m_size(0)
{
}
/// <summary>
/// Destructor
/// </summary>
virtual ~basic_rawptr_buffer()
{
this->_close_read();
this->_close_write();
}
protected:
/// <summary>
/// can_seek is used to determine whether a stream buffer supports seeking.
/// </summary>
virtual bool can_seek() const { return this->is_open(); }
/// <summary>
/// <c>has_size<c/> is used to determine whether a stream buffer supports size().
/// </summary>
virtual bool has_size() const { return this->is_open(); }
/// <summary>
/// Gets the size of the stream, if known. Calls to <c>has_size</c> will determine whether
/// the result of <c>size</c> can be relied on.
/// </summary>
virtual utility::size64_t size() const { return utility::size64_t(m_size); }
/// <summary>
/// Get the stream buffer size, if one has been set.
/// </summary>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
/// <summary>
/// Set the stream buffer implementation to buffer or not buffer.
/// </summary>
/// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
/// will not have
/// any effect on what is returned by subsequent calls to buffer_size().</remarks>
virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
/// <summary>
/// For any input stream, in_avail returns the number of characters that are immediately available
/// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> and sgetn() to
/// read data without incurring the overhead of using tasks.
/// </summary>
virtual size_t in_avail() const
{
// See the comment in seek around the restiction that we do not allow read head to
// seek beyond the current size.
_ASSERTE(m_current_position <= m_size);
msl::safeint3::SafeInt<size_t> readhead(m_current_position);
msl::safeint3::SafeInt<size_t> writeend(m_size);
return (size_t)(writeend - readhead);
}
/// <summary>
/// Closes the stream buffer, preventing further read or write operations.
/// </summary>
/// <param name="mode">The I/O mode (in or out) to close for.</param>
virtual pplx::task<void> close(std::ios_base::openmode mode)
{
if (mode & std::ios_base::in)
{
this->_close_read().get(); // Safe to call get() here.
}
if (mode & std::ios_base::out)
{
this->_close_write().get(); // Safe to call get() here.
}
if (!this->can_read() && !this->can_write())
{
m_data = nullptr;
}
// Exceptions will be propagated out of _close_read or _close_write
return pplx::task_from_result();
}
virtual pplx::task<bool> _sync() { return pplx::task_from_result(true); }
virtual pplx::task<int_type> _putc(_CharType ch)
{
if (m_current_position >= m_size) return pplx::task_from_result<int_type>(traits::eof());
int_type retVal = (this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof();
return pplx::task_from_result<int_type>(retVal);
}
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
{
msl::safeint3::SafeInt<size_t> newSize = msl::safeint3::SafeInt<size_t>(count) + m_current_position;
if (newSize > m_size)
return pplx::task_from_exception<size_t>(
std::make_exception_ptr(std::runtime_error("Writing past the end of the buffer")));
return pplx::task_from_result<size_t>(this->write(ptr, count));
}
/// <summary>
/// Allocates a contiguous memory block and returns it.
/// </summary>
/// <param name="count">The number of characters to allocate.</param>
/// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
/// alloc/commit.</returns>
_CharType* _alloc(size_t count)
{
if (!this->can_write()) return nullptr;
msl::safeint3::SafeInt<size_t> readhead(m_current_position);
msl::safeint3::SafeInt<size_t> writeend(m_size);
size_t space_left = (size_t)(writeend - readhead);
if (space_left < count) return nullptr;
// Let the caller copy the data
return (_CharType*)(m_data + m_current_position);
}
/// <summary>
/// Submits a block already allocated by the stream buffer.
/// </summary>
/// <param name="count">The number of characters to be committed.</param>
void _commit(size_t actual)
{
// Update the write position and satisfy any pending reads
update_current_position(m_current_position + actual);
}
/// <summary>
/// Gets a pointer to the next already allocated contiguous block of data.
/// </summary>
/// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
/// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
/// <remarks>
/// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
/// there is no block to return immediately or that the stream buffer does not support the operation.
/// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
/// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
/// a subsequent read will not succeed.
/// </remarks>
virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
{
count = 0;
ptr = nullptr;
if (!this->can_read()) return false;
count = in_avail();
if (count > 0)
{
ptr = (_CharType*)(m_data + m_current_position);
return true;
}
else
{
ptr = nullptr;
// Can only be open for read OR write, not both. If there is no data then
// we have reached the end of the stream so indicate such with true.
return true;
}
}
/// <summary>
/// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
/// de-allocate the memory, if it so desires. Move the read position ahead by the count.
/// </summary>
/// <param name="ptr">A pointer to the block of data to be released.</param>
/// <param name="count">The number of characters that were read.</param>
virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
{
if (ptr != nullptr) update_current_position(m_current_position + count);
}
virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
return pplx::task_from_result(this->read(ptr, count));
}
size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return this->read(ptr, count); }
virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
return this->read(ptr, count, false);
}
virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result(this->read_byte(true)); }
virtual int_type _sbumpc() { return this->read_byte(true); }
virtual pplx::task<int_type> _getc() { return pplx::task_from_result(this->read_byte(false)); }
int_type _sgetc() { return this->read_byte(false); }
virtual pplx::task<int_type> _nextc()
{
if (m_current_position >= m_size - 1) return pplx::task_from_result(basic_streambuf<_CharType>::traits::eof());
this->read_byte(true);
return pplx::task_from_result(this->read_byte(false));
}
virtual pplx::task<int_type> _ungetc()
{
auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in);
if (pos == (pos_type)traits::eof()) return pplx::task_from_result(traits::eof());
return this->getc();
}
/// <summary>
/// Gets the current read or write position in the stream.
/// </summary>
/// <param name="direction">The I/O direction to seek (see remarks)</param>
/// <returns>The current position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the direction parameter defines whether to move the read or the write
/// cursor.</remarks>
virtual pos_type getpos(std::ios_base::openmode mode) const
{
if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
return static_cast<pos_type>(traits::eof());
if (mode == std::ios_base::in)
return (pos_type)m_current_position;
else if (mode == std::ios_base::out)
return (pos_type)m_current_position;
else
return (pos_type)traits::eof();
}
/// <summary>
/// Seeks to the given position.
/// </summary>
/// <param name="pos">The offset from the beginning of the stream.</param>
/// <param name="direction">The I/O direction to seek (see remarks).</param>
/// <returns>The position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors. For such streams, the direction parameter
/// defines whether to move the read or the write cursor.</remarks>
virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode)
{
pos_type beg(0);
pos_type end(m_size);
if (position >= beg)
{
auto pos = static_cast<size_t>(position);
// Read head
if ((mode & std::ios_base::in) && this->can_read())
{
if (position <= end)
{
// We do not allow reads to seek beyond the end or before the start position.
update_current_position(pos);
return static_cast<pos_type>(m_current_position);
}
}
// Write head
if ((mode & std::ios_base::out) && this->can_write())
{
// Update write head and satisfy read requests if any
update_current_position(pos);
return static_cast<pos_type>(m_current_position);
}
}
return static_cast<pos_type>(traits::eof());
}
/// <summary>
/// Seeks to a position given by a relative offset.
/// </summary>
/// <param name="offset">The relative position to seek to</param>
/// <param name="way">The starting point (beginning, end, current) for the seek.</param>
/// <param name="mode">The I/O direction to seek (see remarks)</param>
/// <returns>The position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
{
pos_type beg = 0;
pos_type cur = static_cast<pos_type>(m_current_position);
pos_type end = static_cast<pos_type>(m_size);
switch (way)
{
case std::ios_base::beg: return seekpos(beg + offset, mode);
case std::ios_base::cur: return seekpos(cur + offset, mode);
case std::ios_base::end: return seekpos(end + offset, mode);
default: return static_cast<pos_type>(traits::eof());
}
}
private:
template<typename _CharType1>
friend class ::concurrency::streams::rawptr_buffer;
/// <summary>
/// Constructor
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
basic_rawptr_buffer(const _CharType* data, size_t size)
: streambuf_state_manager<_CharType>(std::ios_base::in)
, m_data(const_cast<_CharType*>(data))
, m_size(size)
, m_current_position(0)
{
validate_mode(std::ios_base::in);
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
/// <param name="mode">The stream mode (in, out, etc.).</param>
basic_rawptr_buffer(_CharType* data, size_t size, std::ios_base::openmode mode)
: streambuf_state_manager<_CharType>(mode), m_data(data), m_size(size), m_current_position(0)
{
validate_mode(mode);
}
static void validate_mode(std::ios_base::openmode mode)
{
// Disallow simultaneous use of the stream buffer for writing and reading.
if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
throw std::invalid_argument("this combination of modes on raw pointer stream not supported");
}
/// <summary>
/// Determines if the request can be satisfied.
/// </summary>
bool can_satisfy(size_t) const
{
// We can always satisfy a read, at least partially, unless the
// read position is at the very end of the buffer.
return (in_avail() > 0);
}
/// <summary>
/// Reads a byte from the stream and returns it as int_type.
/// Note: This routine must only be called if can_satisfy() returns true.
/// </summary>
int_type read_byte(bool advance = true)
{
_CharType value;
auto read_size = this->read(&value, 1, advance);
return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
}
/// <summary>
/// Reads up to count characters into ptr and returns the count of characters copied.
/// The return value (actual characters copied) could be <= count.
/// Note: This routine must only be called if can_satisfy() returns true.
/// </summary>
size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
{
if (!can_satisfy(count)) return 0;
msl::safeint3::SafeInt<size_t> request_size(count);
msl::safeint3::SafeInt<size_t> read_size = request_size.Min(in_avail());
size_t newPos = m_current_position + read_size;
auto readBegin = m_data + m_current_position;
auto readEnd = m_data + newPos;
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
// Avoid warning C4996: Use checked iterators under SECURE_SCL
std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType*>(ptr, count));
#else
std::copy(readBegin, readEnd, ptr);
#endif // _WIN32
if (advance)
{
update_current_position(newPos);
}
return (size_t)read_size;
}
/// <summary>
/// Write count characters from the ptr into the stream buffer
/// </summary>
size_t write(const _CharType* ptr, size_t count)
{
if (!this->can_write() || (count == 0)) return 0;
msl::safeint3::SafeInt<size_t> newSize = msl::safeint3::SafeInt<size_t>(count) + m_current_position;
if (newSize > m_size) throw std::runtime_error("Writing past the end of the buffer");
// Copy the data
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
// Avoid warning C4996: Use checked iterators under SECURE_SCL
std::copy(ptr, ptr + count, stdext::checked_array_iterator<_CharType*>(m_data, m_size, m_current_position));
#else
std::copy(ptr, ptr + count, m_data + m_current_position);
#endif // _WIN32
// Update write head and satisfy pending reads if any
update_current_position(newSize);
return count;
}
/// <summary>
/// Updates the current read or write position
/// </summary>
void update_current_position(size_t newPos)
{
// The new write head
m_current_position = newPos;
_ASSERTE(m_current_position <= m_size);
}
// The actual memory block
_CharType* m_data;
// The size of the memory block
size_t m_size;
// Read/write head
size_t m_current_position;
};
} // namespace details
/// <summary>
/// The <c>rawptr_buffer</c> class serves as a memory-based stream buffer that supports reading
/// sequences of characters to or from a fixed-size block. Note that it cannot be used simultaneously for reading as
/// well as writing.
/// </summary>
/// <typeparam name="_CharType">
/// The data type of the basic element of the <c>rawptr_buffer</c>.
/// </typeparam>
template<typename _CharType>
class rawptr_buffer : public streambuf<_CharType>
{
public:
typedef _CharType char_type;
/// <summary>
/// Create a rawptr_buffer given a pointer to a memory block and the size of the block.
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
rawptr_buffer(const char_type* data, size_t size)
: streambuf<char_type>(std::shared_ptr<details::basic_rawptr_buffer<char_type>>(
new details::basic_rawptr_buffer<char_type>(data, size)))
{
}
/// <summary>
/// Create a rawptr_buffer given a pointer to a memory block and the size of the block.
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
rawptr_buffer(char_type* data, size_t size, std::ios_base::openmode mode = std::ios::out)
: streambuf<char_type>(std::shared_ptr<details::basic_rawptr_buffer<char_type>>(
new details::basic_rawptr_buffer<char_type>(data, size, mode)))
{
}
/// <summary>
/// Default constructor.
/// </summary>
rawptr_buffer() {}
};
/// <summary>
/// The rawptr_stream class is used to create memory-backed streams that support writing or reading
/// sequences of characters to / from a fixed-size block.
/// </summary>
/// <typeparam name="_CharType">
/// The data type of the basic element of the <c>rawptr_stream</c>.
/// </typeparam>
template<typename _CharType>
class rawptr_stream
{
public:
typedef _CharType char_type;
typedef rawptr_buffer<_CharType> buffer_type;
/// <summary>
/// Create a rawptr-stream given a pointer to a read-only memory block and the size of the block.
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
/// <returns>An opened input stream.</returns>
static concurrency::streams::basic_istream<char_type> open_istream(const char_type* data, size_t size)
{
return concurrency::streams::basic_istream<char_type>(buffer_type(data, size));
}
/// <summary>
/// Create a rawptr-stream given a pointer to a writable memory block and the size of the block.
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
/// <returns>An opened input stream.</returns>
static concurrency::streams::basic_istream<char_type> open_istream(char_type* data, size_t size)
{
return concurrency::streams::basic_istream<char_type>(buffer_type(data, size, std::ios::in));
}
/// <summary>
/// Create a rawptr-stream given a pointer to a writable memory block and the size of the block.
/// </summary>
/// <param name="data">The address (pointer to) the memory block.</param>
/// <param name="size">The memory block size, measured in number of characters.</param>
/// <returns>An opened output stream.</returns>
static concurrency::streams::basic_ostream<char_type> open_ostream(char_type* data, size_t size)
{
return concurrency::streams::basic_ostream<char_type>(buffer_type(data, size, std::ios::out));
}
};
} // namespace streams
} // namespace Concurrency
#endif

File diff suppressed because it is too large Load Diff

21
vendor/cpprestsdk/include/cpprest/uri.h vendored Normal file
View File

@ -0,0 +1,21 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Protocol independent support for URIs.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_URI_H
#define CASA_URI_H
#include "cpprest/base_uri.h"
#include "cpprest/uri_builder.h"
#endif

View File

@ -0,0 +1,295 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Builder style class for creating URIs.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/base_uri.h"
#include <string>
namespace web
{
/// <summary>
/// Builder for constructing URIs incrementally.
/// </summary>
class uri_builder
{
public:
/// <summary>
/// Creates a builder with an initially empty URI.
/// </summary>
uri_builder() = default;
/// <summary>
/// Creates a builder with a existing URI object.
/// </summary>
/// <param name="uri_str">Encoded string containing the URI.</param>
uri_builder(const uri& uri_str) : m_uri(uri_str.m_components) {}
/// <summary>
/// Get the scheme component of the URI as an encoded string.
/// </summary>
/// <returns>The URI scheme as a string.</returns>
const utility::string_t& scheme() const { return m_uri.m_scheme; }
/// <summary>
/// Get the user information component of the URI as an encoded string.
/// </summary>
/// <returns>The URI user information as a string.</returns>
const utility::string_t& user_info() const { return m_uri.m_user_info; }
/// <summary>
/// Get the host component of the URI as an encoded string.
/// </summary>
/// <returns>The URI host as a string.</returns>
const utility::string_t& host() const { return m_uri.m_host; }
/// <summary>
/// Get the port component of the URI. Returns -1 if no port is specified.
/// </summary>
/// <returns>The URI port as an integer.</returns>
int port() const { return m_uri.m_port; }
/// <summary>
/// Get the path component of the URI as an encoded string.
/// </summary>
/// <returns>The URI path as a string.</returns>
const utility::string_t& path() const { return m_uri.m_path; }
/// <summary>
/// Get the query component of the URI as an encoded string.
/// </summary>
/// <returns>The URI query as a string.</returns>
const utility::string_t& query() const { return m_uri.m_query; }
/// <summary>
/// Get the fragment component of the URI as an encoded string.
/// </summary>
/// <returns>The URI fragment as a string.</returns>
const utility::string_t& fragment() const { return m_uri.m_fragment; }
/// <summary>
/// Set the scheme of the URI.
/// </summary>
/// <param name="scheme">Uri scheme.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_scheme(const utility::string_t& scheme)
{
m_uri.m_scheme = scheme;
return *this;
}
/// <summary>
/// Set the user info component of the URI.
/// </summary>
/// <param name="user_info">User info as a decoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_user_info(const utility::string_t& user_info, bool do_encoding = false)
{
if (do_encoding)
{
m_uri.m_user_info = uri::encode_uri(user_info, uri::components::user_info);
}
else
{
m_uri.m_user_info = user_info;
}
return *this;
}
/// <summary>
/// Set the host component of the URI.
/// </summary>
/// <param name="host">Host as a decoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_host(const utility::string_t& host, bool do_encoding = false)
{
if (do_encoding)
{
m_uri.m_host = uri::encode_uri(host, uri::components::host);
}
else
{
m_uri.m_host = host;
}
return *this;
}
/// <summary>
/// Set the port component of the URI.
/// </summary>
/// <param name="port">Port as an integer.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_port(int port)
{
m_uri.m_port = port;
return *this;
}
/// <summary>
/// Set the port component of the URI.
/// </summary>
/// <param name="port">Port as a string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
/// <remarks>When string can't be converted to an integer the port is left unchanged.</remarks>
_ASYNCRTIMP uri_builder& set_port(const utility::string_t& port);
/// <summary>
/// Set the path component of the URI.
/// </summary>
/// <param name="path">Path as a decoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_path(const utility::string_t& path, bool do_encoding = false)
{
if (do_encoding)
{
m_uri.m_path = uri::encode_uri(path, uri::components::path);
}
else
{
m_uri.m_path = path;
}
return *this;
}
/// <summary>
/// Set the query component of the URI.
/// </summary>
/// <param name="query">Query as a decoded string.</param>
/// <param name="do_encoding">Specify whether apply URI encoding to the given string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_query(const utility::string_t& query, bool do_encoding = false)
{
if (do_encoding)
{
m_uri.m_query = uri::encode_uri(query, uri::components::query);
}
else
{
m_uri.m_query = query;
}
return *this;
}
/// <summary>
/// Set the fragment component of the URI.
/// </summary>
/// <param name="fragment">Fragment as a decoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
uri_builder& set_fragment(const utility::string_t& fragment, bool do_encoding = false)
{
if (do_encoding)
{
m_uri.m_fragment = uri::encode_uri(fragment, uri::components::fragment);
}
else
{
m_uri.m_fragment = fragment;
}
return *this;
}
/// <summary>
/// Clears all components of the underlying URI in this uri_builder.
/// </summary>
void clear() { m_uri = details::uri_components(); }
/// <summary>
/// Appends another path to the path of this uri_builder.
/// </summary>
/// <param name="path">Path to append as a already encoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this uri_builder to support chaining.</returns>
_ASYNCRTIMP uri_builder& append_path(const utility::string_t& path, bool do_encoding = false);
/// <summary>
/// Appends the raw contents of the path argument to the path of this uri_builder with no separator de-duplication.
/// </summary>
/// <remarks>
/// The path argument is appended after adding a '/' separator without regards to the contents of path. If an empty
/// string is provided, this function will immediately return without changes to the stored path value. For example:
/// if the current contents are "/abc" and path="/xyz", the result will be "/abc//xyz".
/// </remarks>
/// <param name="path">Path to append as a already encoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this uri_builder to support chaining.</returns>
_ASYNCRTIMP uri_builder& append_path_raw(const utility::string_t& path, bool do_encoding = false);
/// <summary>
/// Appends another query to the query of this uri_builder.
/// </summary>
/// <param name="query">Query to append as a decoded string.</param>
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
/// <returns>A reference to this uri_builder to support chaining.</returns>
_ASYNCRTIMP uri_builder& append_query(const utility::string_t& query, bool do_encoding = false);
/// <summary>
/// Appends an relative uri (Path, Query and fragment) at the end of the current uri.
/// </summary>
/// <param name="relative_uri">The relative uri to append.</param>
/// <returns>A reference to this uri_builder to support chaining.</returns>
_ASYNCRTIMP uri_builder& append(const uri& relative_uri);
/// <summary>
/// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building
/// a query segment of the form "element=10", where the right hand side of the query is stored as a type other than
/// a string, for instance, an integral type.
/// </summary>
/// <param name="name">The name portion of the query string</param>
/// <param name="value">The value portion of the query string</param>
/// <returns>A reference to this uri_builder to support chaining.</returns>
template<typename T>
uri_builder& append_query(const utility::string_t& name, const T& value, bool do_encoding = true)
{
if (do_encoding)
append_query_encode_impl(name, utility::conversions::details::print_utf8string(value));
else
append_query_no_encode_impl(name, utility::conversions::details::print_string(value));
return *this;
}
/// <summary>
/// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is
/// invalid.
/// </summary>
/// <returns>The created URI as a string.</returns>
_ASYNCRTIMP utility::string_t to_string() const;
/// <summary>
/// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is
/// invalid.
/// </summary>
/// <returns>The create URI as a URI class instance.</returns>
_ASYNCRTIMP uri to_uri() const;
/// <summary>
/// Validate the generated URI from all existing components of this uri_builder.
/// </summary>
/// <returns>Whether the URI is valid.</returns>
_ASYNCRTIMP bool is_valid();
private:
_ASYNCRTIMP void append_query_encode_impl(const utility::string_t& name, const utf8string& value);
_ASYNCRTIMP void append_query_no_encode_impl(const utility::string_t& name, const utility::string_t& value);
details::uri_components m_uri;
};
} // namespace web

View File

@ -0,0 +1,10 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
*/
#define CPPREST_VERSION_MINOR 10
#define CPPREST_VERSION_MAJOR 2
#define CPPREST_VERSION_REVISION 19
#define CPPREST_VERSION (CPPREST_VERSION_MAJOR * 100000 + CPPREST_VERSION_MINOR * 100 + CPPREST_VERSION_REVISION)

View File

@ -0,0 +1,610 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Websocket client side implementation
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_WS_CLIENT_H
#define CASA_WS_CLIENT_H
#include "cpprest/details/basic_types.h"
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/web_utilities.h"
#include "cpprest/http_headers.h"
#include "cpprest/uri.h"
#include "cpprest/ws_msg.h"
#include "pplx/pplxtasks.h"
#include <condition_variable>
#include <limits>
#include <memory>
#include <mutex>
#if !defined(_WIN32) || !defined(__cplusplus_winrt)
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#endif
#include "boost/asio/ssl.hpp"
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif
namespace web
{
// For backwards compatibility for when in the experimental namespace.
// At next major release this should be deleted.
namespace experimental = web;
// In the past namespace was accidentally called 'web_sockets'. To avoid breaking code
// alias it. At our next major release this should be deleted.
namespace web_sockets = websockets;
namespace websockets
{
/// WebSocket client side library.
namespace client
{
/// Websocket close status values.
enum class websocket_close_status
{
normal = 1000,
going_away = 1001,
protocol_error = 1002,
unsupported = 1003, // or data_mismatch
abnormal_close = 1006,
inconsistent_datatype = 1007,
policy_violation = 1008,
too_large = 1009,
negotiate_error = 1010,
server_terminate = 1011,
};
/// <summary>
/// Websocket client configuration class, used to set the possible configuration options
/// used to create an websocket_client instance.
/// </summary>
class websocket_client_config
{
public:
/// <summary>
/// Creates a websocket client configuration with default settings.
/// </summary>
websocket_client_config() : m_sni_enabled(true), m_validate_certificates(true) {}
/// <summary>
/// Get the web proxy object
/// </summary>
/// <returns>A reference to the web proxy object.</returns>
const web_proxy& proxy() const { return m_proxy; }
/// <summary>
/// Set the web proxy object
/// </summary>
/// <param name="proxy">The web proxy object.</param>
void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
/// <summary>
/// Get the client credentials
/// </summary>
/// <returns>A reference to the client credentials.</returns>
const web::credentials& credentials() const { return m_credentials; }
/// <summary>
/// Set the client credentials
/// </summary>
/// <param name="cred">The client credentials.</param>
void set_credentials(const web::credentials& cred) { m_credentials = cred; }
/// <summary>
/// Disables Server Name Indication (SNI). Default is on.
/// </summary>
void disable_sni() { m_sni_enabled = false; }
/// <summary>
/// Determines if Server Name Indication (SNI) is enabled.
/// </summary>
/// <returns>True if enabled, false otherwise.</returns>
bool is_sni_enabled() const { return m_sni_enabled; }
/// <summary>
/// Sets the server host name to use for TLS Server Name Indication (SNI).
/// </summary>
/// <remarks>By default the host name is set to the websocket URI host.</remarks>
/// <param name="name">The host name to use, as a string.</param>
void set_server_name(const utf8string& name) { m_sni_hostname = name; }
/// <summary>
/// Gets the server host name to use for TLS Server Name Indication (SNI).
/// </summary>
/// <returns>Host name as a string.</returns>
const utf8string& server_name() const { return m_sni_hostname; }
/// <summary>
/// Sets the User Agent to be used for the connection
/// </summary>
/// <param name="name">The User Agent to use, as a string.</param>
_ASYNCRTIMP void set_user_agent(const utf8string& user_agent);
/// <summary>
/// Gets the headers of the HTTP request message used in the WebSocket protocol handshake.
/// </summary>
/// <returns>HTTP headers for the WebSocket protocol handshake.</returns>
/// <remarks>
/// Use the <seealso cref="http_headers::add Method"/> to fill in desired headers.
/// </remarks>
web::http::http_headers& headers() { return m_headers; }
/// <summary>
/// Gets a const reference to the headers of the WebSocket protocol handshake HTTP message.
/// </summary>
/// <returns>HTTP headers.</returns>
const web::http::http_headers& headers() const { return m_headers; }
/// <summary>
/// Adds a subprotocol to the request headers.
/// </summary>
/// <param name="name">The name of the subprotocol.</param>
/// <remarks>If additional subprotocols have already been specified, the new one will just be added.</remarks>
_ASYNCRTIMP void add_subprotocol(const ::utility::string_t& name);
/// <summary>
/// Gets list of the specified subprotocols.
/// </summary>
/// <returns>Vector of all the subprotocols </returns>
/// <remarks>If you want all the subprotocols in a comma separated string
/// they can be directly looked up in the headers using 'Sec-WebSocket-Protocol'.</remarks>
_ASYNCRTIMP std::vector<::utility::string_t> subprotocols() const;
/// <summary>
/// Gets the server certificate validation property.
/// </summary>
/// <returns>True if certificates are to be verified, false otherwise.</returns>
bool validate_certificates() const { return m_validate_certificates; }
/// <summary>
/// Sets the server certificate validation property.
/// </summary>
/// <param name="validate_certs">False to turn ignore all server certificate validation errors, true
/// otherwise.</param> <remarks>Note ignoring certificate errors can be dangerous and should be done with
/// caution.</remarks>
void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
#if !defined(_WIN32) || !defined(__cplusplus_winrt)
/// <summary>
/// Sets a callback to enable custom setting of the ssl context, at construction time.
/// </summary>
/// <param name="callback">A user callback allowing for customization of the ssl context at construction
/// time.</param>
void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& callback)
{
m_ssl_context_callback = callback;
}
/// <summary>
/// Gets the user's callback to allow for customization of the ssl context.
/// </summary>
const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
{
return m_ssl_context_callback;
}
#endif
private:
web::web_proxy m_proxy;
web::credentials m_credentials;
web::http::http_headers m_headers;
bool m_sni_enabled;
utf8string m_sni_hostname;
bool m_validate_certificates;
#if !defined(_WIN32) || !defined(__cplusplus_winrt)
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
#endif
};
/// <summary>
/// Represents a websocket error. This class holds an error message and an optional error code.
/// </summary>
class websocket_exception : public std::exception
{
public:
/// <summary>
/// Creates an <c>websocket_exception</c> with just a string message and no error code.
/// </summary>
/// <param name="whatArg">Error message string.</param>
websocket_exception(const utility::string_t& whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {}
#ifdef _WIN32
/// <summary>
/// Creates an <c>websocket_exception</c> with just a string message and no error code.
/// </summary>
/// <param name="whatArg">Error message string.</param>
websocket_exception(std::string whatArg) : m_msg(std::move(whatArg)) {}
#endif
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code using the current platform error category.
/// The message of the error code will be used as the what() string message.
/// </summary>
/// <param name="errorCode">Error code value.</param>
websocket_exception(int errorCode) : m_errorCode(utility::details::create_error_code(errorCode))
{
m_msg = m_errorCode.message();
}
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code using the current platform error category.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="whatArg">Message to use in what() string.</param>
websocket_exception(int errorCode, const utility::string_t& whatArg)
: m_errorCode(utility::details::create_error_code(errorCode))
, m_msg(utility::conversions::to_utf8string(whatArg))
{
}
#ifdef _WIN32
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code and string message.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="whatArg">Message to use in what() string.</param>
websocket_exception(int errorCode, std::string whatArg)
: m_errorCode(utility::details::create_error_code(errorCode)), m_msg(std::move(whatArg))
{
}
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code and string message to use as the what() argument.
/// <param name="code">Error code.</param>
/// <param name="whatArg">Message to use in what() string.</param>
/// </summary>
websocket_exception(std::error_code code, std::string whatArg)
: m_errorCode(std::move(code)), m_msg(std::move(whatArg))
{
}
#endif
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code and category. The message of the error code will be used
/// as the <c>what</c> string message.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="cat">Error category for the code.</param>
websocket_exception(int errorCode, const std::error_category& cat) : m_errorCode(std::error_code(errorCode, cat))
{
m_msg = m_errorCode.message();
}
/// <summary>
/// Creates a <c>websocket_exception</c> from a error code and string message to use as the what() argument.
/// <param name="code">Error code.</param>
/// <param name="whatArg">Message to use in what() string.</param>
/// </summary>
websocket_exception(std::error_code code, const utility::string_t& whatArg)
: m_errorCode(std::move(code)), m_msg(utility::conversions::to_utf8string(whatArg))
{
}
/// <summary>
/// Gets a string identifying the cause of the exception.
/// </summary>
/// <returns>A null terminated character string.</returns>
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
/// <summary>
/// Gets the underlying error code for the cause of the exception.
/// </summary>
/// <returns>The <c>error_code</c> object associated with the exception.</returns>
const std::error_code& error_code() const CPPREST_NOEXCEPT { return m_errorCode; }
private:
std::error_code m_errorCode;
std::string m_msg;
};
namespace details
{
// Interface to be implemented by the websocket client callback implementations.
class websocket_client_callback_impl
{
public:
websocket_client_callback_impl(websocket_client_config config) : m_config(std::move(config)) {}
virtual ~websocket_client_callback_impl() CPPREST_NOEXCEPT {}
virtual pplx::task<void> connect() = 0;
virtual pplx::task<void> send(websocket_outgoing_message& msg) = 0;
virtual void set_message_handler(const std::function<void(const websocket_incoming_message&)>& handler) = 0;
virtual pplx::task<void> close() = 0;
virtual pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = {}) = 0;
virtual void set_close_handler(
const std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)>&
handler) = 0;
const web::uri& uri() const { return m_uri; }
void set_uri(const web::uri& uri) { m_uri = uri; }
const websocket_client_config& config() const { return m_config; }
static void verify_uri(const web::uri& uri)
{
// Most of the URI schema validation is taken care by URI class.
// We only need to check certain things specific to websockets.
if (uri.scheme() != _XPLATSTR("ws") && uri.scheme() != _XPLATSTR("wss"))
{
throw std::invalid_argument("URI scheme must be 'ws' or 'wss'");
}
if (uri.host().empty())
{
throw std::invalid_argument("URI must contain a hostname.");
}
// Fragment identifiers are meaningless in the context of WebSocket URIs
// and MUST NOT be used on these URIs.
if (!uri.fragment().empty())
{
throw std::invalid_argument("WebSocket URI must not contain fragment identifiers");
}
}
protected:
web::uri m_uri;
websocket_client_config m_config;
};
// Interface to be implemented by the websocket client task implementations.
class websocket_client_task_impl
{
public:
_ASYNCRTIMP websocket_client_task_impl(websocket_client_config config);
_ASYNCRTIMP virtual ~websocket_client_task_impl() CPPREST_NOEXCEPT;
_ASYNCRTIMP pplx::task<websocket_incoming_message> receive();
_ASYNCRTIMP void close_pending_tasks_with_error(const websocket_exception& exc);
const std::shared_ptr<websocket_client_callback_impl>& callback_client() const { return m_callback_client; };
private:
void set_handler();
// When a message arrives, if there are tasks waiting for a message, signal the topmost one.
// Else enqueue the message in a queue.
// m_receive_queue_lock : to guard access to the queue & m_client_closed
std::mutex m_receive_queue_lock;
// Queue to store incoming messages when there are no tasks waiting for a message
std::queue<websocket_incoming_message> m_receive_msg_queue;
// Queue to maintain the receive tasks when there are no messages(yet).
std::queue<pplx::task_completion_event<websocket_incoming_message>> m_receive_task_queue;
// Initially set to false, becomes true if a close frame is received from the server or
// if the underlying connection is aborted or terminated.
bool m_client_closed;
std::shared_ptr<websocket_client_callback_impl> m_callback_client;
};
} // namespace details
/// <summary>
/// Websocket client class, used to maintain a connection to a remote host for an extended session.
/// </summary>
class websocket_client
{
public:
/// <summary>
/// Creates a new websocket_client.
/// </summary>
websocket_client() : m_client(std::make_shared<details::websocket_client_task_impl>(websocket_client_config())) {}
/// <summary>
/// Creates a new websocket_client.
/// </summary>
/// <param name="config">The client configuration object containing the possible configuration options to initialize
/// the <c>websocket_client</c>. </param>
websocket_client(websocket_client_config config)
: m_client(std::make_shared<details::websocket_client_task_impl>(std::move(config)))
{
}
/// <summary>
/// Connects to the remote network destination. The connect method initiates the websocket handshake with the
/// remote network destination, takes care of the protocol upgrade request.
/// </summary>
/// <param name="uri">The uri address to connect. </param>
/// <returns>An asynchronous operation that is completed once the client has successfully connected to the websocket
/// server.</returns>
pplx::task<void> connect(const web::uri& uri)
{
m_client->callback_client()->verify_uri(uri);
m_client->callback_client()->set_uri(uri);
auto client = m_client;
return m_client->callback_client()->connect().then([client](pplx::task<void> result) {
try
{
result.get();
}
catch (const websocket_exception& ex)
{
client->close_pending_tasks_with_error(ex);
throw;
}
});
}
/// <summary>
/// Sends a websocket message to the server .
/// </summary>
/// <returns>An asynchronous operation that is completed once the message is sent.</returns>
pplx::task<void> send(websocket_outgoing_message msg) { return m_client->callback_client()->send(msg); }
/// <summary>
/// Receive a websocket message.
/// </summary>
/// <returns>An asynchronous operation that is completed when a message has been received by the client
/// endpoint.</returns>
pplx::task<websocket_incoming_message> receive() { return m_client->receive(); }
/// <summary>
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
/// server.
/// </summary>
/// <returns>An asynchronous operation that is completed the connection has been successfully closed.</returns>
pplx::task<void> close() { return m_client->callback_client()->close(); }
/// <summary>
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
/// server.
/// </summary>
/// <param name="close_status">Endpoint MAY use the following pre-defined status codes when sending a Close
/// frame.</param> <param name="close_reason">While closing an established connection, an endpoint may indicate the
/// reason for closure.</param> <returns>An asynchronous operation that is completed the connection has been
/// successfully closed.</returns>
pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = {})
{
return m_client->callback_client()->close(close_status, close_reason);
}
/// <summary>
/// Gets the websocket client URI.
/// </summary>
/// <returns>URI connected to.</returns>
const web::uri& uri() const { return m_client->callback_client()->uri(); }
/// <summary>
/// Gets the websocket client config object.
/// </summary>
/// <returns>A reference to the client configuration object.</returns>
const websocket_client_config& config() const { return m_client->callback_client()->config(); }
private:
std::shared_ptr<details::websocket_client_task_impl> m_client;
};
/// <summary>
/// Websocket client class, used to maintain a connection to a remote host for an extended session, uses callback APIs
/// for handling receive and close event instead of async task. For some scenarios would be a alternative for the
/// websocket_client like if you want to special handling on close event.
/// </summary>
class websocket_callback_client
{
public:
/// <summary>
/// Creates a new websocket_callback_client.
/// </summary>
_ASYNCRTIMP websocket_callback_client();
/// <summary>
/// Creates a new websocket_callback_client.
/// </summary>
/// <param name="client_config">The client configuration object containing the possible configuration options to
/// initialize the <c>websocket_client</c>. </param>
_ASYNCRTIMP websocket_callback_client(websocket_client_config client_config);
/// <summary>
/// Connects to the remote network destination. The connect method initiates the websocket handshake with the
/// remote network destination, takes care of the protocol upgrade request.
/// </summary>
/// <param name="uri">The uri address to connect. </param>
/// <returns>An asynchronous operation that is completed once the client has successfully connected to the websocket
/// server.</returns>
pplx::task<void> connect(const web::uri& uri)
{
m_client->verify_uri(uri);
m_client->set_uri(uri);
return m_client->connect();
}
/// <summary>
/// Sends a websocket message to the server .
/// </summary>
/// <returns>An asynchronous operation that is completed once the message is sent.</returns>
pplx::task<void> send(websocket_outgoing_message msg) { return m_client->send(msg); }
/// <summary>
/// Set the received handler for notification of client websocket messages.
/// </summary>
/// <param name="handler">A function representing the incoming websocket messages handler. It's parameters are:
/// msg: a <c>websocket_incoming_message</c> value indicating the message received
/// </param>
/// <remarks>If this handler is not set before connecting incoming messages will be missed.</remarks>
void set_message_handler(const std::function<void(const websocket_incoming_message& msg)>& handler)
{
m_client->set_message_handler(handler);
}
/// <summary>
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
/// server.
/// </summary>
/// <returns>An asynchronous operation that is completed the connection has been successfully closed.</returns>
pplx::task<void> close() { return m_client->close(); }
/// <summary>
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
/// server.
/// </summary>
/// <param name="close_status">Endpoint MAY use the following pre-defined status codes when sending a Close
/// frame.</param> <param name="close_reason">While closing an established connection, an endpoint may indicate the
/// reason for closure.</param> <returns>An asynchronous operation that is completed the connection has been
/// successfully closed.</returns>
pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = {})
{
return m_client->close(close_status, close_reason);
}
/// <summary>
/// Set the closed handler for notification of client websocket closing event.
/// </summary>
/// <param name="handler">The handler for websocket closing event, It's parameters are:
/// close_status: The pre-defined status codes used by the endpoint when sending a Close frame.
/// reason: The reason string used by the endpoint when sending a Close frame.
/// error: The error code if the websocket is closed with abnormal error.
/// </param>
void set_close_handler(const std::function<void(websocket_close_status close_status,
const utility::string_t& reason,
const std::error_code& error)>& handler)
{
m_client->set_close_handler(handler);
}
/// <summary>
/// Gets the websocket client URI.
/// </summary>
/// <returns>URI connected to.</returns>
const web::uri& uri() const { return m_client->uri(); }
/// <summary>
/// Gets the websocket client config object.
/// </summary>
/// <returns>A reference to the client configuration object.</returns>
const websocket_client_config& config() const { return m_client->config(); }
private:
std::shared_ptr<details::websocket_client_callback_impl> m_client;
};
} // namespace client
} // namespace websockets
} // namespace web
#endif
#endif

View File

@ -0,0 +1,249 @@
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Websocket incoming and outgoing message definitions.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/details/basic_types.h"
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
#include "cpprest/asyncrt_utils.h"
#include "cpprest/containerstream.h"
#include "cpprest/streams.h"
#include "cpprest/uri.h"
#include "pplx/pplxtasks.h"
#include <limits>
#include <memory>
namespace web
{
namespace websockets
{
namespace client
{
namespace details
{
class winrt_callback_client;
class wspp_callback_client;
#if defined(__cplusplus_winrt)
ref class ReceiveContext;
#endif
} // namespace details
/// <summary>
/// The different types of websocket message.
/// Text type contains UTF-8 encoded data.
/// Interpretation of Binary type is left to the application.
/// Note: The fragment types and control frames like close, ping, pong are not supported on WinRT.
/// </summary>
enum class websocket_message_type
{
text_message,
binary_message,
close,
ping,
pong
};
/// <summary>
/// Represents an outgoing websocket message
/// </summary>
class websocket_outgoing_message
{
public:
#if !defined(__cplusplus_winrt)
/// <summary>
/// Sets the outgoing message to be a ping message.
/// This is useful when the client side wants to check whether the server is alive.
/// </summary>
/// <param name="data">UTF-8 String containing the optional ping message.</param>
void set_ping_message(const std::string& data = {})
{
this->set_message_ping(concurrency::streams::container_buffer<std::string>(data));
}
/// <summary>
/// Sets the outgoing message to be an unsolicited pong message.
/// </summary>
/// <param name="data">UTF-8 String containing the optional pong message.</param>
void set_pong_message(const std::string& data = {})
{
this->set_message_pong(concurrency::streams::container_buffer<std::string>(data));
}
#endif
/// <summary>
/// Sets a UTF-8 message as the message body.
/// </summary>
/// <param name="data">UTF-8 String containing body of the message.</param>
void set_utf8_message(std::string&& data)
{
this->set_message(concurrency::streams::container_buffer<std::string>(std::move(data)));
}
/// <summary>
/// Sets a UTF-8 message as the message body.
/// </summary>
/// <param name="data">UTF-8 String containing body of the message.</param>
void set_utf8_message(const std::string& data)
{
this->set_message(concurrency::streams::container_buffer<std::string>(data));
}
/// <summary>
/// Sets a UTF-8 message as the message body.
/// </summary>
/// <param name="istream">casablanca input stream representing the body of the message.</param>
/// <remarks>Upon sending, the entire stream may be buffered to determine the length.</remarks>
void set_utf8_message(const concurrency::streams::istream& istream)
{
this->set_message(istream, SIZE_MAX, websocket_message_type::text_message);
}
/// <summary>
/// Sets a UTF-8 message as the message body.
/// </summary>
/// <param name="istream">casablanca input stream representing the body of the message.</param>
/// <param name="len">number of bytes to send.</param>
void set_utf8_message(const concurrency::streams::istream& istream, size_t len)
{
this->set_message(istream, len, websocket_message_type::text_message);
}
/// <summary>
/// Sets binary data as the message body.
/// </summary>
/// <param name="istream">casablanca input stream representing the body of the message.</param>
/// <param name="len">number of bytes to send.</param>
void set_binary_message(const concurrency::streams::istream& istream, size_t len)
{
this->set_message(istream, len, websocket_message_type::binary_message);
}
/// <summary>
/// Sets binary data as the message body.
/// </summary>
/// <param name="istream">Input stream representing the body of the message.</param>
/// <remarks>Upon sending, the entire stream may be buffered to determine the length.</remarks>
void set_binary_message(const concurrency::streams::istream& istream)
{
this->set_message(istream, SIZE_MAX, websocket_message_type::binary_message);
}
private:
friend class details::winrt_callback_client;
friend class details::wspp_callback_client;
pplx::task_completion_event<void> m_body_sent;
concurrency::streams::streambuf<uint8_t> m_body;
websocket_message_type m_msg_type;
size_t m_length;
void signal_body_sent() const { m_body_sent.set(); }
void signal_body_sent(const std::exception_ptr& e) const { m_body_sent.set_exception(e); }
const pplx::task_completion_event<void>& body_sent() const { return m_body_sent; }
#if !defined(__cplusplus_winrt)
void set_message_ping(const concurrency::streams::container_buffer<std::string>& buffer)
{
m_msg_type = websocket_message_type::ping;
m_length = static_cast<size_t>(buffer.size());
m_body = buffer;
}
void set_message_pong(const concurrency::streams::container_buffer<std::string>& buffer)
{
m_msg_type = websocket_message_type::pong;
m_length = static_cast<size_t>(buffer.size());
m_body = buffer;
}
#endif
void set_message(const concurrency::streams::container_buffer<std::string>& buffer)
{
m_msg_type = websocket_message_type::text_message;
m_length = static_cast<size_t>(buffer.size());
m_body = buffer;
}
void set_message(const concurrency::streams::istream& istream, size_t len, websocket_message_type msg_type)
{
m_msg_type = msg_type;
m_length = len;
m_body = istream.streambuf();
}
};
/// <summary>
/// Represents an incoming websocket message
/// </summary>
class websocket_incoming_message
{
public:
/// <summary>
/// Extracts the body of the incoming message as a string value, only if the message type is UTF-8.
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
/// </summary>
/// <returns>String containing body of the message.</returns>
_ASYNCRTIMP pplx::task<std::string> extract_string() const;
/// <summary>
/// Produces a stream which the caller may use to retrieve body from an incoming message.
/// Can be used for both UTF-8 (text) and binary message types.
/// </summary>
/// <returns>A readable, open asynchronous stream.</returns>
/// <remarks>
/// This cannot be used in conjunction with any other means of getting the body of the message.
/// </remarks>
concurrency::streams::istream body() const
{
auto to_uint8_t_stream =
[](const concurrency::streams::streambuf<uint8_t>& buf) -> concurrency::streams::istream {
return buf.create_istream();
};
return to_uint8_t_stream(m_body);
}
/// <summary>
/// Returns the length of the received message.
/// </summary>
size_t length() const { return static_cast<size_t>(m_body.size()); }
/// <summary>
/// Returns the type of the received message.
/// </summary>
CASABLANCA_DEPRECATED("Incorrectly spelled API, use message_type() instead.")
websocket_message_type messge_type() const { return m_msg_type; }
/// <summary>
/// Returns the type of the received message, either string or binary.
/// </summary>
/// <returns>websocket_message_type</returns>
websocket_message_type message_type() const { return m_msg_type; }
private:
friend class details::winrt_callback_client;
friend class details::wspp_callback_client;
#if defined(__cplusplus_winrt)
friend ref class details::ReceiveContext;
#endif
// Store message body in a container buffer backed by a string.
// Allows for optimization in the string message cases.
concurrency::streams::container_buffer<std::string> m_body;
websocket_message_type m_msg_type;
};
} // namespace client
} // namespace websockets
} // namespace web
#endif