Commit 14571fd2 authored by 藤森雅人's avatar 藤森雅人
Browse files

Initial commit

parents
/***
* 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
/***
* 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
#define CASABLANCA_UNREFERENCED_PARAMETER(x) (x)
#include <sal.h>
#else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv
#define __declspec(x) __attribute__((x))
#define dllimport
#define novtable /* no novtable equivalent */
#define __assume(x) \
do \
{ \
if (!(x)) __builtin_unreachable(); \
} while (false)
#define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x
#define CPPREST_NOEXCEPT noexcept
#define CPPREST_CONSTEXPR constexpr
#include <assert.h>
#define _ASSERTE(x) assert(x)
// No SAL on non Windows platforms
#include "cpprest/details/nosal.h"
#if !defined(__cdecl)
#if defined(cdecl)
#define __cdecl __attribute__((cdecl))
#else // ^^^ defined cdecl ^^^ // vvv !defined cdecl vvv
#define __cdecl
#endif // defined cdecl
#endif // not defined __cdecl
#if defined(__ANDROID__)
// This is needed to disable the use of __thread inside the boost library.
// Android does not support thread local storage -- if boost is included
// without this macro defined, it will create references to __tls_get_addr
// which (while able to link) will not be available at runtime and prevent
// the .so from loading.
#if not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#endif // not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#endif // defined(__ANDROID__)
#ifdef __clang__
#include <cstdio>
#endif // __clang__
#endif // _WIN32
#ifdef _NO_ASYNCRTIMP
#define _ASYNCRTIMP
#else // ^^^ _NO_ASYNCRTIMP ^^^ // vvv !_NO_ASYNCRTIMP vvv
#ifdef _ASYNCRT_EXPORT
#define _ASYNCRTIMP __declspec(dllexport)
#else // ^^^ _ASYNCRT_EXPORT ^^^ // vvv !_ASYNCRT_EXPORT vvv
#define _ASYNCRTIMP __declspec(dllimport)
#endif // _ASYNCRT_EXPORT
#endif // _NO_ASYNCRTIMP
#ifdef CASABLANCA_DEPRECATION_NO_WARNINGS
#define CASABLANCA_DEPRECATED(x)
#else
#define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x))
#endif // CASABLANCA_DEPRECATION_NO_WARNINGS
/***
* 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);
}
#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(response_type, "response_type")
DAT(scope, "scope")
DAT(state, "state")
DAT(token, "token")
DAT(token_type, "token_type")
#endif // _OAUTH2_STRINGS
/***
* 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
/***
* 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
/***
* 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
/***
* 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)
//{{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
/***
* 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;
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
#if defined(__cplusplus_winrt)
class winrt_encryption
{
public:
winrt_encryption() {}
_ASYNCRTIMP winrt_encryption(const std::wstring& data);
_ASYNCRTIMP plaintext_string decrypt() const;
private:
::pplx::task<Windows::Storage::Streams::IBuffer ^> m_buffer;
};
#else
class win32_encryption
{
public:
win32_encryption() {}
_ASYNCRTIMP win32_encryption(const std::wstring& data);
_ASYNCRTIMP ~win32_encryption();
_ASYNCRTIMP plaintext_string decrypt() const;
private:
std::vector<char> m_buffer;
size_t m_numCharacters;
};
#endif
#endif
} // 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) && !defined(CPPREST_TARGET_XP)
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) && !defined(CPPREST_TARGET_XP)
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) && !defined(CPPREST_TARGET_XP)
#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(_XPLATSTR("")), m_mode(use_default_) {}
/// <summary>
/// Creates a proxy with specified mode.
/// </summary>
/// <param name="mode">Mode to use.</param>
web_proxy(web_proxy_mode mode) : m_address(_XPLATSTR("")), m_mode(static_cast<web_proxy_mode_internal>(mode)) {}
/// <summary>
/// Creates a proxy explicitly with provided address.
/// </summary>
/// <param name="address">Proxy URI to use.</param>
web_proxy(uri address) : m_address(address), m_mode(user_provided_) {}
/// <summary>
/// Gets this proxy's URI address. Returns an empty URI if not explicitly set by user.
/// </summary>
/// <returns>A reference to this proxy's URI.</returns>
const uri& address() const { return m_address; }
/// <summary>
/// Gets the credentials used for authentication with this proxy.
/// </summary>
/// <returns>Credentials to for this proxy.</returns>
const web::credentials& credentials() const { return m_credentials; }
/// <summary>
/// Sets the credentials to use for authentication with this proxy.
/// </summary>
/// <param name="cred">Credentials to use for this proxy.</param>
void set_credentials(web::credentials cred)
{
if (m_mode == disabled_)
{
throw std::invalid_argument("Cannot attach credentials to a disabled proxy");
}
m_credentials = std::move(cred);
}
/// <summary>
/// Checks if this proxy was constructed with default settings.
/// </summary>
/// <returns>True if default, false otherwise.</param>
bool is_default() const { return m_mode == use_default_; }
/// <summary>
/// Checks if using a proxy is disabled.
/// </summary>
/// <returns>True if disabled, false otherwise.</returns>
bool is_disabled() const { return m_mode == disabled_; }
/// <summary>
/// Checks if the auto discovery protocol, WPAD, is to be used.
/// </summary>
/// <returns>True if auto discovery enabled, false otherwise.</returns>
bool is_auto_discovery() const { return m_mode == use_auto_discovery_; }
/// <summary>
/// Checks if a proxy address is explicitly specified by the user.
/// </summary>
/// <returns>True if a proxy address was explicitly specified, false otherwise.</returns>
bool is_specified() const { return m_mode == user_provided_; }
private:
web::uri m_address;
web_proxy_mode_internal m_mode;
web::credentials m_credentials;
};
} // namespace web
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Asynchronous File streams
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_FILE_STREAMS_H
#define CASA_FILE_STREAMS_H
#include "cpprest/astreambuf.h"
#include "cpprest/details/fileio.h"
#include "cpprest/streams.h"
#include <assert.h>
#ifndef _CONCRT_H
#ifndef _LWRCASE_CNCRRNCY
#define _LWRCASE_CNCRRNCY
// Note to reader: we're using lower-case namespace names everywhere, but the 'Concurrency' namespace
// is capitalized for historical reasons. The alias let's us pretend that style issue doesn't exist.
namespace Concurrency
{
}
namespace concurrency = Concurrency;
#endif
#endif
namespace Concurrency
{
namespace streams
{
// Forward declarations
template<typename _CharType>
class file_buffer;
namespace details
{
// This operation queue is NOT thread safe
class async_operation_queue
{
pplx::task<void> m_lastOperation;
public:
async_operation_queue() { m_lastOperation = pplx::task_from_result(); }
// It only accepts functors that take no argument and return pplx::task<T>
// This function may execute op inline, thus it could throw immediately
template<typename Func>
auto enqueue_operation(Func&& op) -> decltype(op())
{
decltype(op()) res; // res is task<T> , which always has default constructor
if (m_lastOperation.is_done())
{
res = op(); // Exceptions are expected to be thrown directly without catching
if (res.is_done()) return res;
}
else
{
res = m_lastOperation.then([=] {
return op(); // It will cause task unwrapping
});
}
m_lastOperation = res.then([&](decltype(op())) {
// This empty task is necessary for keeping the rest of the operations on the list running
// even when the previous operation gets error.
// Don't observe exception here.
});
return res;
}
void wait() const { m_lastOperation.wait(); }
};
/// <summary>
/// Private stream buffer implementation for file streams.
/// The class itself should not be used in application code, it is used by the stream definitions farther down in the
/// header file.
/// </summary>
template<typename _CharType>
class basic_file_buffer : public details::streambuf_state_manager<_CharType>
{
public:
typedef typename basic_streambuf<_CharType>::traits traits;
typedef typename basic_streambuf<_CharType>::int_type int_type;
typedef typename basic_streambuf<_CharType>::pos_type pos_type;
typedef typename basic_streambuf<_CharType>::off_type off_type;
virtual ~basic_file_buffer()
{
if (this->can_read())
{
this->_close_read().wait();
}
if (this->can_write())
{
this->_close_write().wait();
}
}
protected:
/// <summary>
/// <c>can_seek</c> is used to determine whether a stream buffer supports seeking.
/// </summary>
virtual bool can_seek() const { return this->is_open(); }
/// <summary>
/// <c>has_size<c/> is used to determine whether a stream buffer supports size().
/// </summary>
virtual bool has_size() const { return this->is_open(); }
virtual utility::size64_t size() const
{
if (!this->is_open()) return 0;
return _get_size(m_info, sizeof(_CharType));
}
/// <summary>
/// Gets the stream buffer size, if one has been set.
/// </summary>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const
{
if (direction == std::ios_base::in)
return m_info->m_buffer_size;
else
return 0;
}
/// <summary>
/// Sets the stream buffer implementation to buffer or not buffer.
/// </summary>
/// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
/// <param name="direction">The direction of buffering (in or out)</param>
/// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
/// will not have
/// any effect on what is returned by subsequent calls to buffer_size().</remarks>
virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in)
{
if (direction == std::ios_base::out) return;
m_info->m_buffer_size = size;
if (size == 0 && m_info->m_buffer != nullptr)
{
delete m_info->m_buffer;
m_info->m_buffer = nullptr;
}
}
/// <summary>
/// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
/// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
/// incurring the overhead of using tasks.
/// </summary>
virtual size_t in_avail() const
{
pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
return _in_avail_unprot();
}
size_t _in_avail_unprot() const
{
if (!this->is_open()) return 0;
if (m_info->m_buffer == nullptr || m_info->m_buffill == 0) return 0;
if (m_info->m_bufoff > m_info->m_rdpos || (m_info->m_bufoff + m_info->m_buffill) < m_info->m_rdpos) return 0;
msl::safeint3::SafeInt<size_t> rdpos(m_info->m_rdpos);
msl::safeint3::SafeInt<size_t> buffill(m_info->m_buffill);
msl::safeint3::SafeInt<size_t> bufpos = rdpos - m_info->m_bufoff;
return buffill - bufpos;
}
_file_info* _close_stream()
{
// indicate that we are no longer open
auto fileInfo = m_info;
m_info = nullptr;
return fileInfo;
}
static pplx::task<void> _close_file(_In_ _file_info* fileInfo)
{
pplx::task_completion_event<void> result_tce;
auto callback = new _filestream_callback_close(result_tce);
if (!_close_fsb_nolock(&fileInfo, callback))
{
delete callback;
return pplx::task_from_result();
}
return pplx::create_task(result_tce);
}
// Workaround GCC compiler bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58972
void _invoke_parent_close_read() { streambuf_state_manager<_CharType>::_close_read(); }
pplx::task<void> _close_read()
{
return m_readOps.enqueue_operation([this] {
_invoke_parent_close_read();
if (this->can_write())
{
return pplx::task_from_result();
}
else
{
// Neither heads are open. Close the underlying device
// to indicate that we are no longer open
auto fileInfo = _close_stream();
return _close_file(fileInfo);
}
});
}
pplx::task<void> _close_write()
{
streambuf_state_manager<_CharType>::_close_write();
if (this->can_read())
{
// Read head is still open. Just flush the write data
return flush_internal();
}
else
{
// Neither heads are open. Close the underlying device
// We need to flush all writes if the file was opened for writing.
return flush_internal().then([=](pplx::task<void> flushTask) -> pplx::task<void> {
// swallow exception from flush
try
{
flushTask.wait();
}
catch (...)
{
}
// indicate that we are no longer open
auto fileInfo = this->_close_stream();
return this->_close_file(fileInfo);
});
}
}
/// <summary>
/// Writes a single byte to an output stream.
/// </summary>
/// <param name="ch">The byte to write</param>
/// <returns>A <c>task</c> that holds the value of the byte written. This is EOF if the write operation
/// fails.</returns>
virtual pplx::task<int_type> _putc(_CharType ch)
{
auto result_tce = pplx::task_completion_event<size_t>();
auto callback = new _filestream_callback_write<size_t>(m_info, result_tce);
// Potentially we should consider deprecating this API, it is TERRIBLY inefficient.
std::shared_ptr<_CharType> sharedCh;
try
{
sharedCh = std::make_shared<_CharType>(ch);
}
catch (const std::bad_alloc&)
{
delete callback;
throw;
}
size_t written = _putn_fsb(m_info, callback, sharedCh.get(), 1, sizeof(_CharType));
if (written == sizeof(_CharType))
{
delete callback;
return pplx::task_from_result<int_type>(ch);
}
return pplx::create_task(result_tce).then([sharedCh](size_t) { return static_cast<int_type>(*sharedCh); });
}
/// <summary>
/// Allocates a contiguous memory block and returns it.
/// </summary>
/// <param name="count">The number of characters to allocate.</param>
/// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
/// alloc/commit.</returns>
_CharType* _alloc(size_t) { return nullptr; }
/// <summary>
/// Submits a block already allocated by the stream buffer.
/// </summary>
/// <param name="ptr">Count of characters to be committed.</param>
void _commit(size_t) {}
/// <summary>
/// Gets a pointer to the next already allocated contiguous block of data.
/// </summary>
/// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
/// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
/// <remarks>
/// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
/// there is no block to return immediately or that the stream buffer does not support the operation.
/// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
/// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
/// a subsequent read will not succeed.
/// </remarks>
virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
{
ptr = nullptr;
count = 0;
return false;
}
/// <summary>
/// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
/// de-allocate the memory, if it so desires. Move the read position ahead by the count.
/// </summary>
/// <param name="ptr">A pointer to the block of data to be released.</param>
/// <param name="count">The number of characters that were read.</param>
virtual void release(_Out_writes_(count) _CharType*, _In_ size_t count) { (void)(count); }
/// <summary>
/// Writes a number of characters to the stream.
/// </summary>
/// <param name="ptr">A pointer to the block of data to be written.</param>
/// <param name="count">The number of characters to write.</param>
/// <returns>A <c>task</c> that holds the number of characters actually written, either 'count' or 0.</returns>
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
{
auto result_tce = pplx::task_completion_event<size_t>();
auto callback = new _filestream_callback_write<size_t>(m_info, result_tce);
size_t written = _putn_fsb(m_info, callback, ptr, count, sizeof(_CharType));
if (written != 0 && written != size_t(-1))
{
delete callback;
written = written / sizeof(_CharType);
return pplx::task_from_result<size_t>(written);
}
return pplx::create_task(result_tce);
}
// Temporarily needed until the deprecated putn is removed.
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count, bool copy)
{
if (copy)
{
auto sharedData = std::make_shared<std::vector<_CharType>>(ptr, ptr + count);
return _putn(ptr, count).then([sharedData](size_t size) { return size; });
}
else
{
return _putn(ptr, count);
}
}
/// <summary>
/// Reads a single byte from the stream and advance the read position.
/// </summary>
/// <returns>A <c>task</c> that holds the value of the byte read. This is EOF if the read fails.</returns>
virtual pplx::task<int_type> _bumpc()
{
return m_readOps.enqueue_operation([this]() -> pplx::task<int_type> {
if (_in_avail_unprot() > 0)
{
pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
// Check again once the lock is held.
if (_in_avail_unprot() > 0)
{
auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
_CharType ch = m_info->m_buffer[bufoff * sizeof(_CharType)];
m_info->m_rdpos += 1;
return pplx::task_from_result<int_type>(ch);
}
}
auto result_tce = pplx::task_completion_event<int_type>();
auto callback = new _filestream_callback_bumpc(m_info, result_tce);
size_t ch = _getn_fsb(m_info, callback, &callback->m_ch, 1, sizeof(_CharType));
if (ch == sizeof(_CharType))
{
pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
m_info->m_rdpos += 1;
_CharType ch1 = (_CharType)callback->m_ch;
delete callback;
return pplx::task_from_result<int_type>(ch1);
}
return pplx::create_task(result_tce);
});
}
/// <summary>
/// Reads a single byte from the stream and advance the read position.
/// </summary>
/// <returns>The value of the byte. EOF if the read fails. <see cref="::requires_async method" /> if an asynchronous
/// read is required</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
virtual int_type _sbumpc()
{
m_readOps.wait();
if (m_info->m_atend) return traits::eof();
if (_in_avail_unprot() == 0) return traits::requires_async();
pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
if (_in_avail_unprot() == 0) return traits::requires_async();
auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
_CharType ch = m_info->m_buffer[bufoff * sizeof(_CharType)];
m_info->m_rdpos += 1;
return (int_type)ch;
}
pplx::task<int_type> _getcImpl()
{
if (_in_avail_unprot() > 0)
{
pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
// Check again once the lock is held.
if (_in_avail_unprot() > 0)
{
auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
_CharType ch = m_info->m_buffer[bufoff * sizeof(_CharType)];
return pplx::task_from_result<int_type>(ch);
}
}
auto result_tce = pplx::task_completion_event<int_type>();
auto callback = new _filestream_callback_getc(m_info, result_tce);
size_t ch = _getn_fsb(m_info, callback, &callback->m_ch, 1, sizeof(_CharType));
if (ch == sizeof(_CharType))
{
pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
_CharType ch1 = (_CharType)callback->m_ch;
delete callback;
return pplx::task_from_result<int_type>(ch1);
}
return pplx::create_task(result_tce);
}
/// <summary>
/// Reads a single byte from the stream without advancing the read position.
/// </summary>
/// <returns>The value of the byte. EOF if the read fails.</returns>
pplx::task<int_type> _getc()
{
return m_readOps.enqueue_operation([this]() -> pplx::task<int_type> { return _getcImpl(); });
}
/// <summary>
/// Reads a single byte from the stream without advancing the read position.
/// </summary>
/// <returns>The value of the byte. EOF if the read fails. <see cref="::requires_async method" /> if an asynchronous
/// read is required</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
int_type _sgetc()
{
m_readOps.wait();
if (m_info->m_atend) return traits::eof();
if (_in_avail_unprot() == 0) return traits::requires_async();
pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
if (_in_avail_unprot() == 0) return traits::requires_async();
auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
_CharType ch = m_info->m_buffer[bufoff * sizeof(_CharType)];
return (int_type)ch;
}
/// <summary>
/// Advances the read position, then return the next character without advancing again.
/// </summary>
/// <returns>A <c>task</c> that holds the value of the byte, which is EOF if the read fails.</returns>
virtual pplx::task<int_type> _nextc()
{
return m_readOps.enqueue_operation([this]() -> pplx::task<int_type> {
_seekrdpos_fsb(m_info, m_info->m_rdpos + 1, sizeof(_CharType));
if (m_info->m_atend) return pplx::task_from_result(basic_file_buffer<_CharType>::traits::eof());
return this->_getcImpl();
});
}
/// <summary>
/// Retreats the read position, then return the current character without advancing.
/// </summary>
/// <returns>A <c>task</c> that holds the value of the byte. The value is EOF if the read fails,
/// <c>requires_async</c> if an asynchronous read is required</returns>
virtual pplx::task<int_type> _ungetc()
{
return m_readOps.enqueue_operation([this]() -> pplx::task<int_type> {
if (m_info->m_rdpos == 0)
return pplx::task_from_result<int_type>(basic_file_buffer<_CharType>::traits::eof());
_seekrdpos_fsb(m_info, m_info->m_rdpos - 1, sizeof(_CharType));
return this->_getcImpl();
});
}
/// <summary>
/// Reads up to a given number of characters from the stream.
/// </summary>
/// <param name="ptr">The address of the target memory area</param>
/// <param name="count">The maximum number of characters to read</param>
/// <returns>A <c>task</c> that holds the number of characters read. This number is O if the end of the stream is
/// reached, EOF if there is some error.</returns>
virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
return m_readOps.enqueue_operation([=]() -> pplx::task<size_t> {
if (m_info->m_atend || count == 0) return pplx::task_from_result<size_t>(0);
if (_in_avail_unprot() >= count)
{
pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
// Check again once the lock is held.
if (_in_avail_unprot() >= count)
{
auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
std::memcpy(
(void*)ptr, this->m_info->m_buffer + bufoff * sizeof(_CharType), count * sizeof(_CharType));
m_info->m_rdpos += count;
return pplx::task_from_result<size_t>(count);
}
}
auto result_tce = pplx::task_completion_event<size_t>();
auto callback = new _filestream_callback_read(m_info, result_tce);
size_t read = _getn_fsb(m_info, callback, ptr, count, sizeof(_CharType));
if (read != 0 && read != size_t(-1))
{
delete callback;
pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
m_info->m_rdpos += read / sizeof(_CharType);
return pplx::task_from_result<size_t>(read / sizeof(_CharType));
}
return pplx::create_task(result_tce);
});
}
/// <summary>
/// Reads up to a given number of characters from the stream.
/// </summary>
/// <param name="ptr">The address of the target memory area</param>
/// <param name="count">The maximum number of characters to read</param>
/// <returns>The number of characters read. O if the end of the stream is reached or an asynchronous read is
/// required.</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
{
m_readOps.wait();
if (m_info->m_atend) return 0;
if (count == 0 || in_avail() == 0) return 0;
pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock);
size_t available = _in_avail_unprot();
size_t copy = (count < available) ? count : available;
auto bufoff = m_info->m_rdpos - m_info->m_bufoff;
std::memcpy((void*)ptr, this->m_info->m_buffer + bufoff * sizeof(_CharType), copy * sizeof(_CharType));
m_info->m_rdpos += copy;
m_info->m_atend = (copy < count);
return copy;
}
/// <summary>
/// Copies up to a given number of characters from the stream.
/// </summary>
/// <param name="ptr">The address of the target memory area</param>
/// <param name="count">The maximum number of characters to copy</param>
/// <returns>The number of characters copied. O if the end of the stream is reached or an asynchronous read is
/// required.</returns> <remarks>This is a synchronous operation, but is guaranteed to never block.</remarks>
virtual size_t _scopy(_CharType*, size_t) { return 0; }
/// <summary>
/// Gets the current read or write position in the stream.
/// </summary>
/// <param name="direction">The I/O direction to seek (see remarks)</param>
/// <returns>The current position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the direction parameter defines whether to move the read or the write
/// cursor.</remarks>
virtual pos_type getpos(std::ios_base::openmode mode) const
{
return const_cast<basic_file_buffer*>(this)->seekoff(0, std::ios_base::cur, mode);
}
/// <summary>
/// Seeks to the given position.
/// </summary>
/// <param name="pos">The offset from the beginning of the stream</param>
/// <param name="direction">The I/O direction to seek (see remarks)</param>
/// <returns>The position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the direction parameter defines whether to move the read or the write
/// cursor.</remarks>
virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode)
{
if (mode == std::ios_base::in)
{
m_readOps.wait();
return (pos_type)_seekrdpos_fsb(m_info, size_t(pos), sizeof(_CharType));
}
else if ((m_info->m_mode & std::ios::ios_base::app) == 0)
{
return (pos_type)_seekwrpos_fsb(m_info, size_t(pos), sizeof(_CharType));
}
return (pos_type)Concurrency::streams::char_traits<_CharType>::eof();
}
/// <summary>
/// Seeks to a position given by a relative offset.
/// </summary>
/// <param name="offset">The relative position to seek to</param>
/// <param name="way">The starting point (beginning, end, current) for the seek.</param>
/// <param name="mode">The I/O direction to seek (see remarks)</param>
/// <returns>The position. EOF if the operation fails.</returns>
/// <remarks>Some streams may have separate write and read cursors.
/// For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
{
if (mode == std::ios_base::in)
{
m_readOps.wait();
size_t current_pos = static_cast<size_t>(-1);
switch (way)
{
case std::ios_base::beg: return (pos_type)_seekrdpos_fsb(m_info, size_t(offset), sizeof(_CharType));
case std::ios_base::cur:
return (pos_type)_seekrdpos_fsb(m_info, size_t(m_info->m_rdpos + offset), sizeof(_CharType));
case std::ios_base::end:
current_pos = _seekrdtoend_fsb(m_info, int64_t(offset), sizeof(_CharType));
if (current_pos == static_cast<size_t>(-1))
{
return -1;
}
return (pos_type)current_pos;
default:
// Fail on invalid input (_S_ios_seekdir_end)
assert(false);
}
}
else if ((m_info->m_mode & std::ios::ios_base::app) == 0)
{
switch (way)
{
case std::ios_base::beg: return (pos_type)_seekwrpos_fsb(m_info, size_t(offset), sizeof(_CharType));
case std::ios_base::cur:
return (pos_type)_seekwrpos_fsb(m_info, size_t(m_info->m_wrpos + offset), sizeof(_CharType));
case std::ios_base::end: return (pos_type)_seekwrpos_fsb(m_info, size_t(-1), sizeof(_CharType));
default:
// Fail on invalid input (_S_ios_seekdir_end)
assert(false);
}
}
return (pos_type)traits::eof();
}
/// <summary>
/// For output streams, flush any internally buffered data to the underlying medium.
/// </summary>
virtual pplx::task<bool> _sync()
{
return flush_internal().then([]() { return true; });
}
private:
template<typename _CharType1>
friend class ::concurrency::streams::file_buffer;
pplx::task<void> flush_internal()
{
pplx::task_completion_event<void> result_tce;
auto callback = utility::details::make_unique<_filestream_callback_write_b>(m_info, result_tce);
if (!_sync_fsb(m_info, callback.get()))
{
return pplx::task_from_exception<void>(std::runtime_error("failure to flush stream"));
}
callback.release();
return pplx::create_task(result_tce);
}
basic_file_buffer(_In_ _file_info* info) : streambuf_state_manager<_CharType>(info->m_mode), m_info(info) {}
#if !defined(__cplusplus_winrt)
static pplx::task<std::shared_ptr<basic_streambuf<_CharType>>> open(
const utility::string_t& _Filename,
std::ios_base::openmode _Mode = std::ios_base::out,
#ifdef _WIN32
int _Prot = (int)std::ios_base::_Openprot
#else
int _Prot = 0 // unsupported on Linux, for now
#endif
)
{
auto result_tce = pplx::task_completion_event<std::shared_ptr<basic_streambuf<_CharType>>>();
auto callback = new _filestream_callback_open(result_tce);
_open_fsb_str(callback, _Filename.c_str(), _Mode, _Prot);
return pplx::create_task(result_tce);
}
#else
static pplx::task<std::shared_ptr<basic_streambuf<_CharType>>> open(
::Windows::Storage::StorageFile ^ file, std::ios_base::openmode _Mode = std::ios_base::out)
{
auto result_tce = pplx::task_completion_event<std::shared_ptr<basic_streambuf<_CharType>>>();
auto callback = new _filestream_callback_open(result_tce);
_open_fsb_stf_str(callback, file, _Mode, 0);
return pplx::create_task(result_tce);
}
#endif
class _filestream_callback_open : public details::_filestream_callback
{
public:
_filestream_callback_open(const pplx::task_completion_event<std::shared_ptr<basic_streambuf<_CharType>>>& op)
: m_op(op)
{
}
virtual void on_opened(_In_ _file_info* info)
{
m_op.set(std::shared_ptr<basic_file_buffer<_CharType>>(new basic_file_buffer<_CharType>(info)));
delete this;
}
virtual void on_error(const std::exception_ptr& e)
{
m_op.set_exception(e);
delete this;
}
private:
pplx::task_completion_event<std::shared_ptr<basic_streambuf<_CharType>>> m_op;
};
class _filestream_callback_close : public details::_filestream_callback
{
public:
_filestream_callback_close(const pplx::task_completion_event<void>& op) : m_op(op) {}
virtual void on_closed()
{
m_op.set();
delete this;
}
virtual void on_error(const std::exception_ptr& e)
{
m_op.set_exception(e);
delete this;
}
private:
pplx::task_completion_event<void> m_op;
};
template<typename ResultType>
class _filestream_callback_write : public details::_filestream_callback
{
public:
_filestream_callback_write(_In_ _file_info* info, const pplx::task_completion_event<ResultType>& op)
: m_info(info), m_op(op)
{
}
virtual void on_completed(size_t result)
{
m_op.set((ResultType)result / sizeof(_CharType));
delete this;
}
virtual void on_error(const std::exception_ptr& e)
{
m_op.set_exception(e);
delete this;
}
private:
_file_info* m_info;
pplx::task_completion_event<ResultType> m_op;
};
class _filestream_callback_write_b : public details::_filestream_callback
{
public:
_filestream_callback_write_b(_In_ _file_info* info, const pplx::task_completion_event<void>& op)
: m_info(info), m_op(op)
{
}
virtual void on_completed(size_t)
{
m_op.set();
delete this;
}
virtual void on_error(const std::exception_ptr& e)
{
m_op.set_exception(e);
delete this;
}
private:
_file_info* m_info;
pplx::task_completion_event<void> m_op;
};
class _filestream_callback_read : public details::_filestream_callback
{
public:
_filestream_callback_read(_In_ _file_info* info, const pplx::task_completion_event<size_t>& op)
: m_info(info), m_op(op)
{
}
virtual void on_completed(size_t result)
{
result = result / sizeof(_CharType);
m_info->m_rdpos += result;
m_op.set(result);
delete this;
}
virtual void on_error(const std::exception_ptr& e)
{
m_op.set_exception(e);
delete this;
}
private:
_file_info* m_info;
pplx::task_completion_event<size_t> m_op;
};
class _filestream_callback_bumpc : public details::_filestream_callback
{
public:
_filestream_callback_bumpc(_In_ _file_info* info, const pplx::task_completion_event<int_type>& op)
: m_ch(0), m_info(info), m_op(op)
{
}
virtual void on_completed(size_t result)
{
if (result == sizeof(_CharType))
{
m_info->m_rdpos += 1;
m_op.set(m_ch);
}
else
{
m_op.set(traits::eof());
}
delete this;
}
virtual void on_error(const std::exception_ptr& e)
{
m_op.set_exception(e);
delete this;
}
int_type m_ch;
private:
_file_info* m_info;
pplx::task_completion_event<int_type> m_op;
};
class _filestream_callback_getc : public details::_filestream_callback
{
public:
_filestream_callback_getc(_In_ _file_info* info, const pplx::task_completion_event<int_type>& op)
: m_ch(0), m_info(info), m_op(op)
{
}
virtual void on_completed(size_t result)
{
if (result == sizeof(_CharType))
{
m_op.set(m_ch);
}
else
{
m_op.set(traits::eof());
}
delete this;
}
int_type m_ch;
virtual void on_error(const std::exception_ptr& e)
{
m_op.set_exception(e);
delete this;
}
private:
_file_info* m_info;
pplx::task_completion_event<int_type> m_op;
};
_file_info* m_info;
async_operation_queue m_readOps;
};
} // namespace details
/// <summary>
/// Stream buffer for file streams.
/// </summary>
/// <typeparam name="_CharType">
/// The data type of the basic element of the <c>file_buffer</c>.
/// </typeparam>
template<typename _CharType>
class file_buffer
{
public:
#if !defined(__cplusplus_winrt)
/// <summary>
/// Open a new stream buffer representing the given file.
/// </summary>
/// <param name="file_name">The name of the file</param>
/// <param name="mode">The opening mode of the file</param>
/// <param name="prot">The file protection mode</param>
/// <returns>A <c>task</c> that returns an opened stream buffer on completion.</returns>
static pplx::task<streambuf<_CharType>> open(const utility::string_t& file_name,
std::ios_base::openmode mode = std::ios_base::out,
#ifdef _WIN32
int prot = _SH_DENYRD
#else
int prot = 0 // unsupported on Linux
#endif
)
{
auto bfb = details::basic_file_buffer<_CharType>::open(file_name, mode, prot);
return bfb.then(
[](pplx::task<std::shared_ptr<details::basic_streambuf<_CharType>>> op) -> streambuf<_CharType> {
return streambuf<_CharType>(op.get());
});
}
#else
/// <summary>
/// Open a new stream buffer representing the given file.
/// </summary>
/// <param name="file">The StorageFile instance</param>
/// <param name="mode">The opening mode of the file</param>
/// <param name="prot">The file protection mode</param>
/// <returns>A <c>task</c> that returns an opened stream buffer on completion.</returns>
static pplx::task<streambuf<_CharType>> open(::Windows::Storage::StorageFile ^ file,
std::ios_base::openmode mode = std::ios_base::out)
{
auto bfb = details::basic_file_buffer<_CharType>::open(file, mode);
return bfb.then(
[](pplx::task<std::shared_ptr<details::basic_streambuf<_CharType>>> op) -> streambuf<_CharType> {
return streambuf<_CharType>(op.get());
});
}
#endif
};
/// <summary>
/// File stream class containing factory functions for file streams.
/// </summary>
/// <typeparam name="_CharType">
/// The data type of the basic element of the <c>file_stream</c>.
/// </typeparam>
template<typename _CharType>
class file_stream
{
public:
#if !defined(__cplusplus_winrt)
/// <summary>
/// Open a new input stream representing the given file.
/// The file should already exist on disk, or an exception will be thrown.
/// </summary>
/// <param name="file_name">The name of the file</param>
/// <param name="mode">The opening mode of the file</param>
/// <param name="prot">The file protection mode</param>
/// <returns>A <c>task</c> that returns an opened input stream on completion.</returns>
static pplx::task<streams::basic_istream<_CharType>> open_istream(const utility::string_t& file_name,
std::ios_base::openmode mode = std::ios_base::in,
#ifdef _WIN32
int prot = (int)std::ios_base::_Openprot
#else
int prot = 0
#endif
)
{
mode |= std::ios_base::in;
return streams::file_buffer<_CharType>::open(file_name, mode, prot)
.then([](streams::streambuf<_CharType> buf) -> basic_istream<_CharType> {
return basic_istream<_CharType>(buf);
});
}
/// <summary>
/// Open a new output stream representing the given file.
/// If the file does not exist, it will be create unless the folder or directory
/// where it is to be found also does not exist.
/// </summary>
/// <param name="file_name">The name of the file</param>
/// <param name="mode">The opening mode of the file</param>
/// <param name="prot">The file protection mode</param>
/// <returns>A <c>task</c> that returns an opened output stream on completion.</returns>
static pplx::task<streams::basic_ostream<_CharType>> open_ostream(const utility::string_t& file_name,
std::ios_base::openmode mode = std::ios_base::out,
#ifdef _WIN32
int prot = (int)std::ios_base::_Openprot
#else
int prot = 0
#endif
)
{
mode |= std::ios_base::out;
return streams::file_buffer<_CharType>::open(file_name, mode, prot)
.then([](streams::streambuf<_CharType> buf) -> basic_ostream<_CharType> {
return basic_ostream<_CharType>(buf);
});
}
#else
/// <summary>
/// Open a new input stream representing the given file.
/// The file should already exist on disk, or an exception will be thrown.
/// </summary>
/// <param name="file">The StorageFile reference representing the file</param>
/// <param name="mode">The opening mode of the file</param>
/// <returns>A <c>task</c> that returns an opened input stream on completion.</returns>
static pplx::task<streams::basic_istream<_CharType>> open_istream(::Windows::Storage::StorageFile ^ file,
std::ios_base::openmode mode = std::ios_base::in)
{
mode |= std::ios_base::in;
return streams::file_buffer<_CharType>::open(file, mode)
.then([](streams::streambuf<_CharType> buf) -> basic_istream<_CharType> {
return basic_istream<_CharType>(buf);
});
}
/// <summary>
/// Open a new output stream representing the given file.
/// If the file does not exist, it will be create unless the folder or directory
/// where it is to be found also does not exist.
/// </summary>
/// <param name="file">The StorageFile reference representing the file</param>
/// <param name="mode">The opening mode of the file</param>
/// <returns>A <c>task</c> that returns an opened output stream on completion.</returns>
static pplx::task<streams::basic_ostream<_CharType>> open_ostream(::Windows::Storage::StorageFile ^ file,
std::ios_base::openmode mode = std::ios_base::out)
{
mode |= std::ios_base::out;
return streams::file_buffer<_CharType>::open(file, mode)
.then([](streams::streambuf<_CharType> buf) -> basic_ostream<_CharType> {
return basic_ostream<_CharType>(buf);
});
}
#endif
};
typedef file_stream<uint8_t> fstream;
} // namespace streams
} // namespace Concurrency
#endif
/***
* 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 !defined(CPPREST_TARGET_XP)
#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)
, m_buffer_request(false)
#endif
{
}
#if !defined(CPPREST_TARGET_XP)
/// <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)
/// <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>
/// 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 !defined(CPPREST_TARGET_XP)
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)
bool m_buffer_request;
#endif
};
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)
extern const utility::char_t* get_with_body_err_msg;
#endif
} // namespace details
} // namespace client
} // namespace http
} // namespace web
#endif
/***
* 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
/***
* 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
/***
* 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
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Request and reply message definitions.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include "cpprest/asyncrt_utils.h"
#include "cpprest/containerstream.h"
#include "cpprest/details/cpprest_compat.h"
#include "cpprest/http_compression.h"
#include "cpprest/http_headers.h"
#include "cpprest/json.h"
#include "cpprest/streams.h"
#include "cpprest/uri.h"
#include "pplx/pplxtasks.h"
#include <map>
#include <memory>
#include <string>
#include <system_error>
#include <vector>
namespace web
{
namespace http
{
// URI class has been moved from web::http namespace to web namespace.
// The below using declarations ensure we don't break existing code.
// Please use the web::uri class going forward.
using web::uri;
using web::uri_builder;
namespace client
{
class http_client;
}
/// <summary>
/// Represents the HTTP protocol version of a message, as {major, minor}.
/// </summary>
struct http_version
{
uint8_t major;
uint8_t minor;
inline bool operator==(const http_version& other) const { return major == other.major && minor == other.minor; }
inline bool operator<(const http_version& other) const
{
return major < other.major || (major == other.major && minor < other.minor);
}
inline bool operator!=(const http_version& other) const { return !(*this == other); }
inline bool operator>=(const http_version& other) const { return !(*this < other); }
inline bool operator>(const http_version& other) const { return !(*this < other || *this == other); }
inline bool operator<=(const http_version& other) const { return *this < other || *this == other; }
/// <summary>
/// Creates <c>http_version</c> from an HTTP-Version string, "HTTP" "/" 1*DIGIT "." 1*DIGIT.
/// </summary>
/// <returns>Returns a <c>http_version</c> of {0, 0} if not successful.</returns>
static _ASYNCRTIMP http_version __cdecl from_string(const std::string& http_version_string);
/// <summary>
/// Returns the string representation of the <c>http_version</c>.
/// </summary>
_ASYNCRTIMP std::string to_utf8string() const;
};
/// <summary>
/// Predefined HTTP protocol versions.
/// </summary>
class http_versions
{
public:
_ASYNCRTIMP static const http_version HTTP_0_9;
_ASYNCRTIMP static const http_version HTTP_1_0;
_ASYNCRTIMP static const http_version HTTP_1_1;
};
/// <summary>
/// Predefined method strings for the standard HTTP methods mentioned in the
/// HTTP 1.1 specification.
/// </summary>
typedef utility::string_t method;
/// <summary>
/// Common HTTP methods.
/// </summary>
class methods
{
public:
#define _METHODS
#define DAT(a, b) _ASYNCRTIMP const static method a;
#include "cpprest/details/http_constants.dat"
#undef _METHODS
#undef DAT
};
typedef unsigned short status_code;
/// <summary>
/// Predefined values for all of the standard HTTP 1.1 response status codes.
/// </summary>
class status_codes
{
public:
#define _PHRASES
#define DAT(a, b, c) const static status_code a = b;
#include "cpprest/details/http_constants.dat"
#undef _PHRASES
#undef DAT
};
namespace details
{
/// <summary>
/// Constants for MIME types.
/// </summary>
class mime_types
{
public:
#define _MIME_TYPES
#define DAT(a, b) _ASYNCRTIMP const static utility::string_t a;
#include "cpprest/details/http_constants.dat"
#undef _MIME_TYPES
#undef DAT
};
/// <summary>
/// Constants for charset types.
/// </summary>
class charset_types
{
public:
#define _CHARSET_TYPES
#define DAT(a, b) _ASYNCRTIMP const static utility::string_t a;
#include "cpprest/details/http_constants.dat"
#undef _CHARSET_TYPES
#undef DAT
};
} // namespace details
/// Message direction
namespace message_direction
{
/// <summary>
/// Enumeration used to denote the direction of a message: a request with a body is
/// an upload, a response with a body is a download.
/// </summary>
enum direction
{
upload,
download
};
} // namespace message_direction
typedef utility::string_t reason_phrase;
typedef std::function<void(message_direction::direction, utility::size64_t)> progress_handler;
struct http_status_to_phrase
{
unsigned short id;
reason_phrase phrase;
};
/// <summary>
/// Constants for the HTTP headers mentioned in RFC 2616.
/// </summary>
class header_names
{
public:
#define _HEADER_NAMES
#define DAT(a, b) _ASYNCRTIMP const static utility::string_t a;
#include "cpprest/details/http_constants.dat"
#undef _HEADER_NAMES
#undef DAT
};
/// <summary>
/// Represents an HTTP error. This class holds an error message and an optional error code.
/// </summary>
class http_exception : public std::exception
{
public:
/// <summary>
/// Creates an <c>http_exception</c> with just a string message and no error code.
/// </summary>
/// <param name="whatArg">Error message string.</param>
http_exception(const utility::string_t& whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {}
#ifdef _WIN32
/// <summary>
/// Creates an <c>http_exception</c> with just a string message and no error code.
/// </summary>
/// <param name="whatArg">Error message string.</param>
http_exception(std::string whatArg) : m_msg(std::move(whatArg)) {}
#endif
/// <summary>
/// Creates an <c>http_exception</c> with from a error code using the current platform error category.
/// The message of the error code will be used as the what() string message.
/// </summary>
/// <param name="errorCode">Error code value.</param>
http_exception(int errorCode) : m_errorCode(utility::details::create_error_code(errorCode))
{
m_msg = m_errorCode.message();
}
/// <summary>
/// Creates an <c>http_exception</c> with from a error code using the current platform error category.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="whatArg">Message to use in what() string.</param>
http_exception(int errorCode, const utility::string_t& whatArg)
: m_errorCode(utility::details::create_error_code(errorCode))
, m_msg(utility::conversions::to_utf8string(whatArg))
{
}
#ifdef _WIN32
/// <summary>
/// Creates an <c>http_exception</c> with from a error code using the current platform error category.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="whatArg">Message to use in what() string.</param>
http_exception(int errorCode, std::string whatArg)
: m_errorCode(utility::details::create_error_code(errorCode)), m_msg(std::move(whatArg))
{
}
#endif
/// <summary>
/// Creates an <c>http_exception</c> with from a error code and category. The message of the error code will be used
/// as the <c>what</c> string message.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="cat">Error category for the code.</param>
http_exception(int errorCode, const std::error_category& cat) : m_errorCode(std::error_code(errorCode, cat))
{
m_msg = m_errorCode.message();
}
/// <summary>
/// Creates an <c>http_exception</c> with from a error code with a category, and a string message.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="whatArg">Error message string.</param>
http_exception(std::error_code errorCode, const utility::string_t& whatArg)
: m_errorCode(std::move(errorCode)), m_msg(utility::conversions::to_utf8string(whatArg))
{
}
#ifdef _WIN32
/// <summary>
/// Creates an <c>http_exception</c> with from a error code with a category, and a string message.
/// </summary>
/// <param name="errorCode">Error code value.</param>
/// <param name="whatArg">Error message string.</param>
http_exception(std::error_code errorCode, std::string whatArg)
: m_errorCode(std::move(errorCode)), m_msg(std::move(whatArg))
{
}
#endif
/// <summary>
/// Gets a string identifying the cause of the exception.
/// </summary>
/// <returns>A null terminated character string.</returns>
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
/// <summary>
/// Retrieves the underlying error code causing this exception.
/// </summary>
/// <returns>A std::error_code.</returns>
const std::error_code& error_code() const { return m_errorCode; }
private:
std::error_code m_errorCode;
std::string m_msg;
};
namespace details
{
/// <summary>
/// Base class for HTTP messages.
/// This class is to store common functionality so it isn't duplicated on
/// both the request and response side.
/// </summary>
class http_msg_base
{
public:
friend class http::client::http_client;
_ASYNCRTIMP http_msg_base();
virtual ~http_msg_base() {}
http::http_version http_version() const { return m_http_version; }
http_headers& headers() { return m_headers; }
_ASYNCRTIMP void set_body(const concurrency::streams::istream& instream, const utf8string& contentType);
_ASYNCRTIMP void set_body(const concurrency::streams::istream& instream, const utf16string& contentType);
_ASYNCRTIMP void set_body(const concurrency::streams::istream& instream,
utility::size64_t contentLength,
const utf8string& contentType);
_ASYNCRTIMP void set_body(const concurrency::streams::istream& instream,
utility::size64_t contentLength,
const utf16string& contentType);
/// <summary>
/// Helper function for extract functions. Parses the Content-Type header and check to make sure it matches,
/// throws an exception if not.
/// </summary>
/// <param name="ignore_content_type">If true ignores the Content-Type header value.</param>
/// <param name="check_content_type">Function to verify additional information on Content-Type.</param>
/// <returns>A string containing the charset, an empty string if no Content-Type header is empty.</returns>
utility::string_t parse_and_check_content_type(
bool ignore_content_type, const std::function<bool(const utility::string_t&)>& check_content_type);
_ASYNCRTIMP utf8string extract_utf8string(bool ignore_content_type = false);
_ASYNCRTIMP utf16string extract_utf16string(bool ignore_content_type = false);
_ASYNCRTIMP utility::string_t extract_string(bool ignore_content_type = false);
_ASYNCRTIMP json::value _extract_json(bool ignore_content_type = false);
_ASYNCRTIMP std::vector<unsigned char> _extract_vector();
virtual _ASYNCRTIMP utility::string_t to_string() const;
/// <summary>
/// Completes this message
/// </summary>
virtual _ASYNCRTIMP void _complete(utility::size64_t bodySize,
const std::exception_ptr& exceptionPtr = std::exception_ptr());
/// <summary>
/// Set the stream through which the message body could be read
/// </summary>
void set_instream(const concurrency::streams::istream& instream) { m_inStream = instream; }
/// <summary>
/// Get the stream through which the message body could be read
/// </summary>
const concurrency::streams::istream& instream() const { return m_inStream; }
/// <summary>
/// Set the stream through which the message body could be written
/// </summary>
void set_outstream(const concurrency::streams::ostream& outstream, bool is_default)
{
m_outStream = outstream;
m_default_outstream = is_default;
}
/// <summary>
/// Get the stream through which the message body could be written
/// </summary>
const concurrency::streams::ostream& outstream() const { return m_outStream; }
/// <summary>
/// Sets the compressor for the message body
/// </summary>
void set_compressor(std::unique_ptr<http::compression::compress_provider> compressor)
{
m_compressor = std::move(compressor);
}
/// <summary>
/// Gets the compressor for the message body, if any
/// </summary>
std::unique_ptr<http::compression::compress_provider>& compressor() { return m_compressor; }
/// <summary>
/// Sets the collection of factory classes for decompressors for use with the message body
/// </summary>
void set_decompress_factories(const std::vector<std::shared_ptr<http::compression::decompress_factory>>& factories)
{
m_decompressors = factories;
}
/// <summary>
/// Gets the collection of factory classes for decompressors to be used to decompress the message body, if any
/// </summary>
const std::vector<std::shared_ptr<http::compression::decompress_factory>>& decompress_factories()
{
return m_decompressors;
}
const pplx::task_completion_event<utility::size64_t>& _get_data_available() const { return m_data_available; }
/// <summary>
/// Prepare the message with an output stream to receive network data
/// </summary>
_ASYNCRTIMP void _prepare_to_receive_data();
/// <summary>
/// Determine the remaining input stream length
/// </summary>
/// <returns>
/// std::numeric_limits<size_t>::max() if the stream's remaining length cannot be determined
/// length if the stream's remaining length (which may be 0) can be determined
/// </returns>
/// <remarks>
/// This routine should only be called after a msg (request/response) has been
/// completely constructed.
/// </remarks>
_ASYNCRTIMP size_t _get_stream_length();
/// <summary>
/// Determine the content length
/// </summary>
/// <returns>
/// std::numeric_limits<size_t>::max() if there is content with unknown length (transfer_encoding:chunked)
/// 0 if there is no content
/// length if there is content with known length
/// </returns>
/// <remarks>
/// This routine should only be called after a msg (request/response) has been
/// completely constructed.
/// </remarks>
_ASYNCRTIMP size_t _get_content_length();
/// <summary>
/// Determine the content length, and, if necessary, manage compression in the Transfer-Encoding header
/// </summary>
/// <returns>
/// std::numeric_limits<size_t>::max() if there is content with unknown length (transfer_encoding:chunked)
/// 0 if there is no content
/// length if there is content with known length
/// </returns>
/// <remarks>
/// This routine is like _get_content_length, except that it adds a compression algorithm to
/// the Trasfer-Length header if compression is configured. It throws if a Transfer-Encoding
/// header exists and does not match the one it generated.
/// </remarks>
_ASYNCRTIMP size_t _get_content_length_and_set_compression();
void _set_http_version(const http::http_version& http_version) { m_http_version = http_version; }
protected:
std::unique_ptr<http::compression::compress_provider> m_compressor;
std::unique_ptr<http::compression::decompress_provider> m_decompressor;
std::vector<std::shared_ptr<http::compression::decompress_factory>> m_decompressors;
/// <summary>
/// Stream to read the message body.
/// By default this is an invalid stream. The user could set the instream on
/// a request by calling set_request_stream(...). This would also be set when
/// set_body() is called - a stream from the body is constructed and set.
/// Even in the presence of msg body this stream could be invalid. An example
/// would be when the user sets an ostream for the response. With that API the
/// user does not provide the ability to read the msg body.
/// Thus m_instream is valid when there is a msg body and it can actually be read
/// </summary>
concurrency::streams::istream m_inStream;
/// <summary>
/// stream to write the msg body
/// By default this is an invalid stream. The user could set this on the response
/// (for http_client). In all the other cases we would construct one to transfer
/// the data from the network into the message body.
/// </summary>
concurrency::streams::ostream m_outStream;
http::http_version m_http_version;
http_headers m_headers;
bool m_default_outstream;
/// <summary> The TCE is used to signal the availability of the message body. </summary>
pplx::task_completion_event<utility::size64_t> m_data_available;
size_t _get_content_length(bool honor_compression);
};
/// <summary>
/// Base structure for associating internal server information
/// with an HTTP request/response.
/// </summary>
class _http_server_context
{
public:
_http_server_context() {}
virtual ~_http_server_context() {}
private:
};
/// <summary>
/// Internal representation of an HTTP response.
/// </summary>
class _http_response final : public http::details::http_msg_base
{
public:
_http_response() : m_status_code((std::numeric_limits<uint16_t>::max)()) {}
_http_response(http::status_code code) : m_status_code(code) {}
http::status_code status_code() const { return m_status_code; }
void set_status_code(http::status_code code) { m_status_code = code; }
const http::reason_phrase& reason_phrase() const { return m_reason_phrase; }
void set_reason_phrase(const http::reason_phrase& reason) { m_reason_phrase = reason; }
_ASYNCRTIMP utility::string_t to_string() const;
_http_server_context* _get_server_context() const { return m_server_context.get(); }
void _set_server_context(std::unique_ptr<details::_http_server_context> server_context)
{
m_server_context = std::move(server_context);
}
private:
std::unique_ptr<_http_server_context> m_server_context;
http::status_code m_status_code;
http::reason_phrase m_reason_phrase;
};
} // namespace details
/// <summary>
/// Represents an HTTP response.
/// </summary>
class http_response
{
public:
/// <summary>
/// Constructs a response with an empty status code, no headers, and no body.
/// </summary>
/// <returns>A new HTTP response.</returns>
http_response() : _m_impl(std::make_shared<details::_http_response>()) {}
/// <summary>
/// Constructs a response with given status code, no headers, and no body.
/// </summary>
/// <param name="code">HTTP status code to use in response.</param>
/// <returns>A new HTTP response.</returns>
http_response(http::status_code code) : _m_impl(std::make_shared<details::_http_response>(code)) {}
/// <summary>
/// Gets the status code of the response message.
/// </summary>
/// <returns>status code.</returns>
http::status_code status_code() const { return _m_impl->status_code(); }
/// <summary>
/// Sets the status code of the response message.
/// </summary>
/// <param name="code">Status code to set.</param>
/// <remarks>
/// This will overwrite any previously set status code.
/// </remarks>
void set_status_code(http::status_code code) const { _m_impl->set_status_code(code); }
/// <summary>
/// Gets the reason phrase of the response message.
/// If no reason phrase is set it will default to the standard one corresponding to the status code.
/// </summary>
/// <returns>Reason phrase.</returns>
const http::reason_phrase& reason_phrase() const { return _m_impl->reason_phrase(); }
/// <summary>
/// Sets the reason phrase of the response message.
/// If no reason phrase is set it will default to the standard one corresponding to the status code.
/// </summary>
/// <param name="reason">The reason phrase to set.</param>
void set_reason_phrase(const http::reason_phrase& reason) const { _m_impl->set_reason_phrase(reason); }
/// <summary>
/// Gets the headers of the response message.
/// </summary>
/// <returns>HTTP headers for this response.</returns>
/// <remarks>
/// Use the <seealso cref="http_headers::add Method"/> to fill in desired headers.
/// </remarks>
http_headers& headers() { return _m_impl->headers(); }
/// <summary>
/// Gets a const reference to the headers of the response message.
/// </summary>
/// <returns>HTTP headers for this response.</returns>
const http_headers& headers() const { return _m_impl->headers(); }
/// <summary>
/// Generates a string representation of the message, including the body when possible.
/// Mainly this should be used for debugging purposes as it has to copy the
/// message body and doesn't have excellent performance.
/// </summary>
/// <returns>A string representation of this HTTP request.</returns>
/// <remarks>Note this function is synchronous and doesn't wait for the
/// entire message body to arrive. If the message body has arrived by the time this
/// function is called and it is has a textual Content-Type it will be included.
/// Otherwise just the headers will be present.</remarks>
utility::string_t to_string() const { return _m_impl->to_string(); }
/// <summary>
/// Extracts the body of the response message as a string value, checking that the content type is a MIME text type.
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
/// </summary>
/// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes text.</param>
/// <returns>String containing body of the message.</returns>
pplx::task<utility::string_t> extract_string(bool ignore_content_type = false) const
{
auto impl = _m_impl;
return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
return impl->extract_string(ignore_content_type);
});
}
/// <summary>
/// Extracts the body of the response message as a UTF-8 string value, checking that the content type is a MIME text
/// type. A body can only be extracted once because in some cases an optimization is made where the data is 'moved'
/// out.
/// </summary>
/// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes text.</param>
/// <returns>String containing body of the message.</returns>
pplx::task<utf8string> extract_utf8string(bool ignore_content_type = false) const
{
auto impl = _m_impl;
return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
return impl->extract_utf8string(ignore_content_type);
});
}
/// <summary>
/// Extracts the body of the response message as a UTF-16 string value, checking that the content type is a MIME
/// text type. A body can only be extracted once because in some cases an optimization is made where the data is
/// 'moved' out.
/// </summary>
/// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes text.</param>
/// <returns>String containing body of the message.</returns>
pplx::task<utf16string> extract_utf16string(bool ignore_content_type = false) const
{
auto impl = _m_impl;
return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
return impl->extract_utf16string(ignore_content_type);
});
}
/// <summary>
/// Extracts the body of the response message into a json value, checking that the content type is application/json.
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
/// </summary>
/// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes json.</param>
/// <returns>JSON value from the body of this message.</returns>
pplx::task<json::value> extract_json(bool ignore_content_type = false) const
{
auto impl = _m_impl;
return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
return impl->_extract_json(ignore_content_type);
});
}
/// <summary>
/// Extracts the body of the response message into a vector of bytes.
/// </summary>
/// <returns>The body of the message as a vector of bytes.</returns>
pplx::task<std::vector<unsigned char>> extract_vector() const
{
auto impl = _m_impl;
return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) {
return impl->_extract_vector();
});
}
/// <summary>
/// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
/// the character encoding of the string is UTF-8.
/// </summary>
/// <param name="body_text">String containing body text.</param>
/// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
/// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
/// </remarks>
void set_body(utf8string&& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
{
const auto length = body_text.size();
_m_impl->set_body(
concurrency::streams::bytestream::open_istream<std::string>(std::move(body_text)), length, content_type);
}
/// <summary>
/// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
/// the character encoding of the string is UTF-8.
/// </summary>
/// <param name="body_text">String containing body text.</param>
/// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
/// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
/// </remarks>
void set_body(const utf8string& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
{
_m_impl->set_body(
concurrency::streams::bytestream::open_istream<std::string>(body_text), body_text.size(), content_type);
}
/// <summary>
/// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
/// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
/// </summary>
/// <param name="body_text">String containing body text.</param>
/// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain".</param>
/// <remarks>
/// This will overwrite any previously set body data and "Content-Type" header.
/// </remarks>
void set_body(const utf16string& body_text,
utf16string content_type = utility::conversions::to_utf16string("text/plain"))
{
if (content_type.find(::utility::conversions::to_utf16string("charset=")) != content_type.npos)
{
throw std::invalid_argument("content_type can't contain a 'charset'.");
}
auto utf8body = utility::conversions::utf16_to_utf8(body_text);
auto length = utf8body.size();
_m_impl->set_body(concurrency::streams::bytestream::open_istream<std::string>(std::move(utf8body)),
length,
std::move(content_type.append(::utility::conversions::to_utf16string("; charset=utf-8"))));
}
/// <summary>
/// Sets the body of the message to contain json value. If the 'Content-Type'
/// header hasn't already been set it will be set to 'application/json'.
/// </summary>
/// <param name="body_text">json value.</param>
/// <remarks>
/// This will overwrite any previously set body data.
/// </remarks>
void set_body(const json::value& body_data)
{
auto body_text = utility::conversions::to_utf8string(body_data.serialize());
auto length = body_text.size();
set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)),
length,
_XPLATSTR("application/json"));
}
/// <summary>
/// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
/// header hasn't already been set it will be set to 'application/octet-stream'.
/// </summary>
/// <param name="body_data">Vector containing body data.</param>
/// <remarks>
/// This will overwrite any previously set body data.
/// </remarks>
void set_body(std::vector<unsigned char>&& body_data)
{
auto length = body_data.size();
set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)), length);
}
/// <summary>
/// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
/// header hasn't already been set it will be set to 'application/octet-stream'.
/// </summary>
/// <param name="body_data">Vector containing body data.</param>
/// <remarks>
/// This will overwrite any previously set body data.
/// </remarks>
void set_body(const std::vector<unsigned char>& body_data)
{
set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size());
}
/// <summary>
/// Defines a stream that will be relied on to provide the body of the HTTP message when it is
/// sent.
/// </summary>
/// <param name="stream">A readable, open asynchronous stream.</param>
/// <param name="content_type">A string holding the MIME type of the message body.</param>
/// <remarks>
/// This cannot be used in conjunction with any external means of setting the body of the request.
/// The stream will not be read until the message is sent.
/// </remarks>
void set_body(const concurrency::streams::istream& stream,
const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
{
_m_impl->set_body(stream, content_type);
}
/// <summary>
/// Defines a stream that will be relied on to provide the body of the HTTP message when it is
/// sent.
/// </summary>
/// <param name="stream">A readable, open asynchronous stream.</param>
/// <param name="content_length">The size of the data to be sent in the body.</param>
/// <param name="content_type">A string holding the MIME type of the message body.</param>
/// <remarks>
/// This cannot be used in conjunction with any external means of setting the body of the request.
/// The stream will not be read until the message is sent.
/// </remarks>
void set_body(const concurrency::streams::istream& stream,
utility::size64_t content_length,
const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
{
_m_impl->set_body(stream, content_length, content_type);
}
/// <summary>
/// Produces a stream which the caller may use to retrieve data from an incoming request.
/// </summary>
/// <returns>A readable, open asynchronous stream.</returns>
/// <remarks>
/// This cannot be used in conjunction with any other means of getting the body of the request.
/// It is not necessary to wait until the message has been sent before starting to write to the
/// stream, but it is advisable to do so, since it will allow the network I/O to start earlier
/// and the work of sending data can be overlapped with the production of more data.
/// </remarks>
concurrency::streams::istream body() const { return _m_impl->instream(); }
/// <summary>
/// Signals the user (client) when all the data for this response message has been received.
/// </summary>
/// <returns>A <c>task</c> which is completed when all of the response body has been received.</returns>
pplx::task<http::http_response> content_ready() const
{
http_response resp = *this;
return pplx::create_task(_m_impl->_get_data_available()).then([resp](utility::size64_t) mutable {
return resp;
});
}
std::shared_ptr<http::details::_http_response> _get_impl() const { return _m_impl; }
http::details::_http_server_context* _get_server_context() const { return _m_impl->_get_server_context(); }
void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)
{
_m_impl->_set_server_context(std::move(server_context));
}
private:
std::shared_ptr<http::details::_http_response> _m_impl;
};
namespace details
{
/// <summary>
/// Internal representation of an HTTP request message.
/// </summary>
class _http_request final : public http::details::http_msg_base, public std::enable_shared_from_this<_http_request>
{
public:
_ASYNCRTIMP _http_request(http::method mtd);
_ASYNCRTIMP _http_request(std::unique_ptr<http::details::_http_server_context> server_context);
virtual ~_http_request() {}
http::method& method() { return m_method; }
uri& request_uri() { return m_uri; }
_ASYNCRTIMP uri absolute_uri() const;
_ASYNCRTIMP uri relative_uri() const;
_ASYNCRTIMP void set_request_uri(const uri&);
const utility::string_t& remote_address() const { return m_remote_address; }
const pplx::cancellation_token& cancellation_token() const { return m_cancellationToken; }
void set_cancellation_token(const pplx::cancellation_token& token) { m_cancellationToken = token; }
_ASYNCRTIMP utility::string_t to_string() const;
_ASYNCRTIMP pplx::task<void> reply(const http_response& response);
pplx::task<http_response> get_response() { return pplx::task<http_response>(m_response); }
_ASYNCRTIMP pplx::task<void> _reply_if_not_already(http::status_code status);
void set_response_stream(const concurrency::streams::ostream& stream) { m_response_stream = stream; }
void set_progress_handler(const progress_handler& handler)
{
m_progress_handler = std::make_shared<progress_handler>(handler);
}
const concurrency::streams::ostream& _response_stream() const { return m_response_stream; }
const std::shared_ptr<progress_handler>& _progress_handler() const { return m_progress_handler; }
http::details::_http_server_context* _get_server_context() const { return m_server_context.get(); }
void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)
{
m_server_context = std::move(server_context);
}
void _set_listener_path(const utility::string_t& path) { m_listener_path = path; }
void _set_base_uri(const http::uri& base_uri) { m_base_uri = base_uri; }
void _set_remote_address(const utility::string_t& remote_address) { m_remote_address = remote_address; }
private:
// Actual initiates sending the response, without checking if a response has already been sent.
pplx::task<void> _reply_impl(http_response response);
http::method m_method;
// Tracks whether or not a response has already been started for this message.
// 0 = No reply sent
// 1 = Usual reply sent
// 2 = Reply aborted by another thread; e.g. server shutdown
pplx::details::atomic_long m_initiated_response;
std::unique_ptr<http::details::_http_server_context> m_server_context;
pplx::cancellation_token m_cancellationToken;
http::uri m_base_uri;
http::uri m_uri;
utility::string_t m_listener_path;
concurrency::streams::ostream m_response_stream;
std::shared_ptr<progress_handler> m_progress_handler;
pplx::task_completion_event<http_response> m_response;
utility::string_t m_remote_address;
};
} // namespace details
/// <summary>
/// Represents an HTTP request.
/// </summary>
class http_request
{
public:
/// <summary>
/// Constructs a new HTTP request with the 'GET' method.
/// </summary>
http_request() : _m_impl(std::make_shared<http::details::_http_request>(methods::GET)) {}
/// <summary>
/// Constructs a new HTTP request with the given request method.
/// </summary>
/// <param name="mtd">Request method.</param>
http_request(http::method mtd) : _m_impl(std::make_shared<http::details::_http_request>(std::move(mtd))) {}
/// <summary>
/// Destructor frees any held resources.
/// </summary>
~http_request() {}
/// <summary>
/// Get the method (GET/PUT/POST/DELETE) of the request message.
/// </summary>
/// <returns>Request method of this HTTP request.</returns>
const http::method& method() const { return _m_impl->method(); }
/// <summary>
/// Set the method (GET/PUT/POST/DELETE) of the request message.
/// </summary>
/// <param name="method">Request method of this HTTP request.</param>
void set_method(const http::method& method) const { _m_impl->method() = method; }
/// <summary>
/// Get the underling URI of the request message.
/// </summary>
/// <returns>The uri of this message.</returns>
const uri& request_uri() const { return _m_impl->request_uri(); }
/// <summary>
/// Set the underling URI of the request message.
/// </summary>
/// <param name="uri">The uri for this message.</param>
void set_request_uri(const uri& uri) { return _m_impl->set_request_uri(uri); }
/// <summary>
/// Gets a reference the URI path, query, and fragment part of this request message.
/// This will be appended to the base URI specified at construction of the http_client.
/// </summary>
/// <returns>A string.</returns>
/// <remarks>When the request is the one passed to a listener's handler, the
/// relative URI is the request URI less the listener's path. In all other circumstances,
/// request_uri() and relative_uri() will return the same value.
/// </remarks>
uri relative_uri() const { return _m_impl->relative_uri(); }
/// <summary>
/// Get an absolute URI with scheme, host, port, path, query, and fragment part of
/// the request message.
/// </summary>
/// <remarks>Absolute URI is only valid after this http_request object has been passed
/// to http_client::request().
/// </remarks>
uri absolute_uri() const { return _m_impl->absolute_uri(); }
/// <summary>
/// Gets a reference to the headers of the response message.
/// </summary>
/// <returns>HTTP headers for this response.</returns>
/// <remarks>
/// Use the http_headers::add to fill in desired headers.
/// </remarks>
http_headers& headers() { return _m_impl->headers(); }
/// <summary>
/// Gets a const reference to the headers of the response message.
/// </summary>
/// <returns>HTTP headers for this response.</returns>
/// <remarks>
/// Use the http_headers::add to fill in desired headers.
/// </remarks>
const http_headers& headers() const { return _m_impl->headers(); }
/// <summary>
/// Returns the HTTP protocol version of this request message.
/// </summary>
/// <returns>The HTTP protocol version.</returns>
http::http_version http_version() const { return _m_impl->http_version(); }
/// <summary>
/// Returns a string representation of the remote IP address.
/// </summary>
/// <returns>The remote IP address.</returns>
const utility::string_t& remote_address() const { return _m_impl->remote_address(); }
CASABLANCA_DEPRECATED("Use `remote_address()` instead.")
const utility::string_t& get_remote_address() const { return _m_impl->remote_address(); }
/// <summary>
/// Extract the body of the request message as a string value, checking that the content type is a MIME text type.
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
/// </summary>
/// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-8.</param>
/// <returns>String containing body of the message.</returns>
pplx::task<utility::string_t> extract_string(bool ignore_content_type = false)
{
auto impl = _m_impl;
return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
return impl->extract_string(ignore_content_type);
});
}
/// <summary>
/// Extract the body of the request message as a UTF-8 string value, checking that the content type is a MIME text
/// type. A body can only be extracted once because in some cases an optimization is made where the data is 'moved'
/// out.
/// </summary>
/// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-8.</param>
/// <returns>String containing body of the message.</returns>
pplx::task<utf8string> extract_utf8string(bool ignore_content_type = false)
{
auto impl = _m_impl;
return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
return impl->extract_utf8string(ignore_content_type);
});
}
/// <summary>
/// Extract the body of the request message as a UTF-16 string value, checking that the content type is a MIME text
/// type. A body can only be extracted once because in some cases an optimization is made where the data is 'moved'
/// out.
/// </summary>
/// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-16.</param>
/// <returns>String containing body of the message.</returns>
pplx::task<utf16string> extract_utf16string(bool ignore_content_type = false)
{
auto impl = _m_impl;
return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
return impl->extract_utf16string(ignore_content_type);
});
}
/// <summary>
/// Extracts the body of the request message into a json value, checking that the content type is application/json.
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
/// </summary>
/// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-8.</param>
/// <returns>JSON value from the body of this message.</returns>
pplx::task<json::value> extract_json(bool ignore_content_type = false) const
{
auto impl = _m_impl;
return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
return impl->_extract_json(ignore_content_type);
});
}
/// <summary>
/// Extract the body of the response message into a vector of bytes. Extracting a vector can be done on
/// </summary>
/// <returns>The body of the message as a vector of bytes.</returns>
pplx::task<std::vector<unsigned char>> extract_vector() const
{
auto impl = _m_impl;
return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) {
return impl->_extract_vector();
});
}
/// <summary>
/// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
/// the character encoding of the string is UTF-8.
/// </summary>
/// <param name="body_text">String containing body text.</param>
/// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
/// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
/// </remarks>
void set_body(utf8string&& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
{
const auto length = body_text.size();
_m_impl->set_body(
concurrency::streams::bytestream::open_istream<std::string>(std::move(body_text)), length, content_type);
}
/// <summary>
/// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
/// the character encoding of the string is UTF-8.
/// </summary>
/// <param name="body_text">String containing body text.</param>
/// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
/// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
/// </remarks>
void set_body(const utf8string& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
{
_m_impl->set_body(
concurrency::streams::bytestream::open_istream<std::string>(body_text), body_text.size(), content_type);
}
/// <summary>
/// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
/// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
/// </summary>
/// </summary>
/// <param name="body_text">String containing body text.</param>
/// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain".</param>
/// <remarks>
/// This will overwrite any previously set body data and "Content-Type" header.
/// </remarks>
void set_body(const utf16string& body_text,
utf16string content_type = utility::conversions::to_utf16string("text/plain"))
{
if (content_type.find(::utility::conversions::to_utf16string("charset=")) != content_type.npos)
{
throw std::invalid_argument("content_type can't contain a 'charset'.");
}
auto utf8body = utility::conversions::utf16_to_utf8(body_text);
auto length = utf8body.size();
_m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(utf8body)),
length,
std::move(content_type.append(::utility::conversions::to_utf16string("; charset=utf-8"))));
}
/// <summary>
/// Sets the body of the message to contain json value. If the 'Content-Type'
/// header hasn't already been set it will be set to 'application/json'.
/// </summary>
/// <param name="body_data">json value.</param>
/// <remarks>
/// This will overwrite any previously set body data.
/// </remarks>
void set_body(const json::value& body_data)
{
auto body_text = utility::conversions::to_utf8string(body_data.serialize());
auto length = body_text.size();
_m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)),
length,
_XPLATSTR("application/json"));
}
/// <summary>
/// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
/// header hasn't already been set it will be set to 'application/octet-stream'.
/// </summary>
/// <param name="body_data">Vector containing body data.</param>
/// <remarks>
/// This will overwrite any previously set body data.
/// </remarks>
void set_body(std::vector<unsigned char>&& body_data)
{
auto length = body_data.size();
_m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)),
length,
_XPLATSTR("application/octet-stream"));
}
/// <summary>
/// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
/// header hasn't already been set it will be set to 'application/octet-stream'.
/// </summary>
/// <param name="body_data">Vector containing body data.</param>
/// <remarks>
/// This will overwrite any previously set body data.
/// </remarks>
void set_body(const std::vector<unsigned char>& body_data)
{
set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size());
}
/// <summary>
/// Defines a stream that will be relied on to provide the body of the HTTP message when it is
/// sent.
/// </summary>
/// <param name="stream">A readable, open asynchronous stream.</param>
/// <param name="content_type">A string holding the MIME type of the message body.</param>
/// <remarks>
/// This cannot be used in conjunction with any other means of setting the body of the request.
/// The stream will not be read until the message is sent.
/// </remarks>
void set_body(const concurrency::streams::istream& stream,
const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
{
_m_impl->set_body(stream, content_type);
}
/// <summary>
/// Defines a stream that will be relied on to provide the body of the HTTP message when it is
/// sent.
/// </summary>
/// <param name="stream">A readable, open asynchronous stream.</param>
/// <param name="content_length">The size of the data to be sent in the body.</param>
/// <param name="content_type">A string holding the MIME type of the message body.</param>
/// <remarks>
/// This cannot be used in conjunction with any other means of setting the body of the request.
/// The stream will not be read until the message is sent.
/// </remarks>
void set_body(const concurrency::streams::istream& stream,
utility::size64_t content_length,
const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
{
_m_impl->set_body(stream, content_length, content_type);
}
/// <summary>
/// Produces a stream which the caller may use to retrieve data from an incoming request.
/// </summary>
/// <returns>A readable, open asynchronous stream.</returns>
/// <remarks>
/// This cannot be used in conjunction with any other means of getting the body of the request.
/// It is not necessary to wait until the message has been sent before starting to write to the
/// stream, but it is advisable to do so, since it will allow the network I/O to start earlier
/// and the work of sending data can be overlapped with the production of more data.
/// </remarks>
concurrency::streams::istream body() const { return _m_impl->instream(); }
/// <summary>
/// Defines a stream that will be relied on to hold the body of the HTTP response message that
/// results from the request.
/// </summary>
/// <param name="stream">A writable, open asynchronous stream.</param>
/// <remarks>
/// If this function is called, the body of the response should not be accessed in any other
/// way.
/// </remarks>
void set_response_stream(const concurrency::streams::ostream& stream)
{
return _m_impl->set_response_stream(stream);
}
/// <summary>
/// Sets a compressor that will be used to compress the body of the HTTP message as it is sent.
/// </summary>
/// <param name="compressor">A pointer to an instantiated compressor of the desired type.</param>
/// <remarks>
/// This cannot be used in conjunction with any external means of compression. The Transfer-Encoding
/// header will be managed internally, and must not be set by the client.
/// </remarks>
void set_compressor(std::unique_ptr<http::compression::compress_provider> compressor)
{
return _m_impl->set_compressor(std::move(compressor));
}
/// <summary>
/// Sets a compressor that will be used to compress the body of the HTTP message as it is sent.
/// </summary>
/// <param name="algorithm">The built-in compression algorithm to use.</param>
/// <returns>
/// True if a built-in compressor was instantiated, otherwise false.
/// </returns>
/// <remarks>
/// This cannot be used in conjunction with any external means of compression. The Transfer-Encoding
/// header will be managed internally, and must not be set by the client.
/// </remarks>
bool set_compressor(utility::string_t algorithm)
{
_m_impl->set_compressor(http::compression::builtin::make_compressor(algorithm));
return (bool)_m_impl->compressor();
}
/// <summary>
/// Gets the compressor to be used to compress the message body, if any.
/// </summary>
/// <returns>
/// The compressor itself.
/// </returns>
std::unique_ptr<http::compression::compress_provider>& compressor() { return _m_impl->compressor(); }
/// <summary>
/// Sets the default collection of built-in factory classes for decompressors that may be used to
/// decompress the body of the HTTP message as it is received, effectively enabling decompression.
/// </summary>
/// <param name="factories">The collection of factory classes for allowable decompressors. The
/// supplied vector itself need not remain valid after the call returns.</param>
/// <remarks>
/// This default collection is implied if request_compressed_response() is set in the associated
/// <c>client::http_client_config</c> and neither overload of this method has been called.
///
/// This cannot be used in conjunction with any external means of decompression. The TE and Accept-Encoding
/// headers must not be set by the client, as they will be managed internally as appropriate.
/// </remarks>
_ASYNCRTIMP void set_decompress_factories();
/// <summary>
/// Sets a collection of factory classes for decompressors that may be used to decompress the
/// body of the HTTP message as it is received, effectively enabling decompression.
/// </summary>
/// <remarks>
/// If set, this collection takes the place of the built-in compression providers. It may contain
/// custom factory classes and/or factory classes for built-in providers, and may be used to adjust
/// the weights of the built-in providers, which default to 500 (i.e. "q=0.500").
///
/// This cannot be used in conjunction with any external means of decompression. The TE and Accept-Encoding
/// headers must not be set by the client, as they will be managed internally as appropriate.
/// </remarks>
void set_decompress_factories(const std::vector<std::shared_ptr<http::compression::decompress_factory>>& factories)
{
return _m_impl->set_decompress_factories(factories);
}
/// <summary>
/// Gets the collection of factory classes for decompressors to be used to decompress the message body, if any.
/// </summary>
/// <returns>
/// The collection of factory classes itself.
/// </returns>
/// <remarks>
/// This cannot be used in conjunction with any external means of decompression. The TE
/// header must not be set by the client, as it will be managed internally.
/// </remarks>
const std::vector<std::shared_ptr<http::compression::decompress_factory>>& decompress_factories() const
{
return _m_impl->decompress_factories();
}
/// <summary>
/// Defines a callback function that will be invoked for every chunk of data uploaded or downloaded
/// as part of the request.
/// </summary>
/// <param name="handler">A function representing the progress handler. It's parameters are:
/// up: a <c>message_direction::direction</c> value indicating the direction of the message
/// that is being reported.
/// progress: the number of bytes that have been processed so far.
/// </param>
/// <remarks>
/// This function will be called at least once for upload and at least once for
/// the download body, unless there is some exception generated. An HTTP message with an error
/// code is not an exception. This means, that even if there is no body, the progress handler
/// will be called.
///
/// Setting the chunk size on the http_client does not guarantee that the client will be using
/// exactly that increment for uploading and downloading data.
///
/// The handler will be called only once for each combination of argument values, in order. Depending
/// on how a service responds, some download values may come before all upload values have been
/// reported.
///
/// The progress handler will be called on the thread processing the request. This means that
/// the implementation of the handler must take care not to block the thread or do anything
/// that takes significant amounts of time. In particular, do not do any kind of I/O from within
/// the handler, do not update user interfaces, and to not acquire any locks. If such activities
/// are necessary, it is the handler's responsibility to execute that work on a separate thread.
/// </remarks>
void set_progress_handler(const progress_handler& handler) { return _m_impl->set_progress_handler(handler); }
/// <summary>
/// Asynchronously responses to this HTTP request.
/// </summary>
/// <param name="response">Response to send.</param>
/// <returns>An asynchronous operation that is completed once response is sent.</returns>
pplx::task<void> reply(const http_response& response) const { return _m_impl->reply(response); }
/// <summary>
/// Asynchronously responses to this HTTP request.
/// </summary>
/// <param name="status">Response status code.</param>
/// <returns>An asynchronous operation that is completed once response is sent.</returns>
pplx::task<void> reply(http::status_code status) const { return reply(http_response(status)); }
/// <summary>
/// Responds to this HTTP request.
/// </summary>
/// <param name="status">Response status code.</param>
/// <param name="body_data">Json value to use in the response body.</param>
/// <returns>An asynchronous operation that is completed once response is sent.</returns>
pplx::task<void> reply(http::status_code status, const json::value& body_data) const
{
http_response response(status);
response.set_body(body_data);
return reply(response);
}
/// Responds to this HTTP request with a string.
/// Assumes the character encoding of the string is UTF-8.
/// </summary>
/// <param name="status">Response status code.</param>
/// <param name="body_data">UTF-8 string containing the text to use in the response body.</param>
/// <param name="content_type">Content type of the body.</param>
/// <returns>An asynchronous operation that is completed once response is sent.</returns>
/// <remarks>
// Callers of this function do NOT need to block waiting for the response to be
/// sent to before the body data is destroyed or goes out of scope.
/// </remarks>
pplx::task<void> reply(http::status_code status,
utf8string&& body_data,
const utf8string& content_type = "text/plain; charset=utf-8") const
{
http_response response(status);
response.set_body(std::move(body_data), content_type);
return reply(response);
}
/// <summary>
/// Responds to this HTTP request with a string.
/// Assumes the character encoding of the string is UTF-8.
/// </summary>
/// <param name="status">Response status code.</param>
/// <param name="body_data">UTF-8 string containing the text to use in the response body.</param>
/// <param name="content_type">Content type of the body.</param>
/// <returns>An asynchronous operation that is completed once response is sent.</returns>
/// <remarks>
// Callers of this function do NOT need to block waiting for the response to be
/// sent to before the body data is destroyed or goes out of scope.
/// </remarks>
pplx::task<void> reply(http::status_code status,
const utf8string& body_data,
const utf8string& content_type = "text/plain; charset=utf-8") const
{
http_response response(status);
response.set_body(body_data, content_type);
return reply(response);
}
/// <summary>
/// Responds to this HTTP request with a string. Assumes the character encoding
/// of the string is UTF-16 will perform conversion to UTF-8.
/// </summary>
/// <param name="status">Response status code.</param>
/// <param name="body_data">UTF-16 string containing the text to use in the response body.</param>
/// <param name="content_type">Content type of the body.</param>
/// <returns>An asynchronous operation that is completed once response is sent.</returns>
/// <remarks>
// Callers of this function do NOT need to block waiting for the response to be
/// sent to before the body data is destroyed or goes out of scope.
/// </remarks>
pplx::task<void> reply(http::status_code status,
const utf16string& body_data,
const utf16string& content_type = utility::conversions::to_utf16string("text/plain")) const
{
http_response response(status);
response.set_body(body_data, content_type);
return reply(response);
}
/// <summary>
/// Responds to this HTTP request.
/// </summary>
/// <param name="status">Response status code.</param>
/// <param name="content_type">A string holding the MIME type of the message body.</param>
/// <param name="body">An asynchronous stream representing the body data.</param>
/// <returns>A task that is completed once a response from the request is received.</returns>
pplx::task<void> reply(status_code status,
const concurrency::streams::istream& body,
const utility::string_t& content_type = _XPLATSTR("application/octet-stream")) const
{
http_response response(status);
response.set_body(body, content_type);
return reply(response);
}
/// <summary>
/// Responds to this HTTP request.
/// </summary>
/// <param name="status">Response status code.</param>
/// <param name="content_length">The size of the data to be sent in the body..</param>
/// <param name="content_type">A string holding the MIME type of the message body.</param>
/// <param name="body">An asynchronous stream representing the body data.</param>
/// <returns>A task that is completed once a response from the request is received.</returns>
pplx::task<void> reply(status_code status,
const concurrency::streams::istream& body,
utility::size64_t content_length,
const utility::string_t& content_type = _XPLATSTR("application/octet-stream")) const
{
http_response response(status);
response.set_body(body, content_length, content_type);
return reply(response);
}
/// <summary>
/// Signals the user (listener) when all the data for this request message has been received.
/// </summary>
/// <returns>A <c>task</c> which is completed when all of the response body has been received</returns>
pplx::task<http_request> content_ready() const
{
http_request req = *this;
return pplx::create_task(_m_impl->_get_data_available()).then([req](utility::size64_t) mutable { return req; });
}
/// <summary>
/// Gets a task representing the response that will eventually be sent.
/// </summary>
/// <returns>A task that is completed once response is sent.</returns>
pplx::task<http_response> get_response() const { return _m_impl->get_response(); }
/// <summary>
/// Generates a string representation of the message, including the body when possible.
/// Mainly this should be used for debugging purposes as it has to copy the
/// message body and doesn't have excellent performance.
/// </summary>
/// <returns>A string representation of this HTTP request.</returns>
/// <remarks>Note this function is synchronous and doesn't wait for the
/// entire message body to arrive. If the message body has arrived by the time this
/// function is called and it is has a textual Content-Type it will be included.
/// Otherwise just the headers will be present.</remarks>
utility::string_t to_string() const { return _m_impl->to_string(); }
/// <summary>
/// Sends a response if one has not already been sent.
/// </summary>
pplx::task<void> _reply_if_not_already(status_code status) { return _m_impl->_reply_if_not_already(status); }
/// <summary>
/// Gets the server context associated with this HTTP message.
/// </summary>
http::details::_http_server_context* _get_server_context() const { return _m_impl->_get_server_context(); }
/// <summary>
/// These are used for the initial creation of the HTTP request.
/// </summary>
static http_request _create_request(std::unique_ptr<http::details::_http_server_context> server_context)
{
return http_request(std::move(server_context));
}
void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)
{
_m_impl->_set_server_context(std::move(server_context));
}
void _set_listener_path(const utility::string_t& path) { _m_impl->_set_listener_path(path); }
const std::shared_ptr<http::details::_http_request>& _get_impl() const { return _m_impl; }
void _set_cancellation_token(const pplx::cancellation_token& token) { _m_impl->set_cancellation_token(token); }
const pplx::cancellation_token& _cancellation_token() const { return _m_impl->cancellation_token(); }
void _set_base_uri(const http::uri& base_uri) { _m_impl->_set_base_uri(base_uri); }
private:
friend class http::details::_http_request;
friend class http::client::http_client;
http_request(std::unique_ptr<http::details::_http_server_context> server_context)
: _m_impl(std::make_shared<details::_http_request>(std::move(server_context)))
{
}
std::shared_ptr<http::details::_http_request> _m_impl;
};
namespace client
{
class http_pipeline;
}
/// <summary>
/// HTTP client handler class, used to represent an HTTP pipeline stage.
/// </summary>
/// <remarks>
/// When a request goes out, it passes through a series of stages, customizable by
/// the application and/or libraries. The default stage will interact with lower-level
/// communication layers to actually send the message on the network. When creating a client
/// instance, an application may add pipeline stages in front of the already existing
/// stages. Each stage has a reference to the next stage available in the <seealso cref="http_pipeline_stage::next_stage
/// Method"/> value.
/// </remarks>
class http_pipeline_stage : public std::enable_shared_from_this<http_pipeline_stage>
{
public:
http_pipeline_stage() = default;
http_pipeline_stage& operator=(const http_pipeline_stage&) = delete;
http_pipeline_stage(const http_pipeline_stage&) = delete;
virtual ~http_pipeline_stage() = default;
/// <summary>
/// Runs this stage against the given request and passes onto the next stage.
/// </summary>
/// <param name="request">The HTTP request.</param>
/// <returns>A task of the HTTP response.</returns>
virtual pplx::task<http_response> propagate(http_request request) = 0;
protected:
/// <summary>
/// Gets the next stage in the pipeline.
/// </summary>
/// <returns>A shared pointer to a pipeline stage.</returns>
const std::shared_ptr<http_pipeline_stage>& next_stage() const { return m_next_stage; }
/// <summary>
/// Gets a shared pointer to this pipeline stage.
/// </summary>
/// <returns>A shared pointer to a pipeline stage.</returns>
CASABLANCA_DEPRECATED("This api is redundant. Use 'shared_from_this()' directly instead.")
std::shared_ptr<http_pipeline_stage> current_stage() { return this->shared_from_this(); }
private:
friend class ::web::http::client::http_pipeline;
void set_next_stage(const std::shared_ptr<http_pipeline_stage>& next) { m_next_stage = next; }
std::shared_ptr<http_pipeline_stage> m_next_stage;
};
} // namespace http
} // namespace web
/***
* 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
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: JSON parser and writer
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_JSON_H
#define CASA_JSON_H
#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/basic_types.h"
#include <cstdint>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
namespace web
{
/// Library for parsing and serializing JSON values to and from C++ types.
namespace json
{
// Various forward declarations.
namespace details
{
class _Value;
class _Number;
class _Null;
class _Boolean;
class _String;
class _Object;
class _Array;
template<typename CharType>
class JSON_Parser;
} // namespace details
namespace details
{
extern bool g_keep_json_object_unsorted;
}
/// <summary>
/// Preserve the order of the name/value pairs when parsing a JSON object.
/// The default is false, which can yield better performance.
/// </summary>
/// <param name="keep_order"><c>true</c> if ordering should be preserved when parsing, <c>false</c> otherwise.</param>
/// <remarks>Note this is a global setting and affects all JSON parsing done.</remarks>
void _ASYNCRTIMP __cdecl keep_object_element_order(bool keep_order);
#ifdef _WIN32
#ifdef _DEBUG
#define ENABLE_JSON_VALUE_VISUALIZER
#endif
#endif
class number;
class array;
class object;
/// <summary>
/// A JSON value represented as a C++ class.
/// </summary>
class value
{
public:
/// <summary>
/// This enumeration represents the various kinds of JSON values.
/// </summary>
enum value_type
{
/// Number value
Number,
/// Boolean value
Boolean,
/// String value
String,
/// Object value
Object,
/// Array value
Array,
/// Null value
Null
};
/// <summary>
/// Constructor creating a null value
/// </summary>
_ASYNCRTIMP value();
/// <summary>
/// Constructor creating a JSON number value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
_ASYNCRTIMP value(int32_t value);
/// <summary>
/// Constructor creating a JSON number value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
_ASYNCRTIMP value(uint32_t value);
/// <summary>
/// Constructor creating a JSON number value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
_ASYNCRTIMP value(int64_t value);
/// <summary>
/// Constructor creating a JSON number value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
_ASYNCRTIMP value(uint64_t value);
/// <summary>
/// Constructor creating a JSON number value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
_ASYNCRTIMP value(double value);
/// <summary>
/// Constructor creating a JSON Boolean value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
_ASYNCRTIMP explicit value(bool value);
/// <summary>
/// Constructor creating a JSON string value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the platform-native character
/// width</param> <remarks> This constructor has O(n) performance because it tries to determine if specified string
/// has characters that should be properly escaped in JSON. <remarks>
_ASYNCRTIMP explicit value(utility::string_t value);
/// <summary>
/// Constructor creating a JSON string value specifying if the string contains characters to escape
/// </summary>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the platform-native character
/// width</param> <param name="has_escape_chars">Whether <paramref name="value" /> contains characters that should
/// be escaped in JSON value</param> <remarks> This constructor has O(1) performance.
/// </remarks>
_ASYNCRTIMP explicit value(utility::string_t value, bool has_escape_chars);
/// <summary>
/// Constructor creating a JSON string value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the platform-native character
/// width</param> <remarks> <para> This constructor has O(n) performance because it tries to determine if specified
/// string has characters that should be properly escaped in JSON.
/// </para>
/// <para>
/// This constructor exists in order to avoid string literals matching another constructor,
/// as is very likely. For example, conversion to bool does not require a user-defined conversion,
/// and will therefore match first, which means that the JSON value turns up as a boolean.
/// </para>
/// </remarks>
_ASYNCRTIMP explicit value(const utility::char_t* value);
/// <summary>
/// Constructor creating a JSON string value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the platform-native character
/// width</param> <param name="has_escape_chars">Whether <paramref name="value" /> contains characters <remarks>
/// <para>
/// This overload has O(1) performance.
/// </para>
/// <para>
/// This constructor exists in order to avoid string literals matching another constructor,
/// as is very likely. For example, conversion to bool does not require a user-defined conversion,
/// and will therefore match first, which means that the JSON value turns up as a boolean.
/// </para>
/// </remarks>
_ASYNCRTIMP explicit value(const utility::char_t* value, bool has_escape_chars);
/// <summary>
/// Copy constructor
/// </summary>
_ASYNCRTIMP value(const value&);
/// <summary>
/// Move constructor
/// </summary>
_ASYNCRTIMP value(value&&) CPPREST_NOEXCEPT;
/// <summary>
/// Assignment operator.
/// </summary>
/// <returns>The JSON value object that contains the result of the assignment.</returns>
_ASYNCRTIMP value& operator=(const value&);
/// <summary>
/// Move assignment operator.
/// </summary>
/// <returns>The JSON value object that contains the result of the assignment.</returns>
_ASYNCRTIMP value& operator=(value&&) CPPREST_NOEXCEPT;
// Static factories
/// <summary>
/// Creates a null value
/// </summary>
/// <returns>A JSON null value</returns>
static _ASYNCRTIMP value __cdecl null();
/// <summary>
/// Creates a number value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
/// <returns>A JSON number value</returns>
static _ASYNCRTIMP value __cdecl number(double value);
/// <summary>
/// Creates a number value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
/// <returns>A JSON number value</returns>
static _ASYNCRTIMP value __cdecl number(int32_t value);
/// <summary>
/// Creates a number value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
/// <returns>A JSON number value</returns>
static _ASYNCRTIMP value __cdecl number(uint32_t value);
/// <summary>
/// Creates a number value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
/// <returns>A JSON number value</returns>
static _ASYNCRTIMP value __cdecl number(int64_t value);
/// <summary>
/// Creates a number value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
/// <returns>A JSON number value</returns>
static _ASYNCRTIMP value __cdecl number(uint64_t value);
/// <summary>
/// Creates a Boolean value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
/// <returns>A JSON Boolean value</returns>
static _ASYNCRTIMP value __cdecl boolean(bool value);
/// <summary>
/// Creates a string value
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
/// <returns>A JSON string value</returns>
/// <remarks>
/// This overload has O(n) performance because it tries to determine if
/// specified string has characters that should be properly escaped in JSON.
/// </remarks>
static _ASYNCRTIMP value __cdecl string(utility::string_t value);
/// <summary>
/// Creates a string value specifying if the string contains characters to escape
/// </summary>
/// <param name="value">The C++ value to create a JSON value from</param>
/// <param name="has_escape_chars">Whether <paramref name="value" /> contains characters
/// that should be escaped in JSON value</param>
/// <returns>A JSON string value</returns>
/// <remarks>
/// This overload has O(1) performance.
/// </remarks>
static _ASYNCRTIMP value __cdecl string(utility::string_t value, bool has_escape_chars);
#ifdef _WIN32
private:
// Only used internally by JSON parser.
static _ASYNCRTIMP value __cdecl string(const std::string& value);
public:
#endif
/// <summary>
/// Creates an object value
/// </summary>
/// <param name="keep_order">Whether to preserve the original order of the fields</param>
/// <returns>An empty JSON object value</returns>
static _ASYNCRTIMP json::value __cdecl object(bool keep_order = false);
/// <summary>
/// Creates an object value from a collection of field/values
/// </summary>
/// <param name="fields">Field names associated with JSON values</param>
/// <param name="keep_order">Whether to preserve the original order of the fields</param>
/// <returns>A non-empty JSON object value</returns>
static _ASYNCRTIMP json::value __cdecl object(std::vector<std::pair<::utility::string_t, value>> fields,
bool keep_order = false);
/// <summary>
/// Creates an empty JSON array
/// </summary>
/// <returns>An empty JSON array value</returns>
static _ASYNCRTIMP json::value __cdecl array();
/// <summary>
/// Creates a JSON array
/// </summary>
/// <param name="size">The initial number of elements of the JSON value</param>
/// <returns>A JSON array value</returns>
static _ASYNCRTIMP json::value __cdecl array(size_t size);
/// <summary>
/// Creates a JSON array
/// </summary>
/// <param name="elements">A vector of JSON values</param>
/// <returns>A JSON array value</returns>
static _ASYNCRTIMP json::value __cdecl array(std::vector<value> elements);
/// <summary>
/// Accesses the type of JSON value the current value instance is
/// </summary>
/// <returns>The value's type</returns>
_ASYNCRTIMP json::value::value_type type() const;
/// <summary>
/// Is the current value a null value?
/// </summary>
/// <returns><c>true</c> if the value is a null value, <c>false</c> otherwise</returns>
bool is_null() const { return type() == Null; };
/// <summary>
/// Is the current value a number value?
/// </summary>
/// <returns><c>true</c> if the value is a number value, <c>false</c> otherwise</returns>
bool is_number() const { return type() == Number; }
/// <summary>
/// Is the current value represented as an integer number value?
/// </summary>
/// <remarks>
/// Note that if a json value is a number but represented as a double it can still
/// be retrieved as a integer using as_integer(), however the value will be truncated.
/// </remarks>
/// <returns><c>true</c> if the value is an integer value, <c>false</c> otherwise.</returns>
_ASYNCRTIMP bool is_integer() const;
/// <summary>
/// Is the current value represented as an double number value?
/// </summary>
/// <remarks>
/// Note that if a json value is a number but represented as a int it can still
/// be retrieved as a double using as_double().
/// </remarks>
/// <returns><c>true</c> if the value is an double value, <c>false</c> otherwise.</returns>
_ASYNCRTIMP bool is_double() const;
/// <summary>
/// Is the current value a Boolean value?
/// </summary>
/// <returns><c>true</c> if the value is a Boolean value, <c>false</c> otherwise</returns>
bool is_boolean() const { return type() == Boolean; }
/// <summary>
/// Is the current value a string value?
/// </summary>
/// <returns><c>true</c> if the value is a string value, <c>false</c> otherwise</returns>
bool is_string() const { return type() == String; }
/// <summary>
/// Is the current value an array?
/// </summary>
/// <returns><c>true</c> if the value is an array, <c>false</c> otherwise</returns>
bool is_array() const { return type() == Array; }
/// <summary>
/// Is the current value an object?
/// </summary>
/// <returns><c>true</c> if the value is an object, <c>false</c> otherwise</returns>
bool is_object() const { return type() == Object; }
/// <summary>
/// Gets the number of children of the value.
/// </summary>
/// <returns>The number of children. 0 for all non-composites.</returns>
size_t size() const;
/// <summary>
/// Parses a string and construct a JSON value.
/// </summary>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL double-byte string</param>
_ASYNCRTIMP static value __cdecl parse(const utility::string_t& value);
/// <summary>
/// Attempts to parse a string and construct a JSON value.
/// </summary>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL double-byte string</param>
/// <param name="errorCode">If parsing fails, the error code is greater than 0</param>
/// <returns>The parsed object. Returns web::json::value::null if failed</returns>
_ASYNCRTIMP static value __cdecl parse(const utility::string_t& value, std::error_code& errorCode);
/// <summary>
/// Serializes the current JSON value to a C++ string.
/// </summary>
/// <returns>A string representation of the value</returns>
_ASYNCRTIMP utility::string_t serialize() const;
/// <summary>
/// Serializes the current JSON value to a C++ string.
/// </summary>
/// <returns>A string representation of the value</returns>
CASABLANCA_DEPRECATED("This API is deprecated and has been renamed to avoid confusion with as_string(), use "
"::web::json::value::serialize() instead.")
_ASYNCRTIMP utility::string_t to_string() const;
/// <summary>
/// Parses a JSON value from the contents of an input stream using the native platform character width.
/// </summary>
/// <param name="input">The stream to read the JSON value from</param>
/// <returns>The JSON value object created from the input stream.</returns>
_ASYNCRTIMP static value __cdecl parse(utility::istream_t& input);
/// <summary>
/// Parses a JSON value from the contents of an input stream using the native platform character width.
/// </summary>
/// <param name="input">The stream to read the JSON value from</param>
/// <param name="errorCode">If parsing fails, the error code is greater than 0</param>
/// <returns>The parsed object. Returns web::json::value::null if failed</returns>
_ASYNCRTIMP static value __cdecl parse(utility::istream_t& input, std::error_code& errorCode);
/// <summary>
/// Writes the current JSON value to a stream with the native platform character width.
/// </summary>
/// <param name="stream">The stream that the JSON string representation should be written to.</param>
_ASYNCRTIMP void serialize(utility::ostream_t& stream) const;
#ifdef _WIN32
/// <summary>
/// Parses a JSON value from the contents of a single-byte (UTF8) stream.
/// </summary>
/// <param name="stream">The stream to read the JSON value from</param>
_ASYNCRTIMP static value __cdecl parse(std::istream& stream);
/// <summary>
/// Parses a JSON value from the contents of a single-byte (UTF8) stream.
/// </summary>
/// <param name="stream">The stream to read the JSON value from</param>
/// <param name="errorCode">If parsing fails, the error code is greater than 0</param>
/// <returns>The parsed object. Returns web::json::value::null if failed</returns>
_ASYNCRTIMP static value __cdecl parse(std::istream& stream, std::error_code& error);
/// <summary>
/// Serializes the content of the value into a single-byte (UTF8) stream.
/// </summary>
/// <param name="stream">The stream that the JSON string representation should be written to.</param>
_ASYNCRTIMP void serialize(std::ostream& stream) const;
#endif
/// <summary>
/// Converts the JSON value to a C++ double, if and only if it is a number value.
/// Throws <see cref="json_exception"/> if the value is not a number
/// </summary>
/// <returns>A double representation of the value</returns>
_ASYNCRTIMP double as_double() const;
/// <summary>
/// Converts the JSON value to a C++ integer, if and only if it is a number value.
/// Throws <see cref="json_exception"/> if the value is not a number
/// </summary>
/// <returns>An integer representation of the value</returns>
_ASYNCRTIMP int as_integer() const;
/// <summary>
/// Converts the JSON value to a number class, if and only if it is a number value.
/// Throws <see cref="json_exception"/> if the value is not a number
/// </summary>
/// <returns>An instance of number class</returns>
_ASYNCRTIMP const json::number& as_number() const;
/// <summary>
/// Converts the JSON value to a C++ bool, if and only if it is a Boolean value.
/// </summary>
/// <returns>A C++ bool representation of the value</returns>
_ASYNCRTIMP bool as_bool() const;
/// <summary>
/// Converts the JSON value to a json array, if and only if it is an array value.
/// </summary>
/// <remarks>The returned <c>json::array</c> should have the same or shorter lifetime as <c>this</c></remarks>
/// <returns>An array representation of the value</returns>
_ASYNCRTIMP json::array& as_array();
/// <summary>
/// Converts the JSON value to a json array, if and only if it is an array value.
/// </summary>
/// <remarks>The returned <c>json::array</c> should have the same or shorter lifetime as <c>this</c></remarks>
/// <returns>An array representation of the value</returns>
_ASYNCRTIMP const json::array& as_array() const;
/// <summary>
/// Converts the JSON value to a json object, if and only if it is an object value.
/// </summary>
/// <returns>An object representation of the value</returns>
_ASYNCRTIMP json::object& as_object();
/// <summary>
/// Converts the JSON value to a json object, if and only if it is an object value.
/// </summary>
/// <returns>An object representation of the value</returns>
_ASYNCRTIMP const json::object& as_object() const;
/// <summary>
/// Converts the JSON value to a C++ STL string, if and only if it is a string value.
/// </summary>
/// <returns>A C++ STL string representation of the value</returns>
_ASYNCRTIMP const utility::string_t& as_string() const;
/// <summary>
/// Compares two JSON values for equality.
/// </summary>
/// <param name="other">The JSON value to compare with.</param>
/// <returns>True if the values are equal.</returns>
_ASYNCRTIMP bool operator==(const value& other) const;
/// <summary>
/// Compares two JSON values for inequality.
/// </summary>
/// <param name="other">The JSON value to compare with.</param>
/// <returns>True if the values are unequal.</returns>
bool operator!=(const value& other) const { return !((*this) == other); }
/// <summary>
/// Tests for the presence of a field.
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>True if the field exists, false otherwise.</returns>
bool has_field(const utility::string_t& key) const;
/// <summary>
/// Tests for the presence of a number field
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>True if the field exists, false otherwise.</returns>
_ASYNCRTIMP bool has_number_field(const utility::string_t& key) const;
/// <summary>
/// Tests for the presence of an integer field
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>True if the field exists, false otherwise.</returns>
_ASYNCRTIMP bool has_integer_field(const utility::string_t& key) const;
/// <summary>
/// Tests for the presence of a double field
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>True if the field exists, false otherwise.</returns>
_ASYNCRTIMP bool has_double_field(const utility::string_t& key) const;
/// <summary>
/// Tests for the presence of a boolean field
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>True if the field exists, false otherwise.</returns>
_ASYNCRTIMP bool has_boolean_field(const utility::string_t& key) const;
/// <summary>
/// Tests for the presence of a string field
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>True if the field exists, false otherwise.</returns>
_ASYNCRTIMP bool has_string_field(const utility::string_t& key) const;
/// <summary>
/// Tests for the presence of an array field
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>True if the field exists, false otherwise.</returns>
_ASYNCRTIMP bool has_array_field(const utility::string_t& key) const;
/// <summary>
/// Tests for the presence of an object field
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>True if the field exists, false otherwise.</returns>
_ASYNCRTIMP bool has_object_field(const utility::string_t& key) const;
/// <summary>
/// Accesses a field of a JSON object.
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>The value kept in the field; null if the field does not exist</returns>
CASABLANCA_DEPRECATED(
"This API is deprecated and will be removed in a future release, use json::value::at() instead.")
value get(const utility::string_t& key) const;
/// <summary>
/// Erases an element of a JSON array. Throws if index is out of bounds.
/// </summary>
/// <param name="index">The index of the element to erase in the JSON array.</param>
_ASYNCRTIMP void erase(size_t index);
/// <summary>
/// Erases an element of a JSON object. Throws if the key doesn't exist.
/// </summary>
/// <param name="key">The key of the element to erase in the JSON object.</param>
_ASYNCRTIMP void erase(const utility::string_t& key);
/// <summary>
/// Accesses an element of a JSON array. Throws when index out of bounds.
/// </summary>
/// <param name="index">The index of an element in the JSON array.</param>
/// <returns>A reference to the value.</returns>
_ASYNCRTIMP json::value& at(size_t index);
/// <summary>
/// Accesses an element of a JSON array. Throws when index out of bounds.
/// </summary>
/// <param name="index">The index of an element in the JSON array.</param>
/// <returns>A reference to the value.</returns>
_ASYNCRTIMP const json::value& at(size_t index) const;
/// <summary>
/// Accesses an element of a JSON object. If the key doesn't exist, this method throws.
/// </summary>
/// <param name="key">The key of an element in the JSON object.</param>
/// <returns>If the key exists, a reference to the value.</returns>
_ASYNCRTIMP json::value& at(const utility::string_t& key);
/// <summary>
/// Accesses an element of a JSON object. If the key doesn't exist, this method throws.
/// </summary>
/// <param name="key">The key of an element in the JSON object.</param>
/// <returns>If the key exists, a reference to the value.</returns>
_ASYNCRTIMP const json::value& at(const utility::string_t& key) const;
/// <summary>
/// Accesses a field of a JSON object.
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>A reference to the value kept in the field.</returns>
_ASYNCRTIMP value& operator[](const utility::string_t& key);
#ifdef _WIN32
private:
// Only used internally by JSON parser
_ASYNCRTIMP value& operator[](const std::string& key)
{
// JSON object stores its field map as a unordered_map of string_t, so this conversion is hard to avoid
return operator[](utility::conversions::to_string_t(key));
}
public:
#endif
/// <summary>
/// Accesses an element of a JSON array.
/// </summary>
/// <param name="index">The index of an element in the JSON array</param>
/// <returns>The value kept at the array index; null if outside the boundaries of the array</returns>
CASABLANCA_DEPRECATED(
"This API is deprecated and will be removed in a future release, use json::value::at() instead.")
value get(size_t index) const;
/// <summary>
/// Accesses an element of a JSON array.
/// </summary>
/// <param name="index">The index of an element in the JSON array.</param>
/// <returns>A reference to the value kept in the field.</returns>
_ASYNCRTIMP value& operator[](size_t index);
private:
friend class web::json::details::_Object;
friend class web::json::details::_Array;
template<typename CharType>
friend class web::json::details::JSON_Parser;
#ifdef _WIN32
/// <summary>
/// Writes the current JSON value as a double-byte string to a string instance.
/// </summary>
/// <param name="string">The string that the JSON representation should be written to.</param>
_ASYNCRTIMP void format(std::basic_string<utf16char>& string) const;
#endif
/// <summary>
/// Serializes the content of the value into a string instance in UTF8 format
/// </summary>
/// <param name="string">The string that the JSON representation should be written to</param>
_ASYNCRTIMP void format(std::basic_string<char>& string) const;
#ifdef ENABLE_JSON_VALUE_VISUALIZER
explicit value(std::unique_ptr<details::_Value> v, value_type kind) : m_value(std::move(v)), m_kind(kind)
#else
explicit value(std::unique_ptr<details::_Value> v) : m_value(std::move(v))
#endif
{
}
std::unique_ptr<details::_Value> m_value;
#ifdef ENABLE_JSON_VALUE_VISUALIZER
value_type m_kind;
#endif
};
/// <summary>
/// A single exception type to represent errors in parsing, converting, and accessing
/// elements of JSON values.
/// </summary>
class json_exception : public std::exception
{
private:
std::string _message;
public:
json_exception(const char* const message) : _message(message) {}
#ifdef _UTF16_STRINGS
json_exception(const wchar_t* const message) : _message(utility::conversions::utf16_to_utf8(message)) {}
#endif // _UTF16_STRINGS
json_exception(std::string&& message) : _message(std::move(message)) {}
// Must be narrow string because it derives from std::exception
const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); }
};
namespace details
{
enum json_error
{
left_over_character_in_stream = 1,
malformed_array_literal,
malformed_comment,
malformed_literal,
malformed_object_literal,
malformed_numeric_literal,
malformed_string_literal,
malformed_token,
mismatched_brances,
nesting,
unexpected_token
};
class json_error_category_impl : public std::error_category
{
public:
virtual const char* name() const CPPREST_NOEXCEPT override { return "json"; }
virtual std::string message(int ev) const override
{
switch (ev)
{
case json_error::left_over_character_in_stream:
return "Left-over characters in stream after parsing a JSON value";
case json_error::malformed_array_literal: return "Malformed array literal";
case json_error::malformed_comment: return "Malformed comment";
case json_error::malformed_literal: return "Malformed literal";
case json_error::malformed_object_literal: return "Malformed object literal";
case json_error::malformed_numeric_literal: return "Malformed numeric literal";
case json_error::malformed_string_literal: return "Malformed string literal";
case json_error::malformed_token: return "Malformed token";
case json_error::mismatched_brances: return "Mismatched braces";
case json_error::nesting: return "Nesting too deep";
case json_error::unexpected_token: return "Unexpected token";
default: return "Unknown json error";
}
}
};
const json_error_category_impl& json_error_category();
} // namespace details
/// <summary>
/// A JSON array represented as a C++ class.
/// </summary>
class array
{
typedef std::vector<json::value> storage_type;
public:
typedef storage_type::iterator iterator;
typedef storage_type::const_iterator const_iterator;
typedef storage_type::reverse_iterator reverse_iterator;
typedef storage_type::const_reverse_iterator const_reverse_iterator;
typedef storage_type::size_type size_type;
private:
array() : m_elements() {}
array(size_type size) : m_elements(size) {}
array(storage_type elements) : m_elements(std::move(elements)) {}
public:
/// <summary>
/// Gets the beginning iterator element of the array
/// </summary>
/// <returns>An <c>iterator</c> to the beginning of the JSON array.</returns>
iterator begin() { return m_elements.begin(); }
/// <summary>
/// Gets the beginning const iterator element of the array.
/// </summary>
/// <returns>A <c>const_iterator</c> to the beginning of the JSON array.</returns>
const_iterator begin() const { return m_elements.cbegin(); }
/// <summary>
/// Gets the end iterator element of the array
/// </summary>
/// <returns>An <c>iterator</c> to the end of the JSON array.</returns>
iterator end() { return m_elements.end(); }
/// <summary>
/// Gets the end const iterator element of the array.
/// </summary>
/// <returns>A <c>const_iterator</c> to the end of the JSON array.</returns>
const_iterator end() const { return m_elements.cend(); }
/// <summary>
/// Gets the beginning reverse iterator element of the array
/// </summary>
/// <returns>An <c>reverse_iterator</c> to the beginning of the JSON array.</returns>
reverse_iterator rbegin() { return m_elements.rbegin(); }
/// <summary>
/// Gets the beginning const reverse iterator element of the array
/// </summary>
/// <returns>An <c>const_reverse_iterator</c> to the beginning of the JSON array.</returns>
const_reverse_iterator rbegin() const { return m_elements.rbegin(); }
/// <summary>
/// Gets the end reverse iterator element of the array
/// </summary>
/// <returns>An <c>reverse_iterator</c> to the end of the JSON array.</returns>
reverse_iterator rend() { return m_elements.rend(); }
/// <summary>
/// Gets the end const reverse iterator element of the array
/// </summary>
/// <returns>An <c>const_reverse_iterator</c> to the end of the JSON array.</returns>
const_reverse_iterator rend() const { return m_elements.crend(); }
/// <summary>
/// Gets the beginning const iterator element of the array.
/// </summary>
/// <returns>A <c>const_iterator</c> to the beginning of the JSON array.</returns>
const_iterator cbegin() const { return m_elements.cbegin(); }
/// <summary>
/// Gets the end const iterator element of the array.
/// </summary>
/// <returns>A <c>const_iterator</c> to the end of the JSON array.</returns>
const_iterator cend() const { return m_elements.cend(); }
/// <summary>
/// Gets the beginning const reverse iterator element of the array.
/// </summary>
/// <returns>A <c>const_reverse_iterator</c> to the beginning of the JSON array.</returns>
const_reverse_iterator crbegin() const { return m_elements.crbegin(); }
/// <summary>
/// Gets the end const reverse iterator element of the array.
/// </summary>
/// <returns>A <c>const_reverse_iterator</c> to the end of the JSON array.</returns>
const_reverse_iterator crend() const { return m_elements.crend(); }
/// <summary>
/// Deletes an element of the JSON array.
/// </summary>
/// <param name="position">A const_iterator to the element to delete.</param>
/// <returns>Iterator to the new location of the element following the erased element.</returns>
/// <remarks>GCC doesn't support erase with const_iterator on vector yet. In the future this should be
/// changed.</remarks>
iterator erase(iterator position) { return m_elements.erase(position); }
/// <summary>
/// Deletes the element at an index of the JSON array.
/// </summary>
/// <param name="index">The index of the element to delete.</param>
void erase(size_type index)
{
if (index >= m_elements.size())
{
throw json_exception("index out of bounds");
}
m_elements.erase(m_elements.begin() + index);
}
/// <summary>
/// Accesses an element of a JSON array. Throws when index out of bounds.
/// </summary>
/// <param name="index">The index of an element in the JSON array.</param>
/// <returns>A reference to the value kept in the field.</returns>
json::value& at(size_type index)
{
if (index >= m_elements.size()) throw json_exception("index out of bounds");
return m_elements[index];
}
/// <summary>
/// Accesses an element of a JSON array. Throws when index out of bounds.
/// </summary>
/// <param name="index">The index of an element in the JSON array.</param>
/// <returns>A reference to the value kept in the field.</returns>
const json::value& at(size_type index) const
{
if (index >= m_elements.size()) throw json_exception("index out of bounds");
return m_elements[index];
}
/// <summary>
/// Accesses an element of a JSON array.
/// </summary>
/// <param name="index">The index of an element in the JSON array.</param>
/// <returns>A reference to the value kept in the field.</returns>
json::value& operator[](size_type index)
{
msl::safeint3::SafeInt<size_type> nMinSize(index);
nMinSize += 1;
msl::safeint3::SafeInt<size_type> nlastSize(m_elements.size());
if (nlastSize < nMinSize) m_elements.resize(nMinSize);
return m_elements[index];
}
/// <summary>
/// Gets the number of elements of the array.
/// </summary>
/// <returns>The number of elements.</returns>
size_type size() const { return m_elements.size(); }
private:
storage_type m_elements;
friend class details::_Array;
template<typename CharType>
friend class json::details::JSON_Parser;
};
/// <summary>
/// A JSON object represented as a C++ class.
/// </summary>
class object
{
typedef std::vector<std::pair<utility::string_t, json::value>> storage_type;
public:
typedef storage_type::iterator iterator;
typedef storage_type::const_iterator const_iterator;
typedef storage_type::reverse_iterator reverse_iterator;
typedef storage_type::const_reverse_iterator const_reverse_iterator;
typedef storage_type::size_type size_type;
private:
object(bool keep_order = false) : m_elements(), m_keep_order(keep_order) {}
object(storage_type elements, bool keep_order = false) : m_elements(std::move(elements)), m_keep_order(keep_order)
{
if (!keep_order)
{
sort(m_elements.begin(), m_elements.end(), compare_pairs);
}
}
public:
/// <summary>
/// Gets the beginning iterator element of the object
/// </summary>
/// <returns>An <c>iterator</c> to the beginning of the JSON object.</returns>
iterator begin() { return m_elements.begin(); }
/// <summary>
/// Gets the beginning const iterator element of the object.
/// </summary>
/// <returns>A <c>const_iterator</c> to the beginning of the JSON object.</returns>
const_iterator begin() const { return m_elements.cbegin(); }
/// <summary>
/// Gets the end iterator element of the object
/// </summary>
/// <returns>An <c>iterator</c> to the end of the JSON object.</returns>
iterator end() { return m_elements.end(); }
/// <summary>
/// Gets the end const iterator element of the object.
/// </summary>
/// <returns>A <c>const_iterator</c> to the end of the JSON object.</returns>
const_iterator end() const { return m_elements.cend(); }
/// <summary>
/// Gets the beginning reverse iterator element of the object
/// </summary>
/// <returns>An <c>reverse_iterator</c> to the beginning of the JSON object.</returns>
reverse_iterator rbegin() { return m_elements.rbegin(); }
/// <summary>
/// Gets the beginning const reverse iterator element of the object
/// </summary>
/// <returns>An <c>const_reverse_iterator</c> to the beginning of the JSON object.</returns>
const_reverse_iterator rbegin() const { return m_elements.rbegin(); }
/// <summary>
/// Gets the end reverse iterator element of the object
/// </summary>
/// <returns>An <c>reverse_iterator</c> to the end of the JSON object.</returns>
reverse_iterator rend() { return m_elements.rend(); }
/// <summary>
/// Gets the end const reverse iterator element of the object
/// </summary>
/// <returns>An <c>const_reverse_iterator</c> to the end of the JSON object.</returns>
const_reverse_iterator rend() const { return m_elements.crend(); }
/// <summary>
/// Gets the beginning const iterator element of the object.
/// </summary>
/// <returns>A <c>const_iterator</c> to the beginning of the JSON object.</returns>
const_iterator cbegin() const { return m_elements.cbegin(); }
/// <summary>
/// Gets the end const iterator element of the object.
/// </summary>
/// <returns>A <c>const_iterator</c> to the end of the JSON object.</returns>
const_iterator cend() const { return m_elements.cend(); }
/// <summary>
/// Gets the beginning const reverse iterator element of the object.
/// </summary>
/// <returns>A <c>const_reverse_iterator</c> to the beginning of the JSON object.</returns>
const_reverse_iterator crbegin() const { return m_elements.crbegin(); }
/// <summary>
/// Gets the end const reverse iterator element of the object.
/// </summary>
/// <returns>A <c>const_reverse_iterator</c> to the end of the JSON object.</returns>
const_reverse_iterator crend() const { return m_elements.crend(); }
/// <summary>
/// Deletes an element of the JSON object.
/// </summary>
/// <param name="position">A const_iterator to the element to delete.</param>
/// <returns>Iterator to the new location of the element following the erased element.</returns>
/// <remarks>GCC doesn't support erase with const_iterator on vector yet. In the future this should be
/// changed.</remarks>
iterator erase(iterator position) { return m_elements.erase(position); }
/// <summary>
/// Deletes an element of the JSON object. If the key doesn't exist, this method throws.
/// </summary>
/// <param name="key">The key of an element in the JSON object.</param>
void erase(const utility::string_t& key)
{
auto iter = find_by_key(key);
if (iter == m_elements.end())
{
throw web::json::json_exception("Key not found");
}
m_elements.erase(iter);
}
/// <summary>
/// Accesses an element of a JSON object. If the key doesn't exist, this method throws.
/// </summary>
/// <param name="key">The key of an element in the JSON object.</param>
/// <returns>If the key exists, a reference to the value kept in the field.</returns>
json::value& at(const utility::string_t& key)
{
auto iter = find_by_key(key);
if (iter == m_elements.end())
{
throw web::json::json_exception("Key not found");
}
return iter->second;
}
/// <summary>
/// Accesses an element of a JSON object. If the key doesn't exist, this method throws.
/// </summary>
/// <param name="key">The key of an element in the JSON object.</param>
/// <returns>If the key exists, a reference to the value kept in the field.</returns>
const json::value& at(const utility::string_t& key) const
{
auto iter = find_by_key(key);
if (iter == m_elements.end())
{
throw web::json::json_exception("Key not found");
}
return iter->second;
}
/// <summary>
/// Accesses an element of a JSON object.
/// </summary>
/// <param name="key">The key of an element in the JSON object.</param>
/// <returns>If the key exists, a reference to the value kept in the field, otherwise a newly created null value
/// that will be stored for the given key.</returns>
json::value& operator[](const utility::string_t& key)
{
auto iter = find_insert_location(key);
if (iter == m_elements.end() || key != iter->first)
{
return m_elements.insert(iter, std::pair<utility::string_t, value>(key, value()))->second;
}
return iter->second;
}
/// <summary>
/// Gets an iterator to an element of a JSON object.
/// </summary>
/// <param name="key">The key of an element in the JSON object.</param>
/// <returns>A const iterator to the value kept in the field.</returns>
const_iterator find(const utility::string_t& key) const { return find_by_key(key); }
/// <summary>
/// Gets the number of elements of the object.
/// </summary>
/// <returns>The number of elements.</returns>
size_type size() const { return m_elements.size(); }
/// <summary>
/// Checks if there are any elements in the JSON object.
/// </summary>
/// <returns>True if empty.</returns>
bool empty() const { return m_elements.empty(); }
private:
static bool compare_pairs(const std::pair<utility::string_t, value>& p1,
const std::pair<utility::string_t, value>& p2)
{
return p1.first < p2.first;
}
static bool compare_with_key(const std::pair<utility::string_t, value>& p1, const utility::string_t& key)
{
return p1.first < key;
}
storage_type::iterator find_insert_location(const utility::string_t& key)
{
if (m_keep_order)
{
return std::find_if(m_elements.begin(),
m_elements.end(),
[&key](const std::pair<utility::string_t, value>& p) { return p.first == key; });
}
else
{
return std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key);
}
}
storage_type::const_iterator find_by_key(const utility::string_t& key) const
{
if (m_keep_order)
{
return std::find_if(m_elements.begin(),
m_elements.end(),
[&key](const std::pair<utility::string_t, value>& p) { return p.first == key; });
}
else
{
auto iter = std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key);
if (iter != m_elements.end() && key != iter->first)
{
return m_elements.end();
}
return iter;
}
}
storage_type::iterator find_by_key(const utility::string_t& key)
{
auto iter = find_insert_location(key);
if (iter != m_elements.end() && key != iter->first)
{
return m_elements.end();
}
return iter;
}
storage_type m_elements;
bool m_keep_order;
friend class details::_Object;
template<typename CharType>
friend class json::details::JSON_Parser;
};
/// <summary>
/// A JSON number represented as a C++ class.
/// </summary>
class number
{
// Note that these constructors make sure that only negative integers are stored as signed int64 (while others
// convert to unsigned int64). This helps handling number objects e.g. comparing two numbers.
number(double value) : m_value(value), m_type(double_type) {}
number(int32_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {}
number(uint32_t value) : m_intval(value), m_type(unsigned_type) {}
number(int64_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {}
number(uint64_t value) : m_uintval(value), m_type(unsigned_type) {}
public:
/// <summary>
/// Does the number fit into int32?
/// </summary>
/// <returns><c>true</c> if the number fits into int32, <c>false</c> otherwise</returns>
_ASYNCRTIMP bool is_int32() const;
/// <summary>
/// Does the number fit into unsigned int32?
/// </summary>
/// <returns><c>true</c> if the number fits into unsigned int32, <c>false</c> otherwise</returns>
_ASYNCRTIMP bool is_uint32() const;
/// <summary>
/// Does the number fit into int64?
/// </summary>
/// <returns><c>true</c> if the number fits into int64, <c>false</c> otherwise</returns>
_ASYNCRTIMP bool is_int64() const;
/// <summary>
/// Does the number fit into unsigned int64?
/// </summary>
/// <returns><c>true</c> if the number fits into unsigned int64, <c>false</c> otherwise</returns>
bool is_uint64() const
{
switch (m_type)
{
case signed_type: return m_intval >= 0;
case unsigned_type: return true;
case double_type:
default: return false;
}
}
/// <summary>
/// Converts the JSON number to a C++ double.
/// </summary>
/// <returns>A double representation of the number</returns>
double to_double() const
{
switch (m_type)
{
case double_type: return m_value;
case signed_type: return static_cast<double>(m_intval);
case unsigned_type: return static_cast<double>(m_uintval);
default: return false;
}
}
/// <summary>
/// Converts the JSON number to int32.
/// </summary>
/// <returns>An int32 representation of the number</returns>
int32_t to_int32() const
{
if (m_type == double_type)
return static_cast<int32_t>(m_value);
else
return static_cast<int32_t>(m_intval);
}
/// <summary>
/// Converts the JSON number to unsigned int32.
/// </summary>
/// <returns>An unsigned int32 representation of the number</returns>
uint32_t to_uint32() const
{
if (m_type == double_type)
return static_cast<uint32_t>(m_value);
else
return static_cast<uint32_t>(m_intval);
}
/// <summary>
/// Converts the JSON number to int64.
/// </summary>
/// <returns>An int64 representation of the number</returns>
int64_t to_int64() const
{
if (m_type == double_type)
return static_cast<int64_t>(m_value);
else
return static_cast<int64_t>(m_intval);
}
/// <summary>
/// Converts the JSON number to unsigned int64.
/// </summary>
/// <returns>An unsigned int64 representation of the number</returns>
uint64_t to_uint64() const
{
if (m_type == double_type)
return static_cast<uint64_t>(m_value);
else
return static_cast<uint64_t>(m_intval);
}
/// <summary>
/// Is the number represented internally as an integral type?
/// </summary>
/// <returns><c>true</c> if the number is represented as an integral type, <c>false</c> otherwise</returns>
bool is_integral() const { return m_type != double_type; }
/// <summary>
/// Compares two JSON numbers for equality.
/// </summary>
/// <param name="other">The JSON number to compare with.</param>
/// <returns>True if the numbers are equal.</returns>
bool operator==(const number& other) const
{
if (m_type != other.m_type) return false;
switch (m_type)
{
case json::number::type::signed_type: return m_intval == other.m_intval;
case json::number::type::unsigned_type: return m_uintval == other.m_uintval;
case json::number::type::double_type: return m_value == other.m_value;
}
__assume(0);
// Absence of this return statement provokes a warning from Intel
// compiler, but its presence results in a warning from MSVC, so
// we have to resort to conditional compilation to keep both happy.
#ifdef __INTEL_COMPILER
return false;
#endif
}
private:
union {
int64_t m_intval;
uint64_t m_uintval;
double m_value;
};
enum type
{
signed_type = 0,
unsigned_type,
double_type
} m_type;
friend class details::_Number;
};
namespace details
{
class _Value
{
public:
virtual std::unique_ptr<_Value> _copy_value() = 0;
virtual bool has_field(const utility::string_t&) const { return false; }
virtual value get_field(const utility::string_t&) const { throw json_exception("not an object"); }
virtual value get_element(array::size_type) const { throw json_exception("not an array"); }
virtual value& index(const utility::string_t&) { throw json_exception("not an object"); }
virtual value& index(array::size_type) { throw json_exception("not an array"); }
virtual const value& cnst_index(const utility::string_t&) const { throw json_exception("not an object"); }
virtual const value& cnst_index(array::size_type) const { throw json_exception("not an array"); }
// Common function used for serialization to strings and streams.
virtual void serialize_impl(std::string& str) const { format(str); }
#ifdef _WIN32
virtual void serialize_impl(std::wstring& str) const { format(str); }
#endif
virtual utility::string_t to_string() const
{
utility::string_t str;
serialize_impl(str);
return str;
}
virtual json::value::value_type type() const { return json::value::Null; }
virtual bool is_integer() const { throw json_exception("not a number"); }
virtual bool is_double() const { throw json_exception("not a number"); }
virtual const json::number& as_number() { throw json_exception("not a number"); }
virtual double as_double() const { throw json_exception("not a number"); }
virtual int as_integer() const { throw json_exception("not a number"); }
virtual bool as_bool() const { throw json_exception("not a boolean"); }
virtual json::array& as_array() { throw json_exception("not an array"); }
virtual const json::array& as_array() const { throw json_exception("not an array"); }
virtual json::object& as_object() { throw json_exception("not an object"); }
virtual const json::object& as_object() const { throw json_exception("not an object"); }
virtual const utility::string_t& as_string() const { throw json_exception("not a string"); }
virtual size_t size() const { return 0; }
virtual ~_Value() {}
protected:
_Value() {}
virtual void format(std::basic_string<char>& stream) const { stream.append("null"); }
#ifdef _WIN32
virtual void format(std::basic_string<wchar_t>& stream) const { stream.append(L"null"); }
#endif
private:
friend class web::json::value;
};
class _Null : public _Value
{
public:
virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Null>(); }
virtual json::value::value_type type() const { return json::value::Null; }
};
class _Number : public _Value
{
public:
_Number(double value) : m_number(value) {}
_Number(int32_t value) : m_number(value) {}
_Number(uint32_t value) : m_number(value) {}
_Number(int64_t value) : m_number(value) {}
_Number(uint64_t value) : m_number(value) {}
virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Number>(*this); }
virtual json::value::value_type type() const { return json::value::Number; }
virtual bool is_integer() const { return m_number.is_integral(); }
virtual bool is_double() const { return !m_number.is_integral(); }
virtual double as_double() const { return m_number.to_double(); }
virtual int as_integer() const { return m_number.to_int32(); }
virtual const number& as_number() { return m_number; }
protected:
virtual void format(std::basic_string<char>& stream) const;
#ifdef _WIN32
virtual void format(std::basic_string<wchar_t>& stream) const;
#endif
private:
template<typename CharType>
friend class json::details::JSON_Parser;
json::number m_number;
};
class _Boolean : public _Value
{
public:
_Boolean(bool value) : m_value(value) {}
virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Boolean>(*this); }
virtual json::value::value_type type() const { return json::value::Boolean; }
virtual bool as_bool() const { return m_value; }
protected:
virtual void format(std::basic_string<char>& stream) const { stream.append(m_value ? "true" : "false"); }
#ifdef _WIN32
virtual void format(std::basic_string<wchar_t>& stream) const { stream.append(m_value ? L"true" : L"false"); }
#endif
private:
template<typename CharType>
friend class json::details::JSON_Parser;
bool m_value;
};
class _String : public _Value
{
public:
_String(utility::string_t value) : m_string(std::move(value)) { m_has_escape_char = has_escape_chars(*this); }
_String(utility::string_t value, bool escaped_chars) : m_string(std::move(value)), m_has_escape_char(escaped_chars)
{
}
#ifdef _WIN32
_String(std::string&& value) : m_string(utility::conversions::to_utf16string(std::move(value)))
{
m_has_escape_char = has_escape_chars(*this);
}
_String(std::string&& value, bool escape_chars)
: m_string(utility::conversions::to_utf16string(std::move(value))), m_has_escape_char(escape_chars)
{
}
#endif
virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_String>(*this); }
virtual json::value::value_type type() const { return json::value::String; }
virtual const utility::string_t& as_string() const;
virtual void serialize_impl(std::string& str) const { serialize_impl_char_type(str); }
#ifdef _WIN32
virtual void serialize_impl(std::wstring& str) const { serialize_impl_char_type(str); }
#endif
protected:
virtual void format(std::basic_string<char>& str) const;
#ifdef _WIN32
virtual void format(std::basic_string<wchar_t>& str) const;
#endif
private:
friend class _Object;
friend class _Array;
size_t get_reserve_size() const { return m_string.size() + 2; }
template<typename CharType>
void serialize_impl_char_type(std::basic_string<CharType>& str) const
{
// To avoid repeated allocations reserve some space all up front.
// size of string + 2 for quotes
str.reserve(get_reserve_size());
format(str);
}
std::string as_utf8_string() const;
utf16string as_utf16_string() const;
utility::string_t m_string;
// There are significant performance gains that can be made by knowing whether
// or not a character that requires escaping is present.
bool m_has_escape_char;
static bool has_escape_chars(const _String& str);
};
template<typename CharType>
_ASYNCRTIMP void append_escape_string(std::basic_string<CharType>& str, const std::basic_string<CharType>& escaped);
void format_string(const utility::string_t& key, utility::string_t& str);
#ifdef _WIN32
void format_string(const utility::string_t& key, std::string& str);
#endif
class _Object : public _Value
{
public:
_Object(bool keep_order) : m_object(keep_order) {}
_Object(object::storage_type fields, bool keep_order) : m_object(std::move(fields), keep_order) {}
virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Object>(*this); }
virtual json::object& as_object() { return m_object; }
virtual const json::object& as_object() const { return m_object; }
virtual json::value::value_type type() const { return json::value::Object; }
virtual bool has_field(const utility::string_t&) const;
virtual json::value& index(const utility::string_t& key);
bool is_equal(const _Object* other) const
{
if (m_object.size() != other->m_object.size()) return false;
return std::equal(std::begin(m_object), std::end(m_object), std::begin(other->m_object));
}
virtual void serialize_impl(std::string& str) const
{
// To avoid repeated allocations reserve some space all up front.
str.reserve(get_reserve_size());
format(str);
}
#ifdef _WIN32
virtual void serialize_impl(std::wstring& str) const
{
// To avoid repeated allocations reserve some space all up front.
str.reserve(get_reserve_size());
format(str);
}
#endif
size_t size() const { return m_object.size(); }
protected:
virtual void format(std::basic_string<char>& str) const { format_impl(str); }
#ifdef _WIN32
virtual void format(std::basic_string<wchar_t>& str) const { format_impl(str); }
#endif
private:
json::object m_object;
template<typename CharType>
friend class json::details::JSON_Parser;
template<typename CharType>
void format_impl(std::basic_string<CharType>& str) const
{
str.push_back('{');
if (!m_object.empty())
{
auto lastElement = m_object.end() - 1;
for (auto iter = m_object.begin(); iter != lastElement; ++iter)
{
format_string(iter->first, str);
str.push_back(':');
iter->second.format(str);
str.push_back(',');
}
format_string(lastElement->first, str);
str.push_back(':');
lastElement->second.format(str);
}
str.push_back('}');
}
size_t get_reserve_size() const
{
// This is a heuristic we can tune more in the future:
// Basically size of string plus
// sum size of value if an object, array, or string.
size_t reserveSize = 2; // For brackets {}
for (auto iter = m_object.begin(); iter != m_object.end(); ++iter)
{
reserveSize += iter->first.length() + 2; // 2 for quotes
size_t valueSize = iter->second.size() * 20; // Multiply by each object/array element
if (valueSize == 0)
{
if (iter->second.type() == json::value::String)
{
valueSize = static_cast<_String*>(iter->second.m_value.get())->get_reserve_size();
}
else
{
valueSize = 5; // true, false, or null
}
}
reserveSize += valueSize;
}
return reserveSize;
}
};
class _Array : public _Value
{
public:
_Array() {}
_Array(array::size_type size) : m_array(size) {}
_Array(array::storage_type elements) : m_array(std::move(elements)) {}
virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Array>(*this); }
virtual json::value::value_type type() const { return json::value::Array; }
virtual json::array& as_array() { return m_array; }
virtual const json::array& as_array() const { return m_array; }
virtual json::value& index(json::array::size_type index) { return m_array[index]; }
bool is_equal(const _Array* other) const
{
if (m_array.size() != other->m_array.size()) return false;
auto iterT = m_array.cbegin();
auto iterO = other->m_array.cbegin();
auto iterTe = m_array.cend();
auto iterOe = other->m_array.cend();
for (; iterT != iterTe && iterO != iterOe; ++iterT, ++iterO)
{
if (*iterT != *iterO) return false;
}
return true;
}
virtual void serialize_impl(std::string& str) const
{
// To avoid repeated allocations reserve some space all up front.
str.reserve(get_reserve_size());
format(str);
}
#ifdef _WIN32
virtual void serialize_impl(std::wstring& str) const
{
// To avoid repeated allocations reserve some space all up front.
str.reserve(get_reserve_size());
format(str);
}
#endif
size_t size() const { return m_array.size(); }
protected:
virtual void format(std::basic_string<char>& str) const { format_impl(str); }
#ifdef _WIN32
virtual void format(std::basic_string<wchar_t>& str) const { format_impl(str); }
#endif
private:
json::array m_array;
template<typename CharType>
friend class json::details::JSON_Parser;
template<typename CharType>
void format_impl(std::basic_string<CharType>& str) const
{
str.push_back('[');
if (!m_array.m_elements.empty())
{
auto lastElement = m_array.m_elements.end() - 1;
for (auto iter = m_array.m_elements.begin(); iter != lastElement; ++iter)
{
iter->format(str);
str.push_back(',');
}
lastElement->format(str);
}
str.push_back(']');
}
size_t get_reserve_size() const
{
// This is a heuristic we can tune more in the future:
// Basically sum size of each value if an object, array, or string by a multiplier.
size_t reserveSize = 2; // For brackets []
for (auto iter = m_array.cbegin(); iter != m_array.cend(); ++iter)
{
size_t valueSize = iter->size() * 20; // Per each nested array/object
if (valueSize == 0) valueSize = 5; // true, false, or null
reserveSize += valueSize;
}
return reserveSize;
}
};
} // namespace details
/// <summary>
/// Gets the number of children of the value.
/// </summary>
/// <returns>The number of children. 0 for all non-composites.</returns>
inline size_t json::value::size() const { return m_value->size(); }
/// <summary>
/// Test for the presence of a field.
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>True if the field exists, false otherwise.</returns>
inline bool json::value::has_field(const utility::string_t& key) const { return m_value->has_field(key); }
/// <summary>
/// Access a field of a JSON object.
/// </summary>
/// <param name="key">The name of the field</param>
/// <returns>The value kept in the field; null if the field does not exist</returns>
inline json::value json::value::get(const utility::string_t& key) const { return m_value->get_field(key); }
/// <summary>
/// Access an element of a JSON array.
/// </summary>
/// <param name="index">The index of an element in the JSON array</param>
/// <returns>The value kept at the array index; null if outside the boundaries of the array</returns>
inline json::value json::value::get(size_t index) const { return m_value->get_element(index); }
/// <summary>
/// A standard <c>std::ostream</c> operator to facilitate writing JSON values to streams.
/// </summary>
/// <param name="os">The output stream to write the JSON value to.</param>
/// <param name="val">The JSON value to be written to the stream.</param>
/// <returns>The output stream object</returns>
_ASYNCRTIMP utility::ostream_t& __cdecl operator<<(utility::ostream_t& os, const json::value& val);
/// <summary>
/// A standard <c>std::istream</c> operator to facilitate reading JSON values from streams.
/// </summary>
/// <param name="is">The input stream to read the JSON value from.</param>
/// <param name="val">The JSON value object read from the stream.</param>
/// <returns>The input stream object.</returns>
_ASYNCRTIMP utility::istream_t& __cdecl operator>>(utility::istream_t& is, json::value& val);
} // namespace json
} // namespace web
#endif
/***
* 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
/***
* Copyright (C) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: Oauth 2.0
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef CASA_OAUTH2_H
#define CASA_OAUTH2_H
#include "cpprest/details/web_utilities.h"
#include "cpprest/http_msg.h"
namespace web
{
namespace http
{
namespace client
{
// Forward declaration to avoid circular include dependency.
class http_client_config;
} // namespace client
/// oAuth 2.0 library.
namespace oauth2
{
namespace details
{
class oauth2_handler;
// Constant strings for OAuth 2.0.
typedef utility::string_t oauth2_string;
class oauth2_strings
{
public:
#define _OAUTH2_STRINGS
#define DAT(a_, b_) _ASYNCRTIMP static const oauth2_string a_;
#include "cpprest/details/http_constants.dat"
#undef _OAUTH2_STRINGS
#undef DAT
};
} // namespace details
/// oAuth functionality is currently in beta.
namespace experimental
{
/// <summary>
/// Exception type for OAuth 2.0 errors.
/// </summary>
class oauth2_exception : public std::exception
{
public:
oauth2_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {}
~oauth2_exception() CPPREST_NOEXCEPT {}
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
private:
std::string m_msg;
};
/// <summary>
/// OAuth 2.0 token and associated information.
/// </summary>
class oauth2_token
{
public:
/// <summary>
/// Value for undefined expiration time in expires_in().
/// </summary>
enum
{
undefined_expiration = -1
};
oauth2_token(utility::string_t access_token = utility::string_t())
: m_access_token(std::move(access_token)), m_expires_in(undefined_expiration)
{
}
/// <summary>
/// Get access token validity state.
/// If true, access token is a valid.
/// </summary>
/// <returns>Access token validity state.</returns>
bool is_valid_access_token() const { return !access_token().empty(); }
/// <summary>
/// Get access token.
/// </summary>
/// <returns>Access token string.</returns>
const utility::string_t& access_token() const { return m_access_token; }
/// <summary>
/// Set access token.
/// </summary>
/// <param name="access_token">Access token string to set.</param>
void set_access_token(utility::string_t access_token) { m_access_token = std::move(access_token); }
/// <summary>
/// Get refresh token.
/// </summary>
/// <returns>Refresh token string.</returns>
const utility::string_t& refresh_token() const { return m_refresh_token; }
/// <summary>
/// Set refresh token.
/// </summary>
/// <param name="refresh_token">Refresh token string to set.</param>
void set_refresh_token(utility::string_t refresh_token) { m_refresh_token = std::move(refresh_token); }
/// <summary>
/// Get token type.
/// </summary>
/// <returns>Token type string.</returns>
const utility::string_t& token_type() const { return m_token_type; }
/// <summary>
/// Set token type.
/// </summary>
/// <param name="token_type">Token type string to set.</param>
void set_token_type(utility::string_t token_type) { m_token_type = std::move(token_type); }
/// <summary>
/// Get token scope.
/// </summary>
/// <returns>Token scope string.</returns>
const utility::string_t& scope() const { return m_scope; }
/// <summary>
/// Set token scope.
/// </summary>
/// <param name="scope">Token scope string to set.</param>
void set_scope(utility::string_t scope) { m_scope = std::move(scope); }
/// <summary>
/// Get the lifetime of the access token in seconds.
/// For example, 3600 means the access token will expire in one hour from
/// the time when access token response was generated by the authorization server.
/// Value of undefined_expiration means expiration time is either
/// unset or that it was not returned by the server with the access token.
/// </summary>
/// <returns>Lifetime of the access token in seconds or undefined_expiration if not set.</returns>
int64_t expires_in() const { return m_expires_in; }
/// <summary>
/// Set lifetime of access token (in seconds).
/// </summary>
/// <param name="expires_in">Lifetime of access token in seconds.</param>
void set_expires_in(int64_t expires_in) { m_expires_in = expires_in; }
private:
utility::string_t m_access_token;
utility::string_t m_refresh_token;
utility::string_t m_token_type;
utility::string_t m_scope;
int64_t m_expires_in;
};
/// <summary>
/// OAuth 2.0 configuration.
///
/// Encapsulates functionality for:
/// - Authenticating requests with an access token.
/// - Performing the OAuth 2.0 authorization code grant authorization flow.
/// See: http://tools.ietf.org/html/rfc6749#section-4.1
/// - Performing the OAuth 2.0 implicit grant authorization flow.
/// See: http://tools.ietf.org/html/rfc6749#section-4.2
///
/// Performing OAuth 2.0 authorization:
/// 1. Set service and client/app parameters:
/// - Client/app key & secret (as provided by the service).
/// - The service authorization endpoint and token endpoint.
/// - Your client/app redirect URI.
/// - Use set_state() to assign a unique state string for the authorization
/// session (default: "").
/// - If needed, use set_bearer_auth() to control bearer token passing in either
/// query or header (default: header). See: http://tools.ietf.org/html/rfc6750#section-2
/// - If needed, use set_access_token_key() to set "non-standard" access token
/// key (default: "access_token").
/// - If needed, use set_implicit_grant() to enable implicit grant flow.
/// 2. Build authorization URI with build_authorization_uri() and open this in web browser/control.
/// 3. The resource owner should then clicks "Yes" to authorize your client/app, and
/// as a result the web browser/control is redirected to redirect_uri().
/// 5. Capture the redirected URI either in web control or by HTTP listener.
/// 6. Pass the redirected URI to token_from_redirected_uri() to obtain access token.
/// - The method ensures redirected URI contains same state() as set in step 1.
/// - In implicit_grant() is false, this will create HTTP request to fetch access token
/// from the service. Otherwise access token is already included in the redirected URI.
///
/// Usage for issuing authenticated requests:
/// 1. Perform authorization as above to obtain the access token or use an existing token.
/// - Some services provide option to generate access tokens for testing purposes.
/// 2. Pass the resulting oauth2_config with the access token to http_client_config::set_oauth2().
/// 3. Construct http_client with this http_client_config. As a result, all HTTP requests
/// by that client will be OAuth 2.0 authenticated.
///
/// </summary>
class oauth2_config
{
public:
oauth2_config(utility::string_t client_key,
utility::string_t client_secret,
utility::string_t auth_endpoint,
utility::string_t token_endpoint,
utility::string_t redirect_uri,
utility::string_t scope = utility::string_t(),
utility::string_t user_agent = utility::string_t())
: m_client_key(std::move(client_key))
, m_client_secret(std::move(client_secret))
, m_auth_endpoint(std::move(auth_endpoint))
, m_token_endpoint(std::move(token_endpoint))
, m_redirect_uri(std::move(redirect_uri))
, m_scope(std::move(scope))
, m_user_agent(std::move(user_agent))
, m_implicit_grant(false)
, m_bearer_auth(true)
, m_http_basic_auth(true)
, m_access_token_key(details::oauth2_strings::access_token)
{
}
/// <summary>
/// Builds an authorization URI to be loaded in the web browser/view.
/// The URI is built with auth_endpoint() as basis.
/// The implicit_grant() affects the built URI by selecting
/// either authorization code or implicit grant flow.
/// You can set generate_state to generate a new random state string.
/// </summary>
/// <param name="generate_state">If true, a new random state() string is generated
/// which replaces the current state(). If false, state() is unchanged and used as-is.</param>
/// <returns>Authorization URI string.</returns>
_ASYNCRTIMP utility::string_t build_authorization_uri(bool generate_state);
/// <summary>
/// Fetch an access token (and possibly a refresh token) based on redirected URI.
/// Behavior depends on the implicit_grant() setting.
/// If implicit_grant() is false, the URI is parsed for 'code'
/// parameter, and then token_from_code() is called with this code.
/// See: http://tools.ietf.org/html/rfc6749#section-4.1
/// Otherwise, redirect URI fragment part is parsed for 'access_token'
/// parameter, which directly contains the token(s).
/// See: http://tools.ietf.org/html/rfc6749#section-4.2
/// In both cases, the 'state' parameter is parsed and is verified to match state().
/// </summary>
/// <param name="redirected_uri">The URI where web browser/view was redirected after resource owner's
/// authorization.</param> <returns>Task that fetches the token(s) based on redirected URI.</returns>
_ASYNCRTIMP pplx::task<void> token_from_redirected_uri(const web::http::uri& redirected_uri);
/// <summary>
/// Fetches an access token (and possibly a refresh token) from the token endpoint.
/// The task creates an HTTP request to the token_endpoint() which exchanges
/// the authorization code for the token(s).
/// This also sets the refresh token if one was returned.
/// See: http://tools.ietf.org/html/rfc6749#section-4.1.3
/// </summary>
/// <param name="authorization_code">Code received via redirect upon successful authorization.</param>
/// <returns>Task that fetches token(s) based on the authorization code.</returns>
pplx::task<void> token_from_code(utility::string_t authorization_code)
{
uri_builder ub;
ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::authorization_code, false);
ub.append_query(details::oauth2_strings::code, uri::encode_data_string(std::move(authorization_code)), false);
ub.append_query(details::oauth2_strings::redirect_uri, uri::encode_data_string(redirect_uri()), false);
return _request_token(ub);
}
/// <summary>
/// Fetches a new access token (and possibly a new refresh token) using the refresh token.
/// The task creates a HTTP request to the token_endpoint().
/// If successful, resulting access token is set as active via set_token().
/// See: http://tools.ietf.org/html/rfc6749#section-6
/// This also sets a new refresh token if one was returned.
/// </summary>
/// <returns>Task that fetches the token(s) using the refresh token.</returns>
pplx::task<void> token_from_refresh()
{
uri_builder ub;
ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::refresh_token, false);
ub.append_query(
details::oauth2_strings::refresh_token, uri::encode_data_string(token().refresh_token()), false);
return _request_token(ub);
}
/// <summary>
/// Returns enabled state of the configuration.
/// The oauth2_handler will perform OAuth 2.0 authentication only if
/// this method returns true.
/// Return value is true if access token is valid (=fetched or manually set).
/// </summary>
/// <returns>The configuration enabled state.</returns>
bool is_enabled() const { return token().is_valid_access_token(); }
/// <summary>
/// Get client key.
/// </summary>
/// <returns>Client key string.</returns>
const utility::string_t& client_key() const { return m_client_key; }
/// <summary>
/// Set client key.
/// </summary>
/// <param name="client_key">Client key string to set.</param>
void set_client_key(utility::string_t client_key) { m_client_key = std::move(client_key); }
/// <summary>
/// Get client secret.
/// </summary>
/// <returns>Client secret string.</returns>
const utility::string_t& client_secret() const { return m_client_secret; }
/// <summary>
/// Set client secret.
/// </summary>
/// <param name="client_secret">Client secret string to set.</param>
void set_client_secret(utility::string_t client_secret) { m_client_secret = std::move(client_secret); }
/// <summary>
/// Get authorization endpoint URI string.
/// </summary>
/// <returns>Authorization endpoint URI string.</returns>
const utility::string_t& auth_endpoint() const { return m_auth_endpoint; }
/// <summary>
/// Set authorization endpoint URI string.
/// </summary>
/// <param name="auth_endpoint">Authorization endpoint URI string to set.</param>
void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); }
/// <summary>
/// Get token endpoint URI string.
/// </summary>
/// <returns>Token endpoint URI string.</returns>
const utility::string_t& token_endpoint() const { return m_token_endpoint; }
/// <summary>
/// Set token endpoint URI string.
/// </summary>
/// <param name="token_endpoint">Token endpoint URI string to set.</param>
void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); }
/// <summary>
/// Get redirect URI string.
/// </summary>
/// <returns>Redirect URI string.</returns>
const utility::string_t& redirect_uri() const { return m_redirect_uri; }
/// <summary>
/// Set redirect URI string.
/// </summary>
/// <param name="redirect_uri">Redirect URI string to set.</param>
void set_redirect_uri(utility::string_t redirect_uri) { m_redirect_uri = std::move(redirect_uri); }
/// <summary>
/// Get scope used in authorization for token.
/// </summary>
/// <returns>Scope string used in authorization.</returns>
const utility::string_t& scope() const { return m_scope; }
/// <summary>
/// Set scope for authorization for token.
/// </summary>
/// <param name="scope">Scope string for authorization for token.</param>
void set_scope(utility::string_t scope) { m_scope = std::move(scope); }
/// <summary>
/// Get client state string used in authorization.
/// </summary>
/// <returns>Client state string used in authorization.</returns>
const utility::string_t& state() { return m_state; }
/// <summary>
/// Set client state string for authorization for token.
/// The state string is used in authorization for security reasons
/// (to uniquely identify authorization sessions).
/// If desired, suitably secure state string can be automatically generated
/// by build_authorization_uri().
/// A good state string consist of 30 or more random alphanumeric characters.
/// </summary>
/// <param name="state">Client authorization state string to set.</param>
void set_state(utility::string_t state) { m_state = std::move(state); }
/// <summary>
/// Get token.
/// </summary>
/// <returns>Token.</returns>
const oauth2_token& token() const { return m_token; }
/// <summary>
/// Set token.
/// </summary>
/// <param name="token">Token to set.</param>
void set_token(oauth2_token token) { m_token = std::move(token); }
/// <summary>
/// Get implicit grant setting for authorization.
/// </summary>
/// <returns>Implicit grant setting for authorization.</returns>
bool implicit_grant() const { return m_implicit_grant; }
/// <summary>
/// Set implicit grant setting for authorization.
/// False means authorization code grant is used for authorization.
/// True means implicit grant is used.
/// Default: False.
/// </summary>
/// <param name="implicit_grant">The implicit grant setting to set.</param>
void set_implicit_grant(bool implicit_grant) { m_implicit_grant = implicit_grant; }
/// <summary>
/// Get bearer token authentication setting.
/// </summary>
/// <returns>Bearer token authentication setting.</returns>
bool bearer_auth() const { return m_bearer_auth; }
/// <summary>
/// Set bearer token authentication setting.
/// This must be selected based on what the service accepts.
/// True means access token is passed in the request header. (http://tools.ietf.org/html/rfc6750#section-2.1)
/// False means access token in passed in the query parameters. (http://tools.ietf.org/html/rfc6750#section-2.3)
/// Default: True.
/// </summary>
/// <param name="bearer_auth">The bearer token authentication setting to set.</param>
void set_bearer_auth(bool bearer_auth) { m_bearer_auth = bearer_auth; }
/// <summary>
/// Get HTTP Basic authentication setting for token endpoint.
/// </summary>
/// <returns>HTTP Basic authentication setting for token endpoint.</returns>
bool http_basic_auth() const { return m_http_basic_auth; }
/// <summary>
/// Set HTTP Basic authentication setting for token endpoint.
/// This setting must be selected based on what the service accepts.
/// True means HTTP Basic authentication is used for the token endpoint.
/// False means client key & secret are passed in the HTTP request body.
/// Default: True.
/// </summary>
/// <param name="http_basic_auth">The HTTP Basic authentication setting to set.</param>
void set_http_basic_auth(bool http_basic_auth) { m_http_basic_auth = http_basic_auth; }
/// <summary>
/// Get access token key.
/// </summary>
/// <returns>Access token key string.</returns>
const utility::string_t& access_token_key() const { return m_access_token_key; }
/// <summary>
/// Set access token key.
/// If the service requires a "non-standard" key you must set it here.
/// Default: "access_token".
/// </summary>
void set_access_token_key(utility::string_t access_token_key) { m_access_token_key = std::move(access_token_key); }
/// <summary>
/// Get the web proxy object
/// </summary>
/// <returns>A reference to the web proxy object.</returns>
const web_proxy& proxy() const { return m_proxy; }
/// <summary>
/// Set the web proxy object that will be used by token_from_code and token_from_refresh
/// </summary>
/// <param name="proxy">A reference to the web proxy object.</param>
void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
/// <summary>
/// Get user agent to be used in oauth2 flows.
/// </summary>
/// <returns>User agent string.</returns>
const utility::string_t& user_agent() const { return m_user_agent; }
/// <summary>
/// Set user agent to be used in oauth2 flows.
/// If none is provided a default user agent is provided.
/// </summary>
void set_user_agent(utility::string_t user_agent) { m_user_agent = std::move(user_agent); }
private:
friend class web::http::client::http_client_config;
friend class web::http::oauth2::details::oauth2_handler;
oauth2_config() : m_implicit_grant(false), m_bearer_auth(true), m_http_basic_auth(true) {}
_ASYNCRTIMP pplx::task<void> _request_token(uri_builder& request_body);
oauth2_token _parse_token_from_json(const json::value& token_json);
void _authenticate_request(http_request& req) const
{
if (bearer_auth())
{
req.headers().add(header_names::authorization, _XPLATSTR("Bearer ") + token().access_token());
}
else
{
uri_builder ub(req.request_uri());
ub.append_query(access_token_key(), token().access_token());
req.set_request_uri(ub.to_uri());
}
}
utility::string_t m_client_key;
utility::string_t m_client_secret;
utility::string_t m_auth_endpoint;
utility::string_t m_token_endpoint;
utility::string_t m_redirect_uri;
utility::string_t m_scope;
utility::string_t m_state;
utility::string_t m_user_agent;
web::web_proxy m_proxy;
bool m_implicit_grant;
bool m_bearer_auth;
bool m_http_basic_auth;
utility::string_t m_access_token_key;
oauth2_token m_token;
utility::nonce_generator m_state_generator;
};
} // namespace experimental
namespace details
{
class oauth2_handler : public http_pipeline_stage
{
public:
oauth2_handler(std::shared_ptr<experimental::oauth2_config> cfg) : m_config(std::move(cfg)) {}
virtual pplx::task<http_response> propagate(http_request request) override
{
if (m_config)
{
m_config->_authenticate_request(request);
}
return next_stage()->propagate(request);
}
private:
std::shared_ptr<experimental::oauth2_config> m_config;
};
} // namespace details
} // namespace oauth2
} // namespace http
} // namespace web
#endif
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment