1 /***
2  * Copyright (C) Microsoft. All rights reserved.
3  * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4  *
5  * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
6  *
7  * HTTP Library: Client-side APIs.
8  *
9  * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
10  *
11  * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
12  ****/
13 #pragma once
14 
15 #ifndef CASA_HTTP_CLIENT_H
16 #define CASA_HTTP_CLIENT_H
17 
18 #if defined(__cplusplus_winrt)
19 #if !defined(__WRL_NO_DEFAULT_LIB__)
20 #define __WRL_NO_DEFAULT_LIB__
21 #endif
22 #include <msxml6.h>
23 #include <wrl.h>
24 namespace web
25 {
26 namespace http
27 {
28 namespace client
29 {
30 typedef IXMLHTTPRequest2* native_handle;
31 }
32 } // namespace http
33 } // namespace web
34 #else
35 namespace web
36 {
37 namespace http
38 {
39 namespace client
40 {
41 typedef void* native_handle;
42 }
43 } // namespace http
44 } // namespace web
45 #endif // __cplusplus_winrt
46 
47 #include "cpprest/asyncrt_utils.h"
48 #include "cpprest/details/basic_types.h"
49 #include "cpprest/details/web_utilities.h"
50 #include "cpprest/http_msg.h"
51 #include "cpprest/json.h"
52 #include "cpprest/uri.h"
53 #include "pplx/pplxtasks.h"
54 #include <limits>
55 #include <memory>
56 
57 #if _WIN32_WINNT >= _WIN32_WINNT_VISTA
58 #include "cpprest/oauth1.h"
59 #endif
60 
61 #include "cpprest/oauth2.h"
62 
63 #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
64 #if defined(__clang__)
65 #pragma clang diagnostic push
66 #pragma clang diagnostic ignored "-Wconversion"
67 #endif
68 #include "boost/asio/ssl.hpp"
69 #if defined(__clang__)
70 #pragma clang diagnostic pop
71 #endif
72 #endif
73 
74 /// The web namespace contains functionality common to multiple protocols like HTTP and WebSockets.
75 namespace web
76 {
77 /// Declarations and functionality for the HTTP protocol.
78 namespace http
79 {
80 /// HTTP client side library.
81 namespace client
82 {
83 // credentials and web_proxy class has been moved from web::http::client namespace to web namespace.
84 // The below using declarations ensure we don't break existing code.
85 // Please use the web::credentials and web::web_proxy class going forward.
86 using web::credentials;
87 using web::web_proxy;
88 
89 /// <summary>
90 /// HTTP client configuration class, used to set the possible configuration options
91 /// used to create an http_client instance.
92 /// </summary>
93 class http_client_config
94 {
95 public:
http_client_config()96     http_client_config()
97         : m_guarantee_order(false)
98         , m_timeout(std::chrono::seconds(30))
99         , m_chunksize(0)
100         , m_request_compressed(false)
101 #if !defined(__cplusplus_winrt)
102         , m_validate_certificates(true)
103 #endif
104 #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
105         , m_tlsext_sni_enabled(true)
106 #endif
107 #if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
108         , m_buffer_request(false)
109 #endif
110         , m_max_redirects(10)
111         , m_https_to_http_redirects(false)
112     {
113     }
114 
115 #if _WIN32_WINNT >= _WIN32_WINNT_VISTA
116     /// <summary>
117     /// Get OAuth 1.0 configuration.
118     /// </summary>
119     /// <returns>Shared pointer to OAuth 1.0 configuration.</returns>
oauth1()120     const std::shared_ptr<oauth1::experimental::oauth1_config> oauth1() const { return m_oauth1; }
121 
122     /// <summary>
123     /// Set OAuth 1.0 configuration.
124     /// </summary>
125     /// <param name="config">OAuth 1.0 configuration to set.</param>
set_oauth1(oauth1::experimental::oauth1_config config)126     void set_oauth1(oauth1::experimental::oauth1_config config)
127     {
128         m_oauth1 = std::make_shared<oauth1::experimental::oauth1_config>(std::move(config));
129     }
130 #endif
131 
132     /// <summary>
133     /// Get OAuth 2.0 configuration.
134     /// </summary>
135     /// <returns>Shared pointer to OAuth 2.0 configuration.</returns>
oauth2()136     const std::shared_ptr<oauth2::experimental::oauth2_config> oauth2() const { return m_oauth2; }
137 
138     /// <summary>
139     /// Set OAuth 2.0 configuration.
140     /// </summary>
141     /// <param name="config">OAuth 2.0 configuration to set.</param>
set_oauth2(oauth2::experimental::oauth2_config config)142     void set_oauth2(oauth2::experimental::oauth2_config config)
143     {
144         m_oauth2 = std::make_shared<oauth2::experimental::oauth2_config>(std::move(config));
145     }
146 
147     /// <summary>
148     /// Get the web proxy object
149     /// </summary>
150     /// <returns>A reference to the web proxy object.</returns>
proxy()151     const web_proxy& proxy() const { return m_proxy; }
152 
153     /// <summary>
154     /// Set the web proxy object
155     /// </summary>
156     /// <param name="proxy">A reference to the web proxy object.</param>
set_proxy(web_proxy proxy)157     void set_proxy(web_proxy proxy) { m_proxy = std::move(proxy); }
158 
159     /// <summary>
160     /// Get the client credentials
161     /// </summary>
162     /// <returns>A reference to the client credentials.</returns>
credentials()163     const http::client::credentials& credentials() const { return m_credentials; }
164 
165     /// <summary>
166     /// Set the client credentials
167     /// </summary>
168     /// <param name="cred">A reference to the client credentials.</param>
set_credentials(const http::client::credentials & cred)169     void set_credentials(const http::client::credentials& cred) { m_credentials = cred; }
170 
171     /// <summary>
172     /// Get the 'guarantee order' property
173     /// </summary>
174     /// <returns>The value of the property.</returns>
guarantee_order()175     bool guarantee_order() const { return m_guarantee_order; }
176 
177     /// <summary>
178     /// Set the 'guarantee order' property
179     /// </summary>
180     /// <param name="guarantee_order">The value of the property.</param>
181     CASABLANCA_DEPRECATED(
182         "Confusing API will be removed in future releases. If you need to order HTTP requests use task continuations.")
set_guarantee_order(bool guarantee_order)183     void set_guarantee_order(bool guarantee_order) { m_guarantee_order = guarantee_order; }
184 
185     /// <summary>
186     /// Get the timeout
187     /// </summary>
188     /// <returns>The timeout (in seconds) used for each send and receive operation on the client.</returns>
timeout()189     utility::seconds timeout() const { return std::chrono::duration_cast<utility::seconds>(m_timeout); }
190 
191     /// <summary>
192     /// Get the timeout
193     /// </summary>
194     /// <returns>The timeout (in whatever duration) used for each send and receive operation on the client.</returns>
195     template<class T>
timeout()196     T timeout() const
197     {
198         return std::chrono::duration_cast<T>(m_timeout);
199     }
200     /// <summary>
201     /// Set the timeout
202     /// </summary>
203     /// <param name="timeout">The timeout (duration from microseconds range and up) used for each send and receive
204     /// operation on the client.</param>
205     template<class T>
set_timeout(const T & timeout)206     void set_timeout(const T& timeout)
207     {
208         m_timeout = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
209     }
210 
211     /// <summary>
212     /// Get the client chunk size.
213     /// </summary>
214     /// <returns>The internal buffer size used by the http client when sending and receiving data from the
215     /// network.</returns>
chunksize()216     size_t chunksize() const { return m_chunksize == 0 ? 64 * 1024 : m_chunksize; }
217 
218     /// <summary>
219     /// Sets the client chunk size.
220     /// </summary>
221     /// <param name="size">The internal buffer size used by the http client when sending and receiving data from the
222     /// network.</param> <remarks>This is a hint -- an implementation may disregard the setting and use some other chunk
223     /// size.</remarks>
set_chunksize(size_t size)224     void set_chunksize(size_t size) { m_chunksize = size; }
225 
226     /// <summary>
227     /// Returns true if the default chunk size is in use.
228     /// <remarks>If true, implementations are allowed to choose whatever size is best.</remarks>
229     /// </summary>
230     /// <returns>True if default, false if set by user.</returns>
is_default_chunksize()231     bool is_default_chunksize() const { return m_chunksize == 0; }
232 
233     /// <summary>
234     /// Checks if requesting a compressed response using Content-Encoding is turned on, the default is off.
235     /// </summary>
236     /// <returns>True if a content-encoded compressed response is allowed, false otherwise</returns>
request_compressed_response()237     bool request_compressed_response() const { return m_request_compressed; }
238 
239     /// <summary>
240     /// Request that the server respond with a compressed body using Content-Encoding; to use Transfer-Encoding, do not
241     /// set this, and specify a vector of <see cref="web::http::details::compression::decompress_factory" /> pointers
242     /// to the set_decompress_factories method of the <see cref="web::http::http_request" /> object for the request.
243     /// If true and the server does not support compression, this will have no effect.
244     /// The response body is internally decompressed before the consumer receives the data.
245     /// </summary>
246     /// <param name="request_compressed">True to turn on content-encoded response body compression, false
247     /// otherwise.</param> <remarks>Please note there is a performance cost due to copying the request data. Currently
248     /// only supported on Windows and OSX.</remarks>
set_request_compressed_response(bool request_compressed)249     void set_request_compressed_response(bool request_compressed) { m_request_compressed = request_compressed; }
250 
251 #if !defined(__cplusplus_winrt)
252     /// <summary>
253     /// Gets the server certificate validation property.
254     /// </summary>
255     /// <returns>True if certificates are to be verified, false otherwise.</returns>
validate_certificates()256     bool validate_certificates() const { return m_validate_certificates; }
257 
258     /// <summary>
259     /// Sets the server certificate validation property.
260     /// </summary>
261     /// <param name="validate_certs">False to turn ignore all server certificate validation errors, true
262     /// otherwise.</param> <remarks>Note ignoring certificate errors can be dangerous and should be done with
263     /// caution.</remarks>
set_validate_certificates(bool validate_certs)264     void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
265 #endif
266 
267 #if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
268     /// <summary>
269     /// Checks if request data buffering is turned on, the default is off.
270     /// </summary>
271     /// <returns>True if buffering is enabled, false otherwise</returns>
buffer_request()272     bool buffer_request() const { return m_buffer_request; }
273 
274     /// <summary>
275     /// Sets the request buffering property.
276     /// If true, in cases where the request body/stream doesn't support seeking the request data will be buffered.
277     /// This can help in situations where an authentication challenge might be expected.
278     /// </summary>
279     /// <param name="buffer_request">True to turn on buffer, false otherwise.</param>
280     /// <remarks>Please note there is a performance cost due to copying the request data.</remarks>
set_buffer_request(bool buffer_request)281     void set_buffer_request(bool buffer_request) { m_buffer_request = buffer_request; }
282 #endif
283 
284     /// <summary>
285     /// Get the maximum number of redirects to follow automatically.
286     /// A value of 0 indicates that no automatic redirection is performed.
287     /// </summary>
288     /// <returns>The maximum number of redirects to follow automatically.</returns>
289     /// <remarks>This is a hint -- an implementation may enforce a lower value.</remarks>
max_redirects()290     size_t max_redirects() const { return m_max_redirects; }
291 
292     /// <summary>
293     /// Set the maximum number of redirects to follow automatically.
294     /// A value of 0 indicates that no automatic redirection is performed.
295     /// </summary>
296     /// <param name="max_redirects">The maximum number of redirects to follow automatically.</param>
297     /// <remarks>This is a hint -- an implementation may enforce a lower value.</remarks>
set_max_redirects(size_t max_redirects)298     void set_max_redirects(size_t max_redirects) { m_max_redirects = max_redirects; }
299 
300     /// <summary>
301     /// Checks if HTTPS to HTTP redirects are automatically followed.
302     /// </summary>
303     /// <returns>True if HTTPS to HTTP redirects are automatically followed, false otherwise.</returns>
https_to_http_redirects()304     bool https_to_http_redirects() const { return m_https_to_http_redirects; }
305 
306     /// <summary>
307     /// Sets if HTTPS to HTTP redirects are automatically followed.
308     /// </summary>
309     /// <param name="https_to_http_redirects">True if HTTPS to HTTP redirects are to be automatically
310     /// followed, false otherwise.</param>
set_https_to_http_redirects(bool https_to_http_redirects)311     void set_https_to_http_redirects(bool https_to_http_redirects)
312     {
313         m_https_to_http_redirects = https_to_http_redirects;
314     }
315 
316     /// <summary>
317     /// Sets a callback to enable custom setting of platform specific options.
318     /// </summary>
319     /// <remarks>
320     /// The native_handle is the following type depending on the underlying platform:
321     ///     Windows Desktop, WinHTTP - HINTERNET (session)
322     /// </remarks>
323     /// <param name="callback">A user callback allowing for customization of the session</param>
set_nativesessionhandle_options(const std::function<void (native_handle)> & callback)324     void set_nativesessionhandle_options(const std::function<void(native_handle)>& callback)
325     {
326         m_set_user_nativesessionhandle_options = callback;
327     }
328 
329     /// <summary>
330     /// Invokes a user's callback to allow for customization of the session.
331     /// </summary>
332     /// <remarks>Internal Use Only</remarks>
333     /// <param name="handle">A internal implementation handle.</param>
_invoke_nativesessionhandle_options(native_handle handle)334     void _invoke_nativesessionhandle_options(native_handle handle) const
335     {
336         if (m_set_user_nativesessionhandle_options) m_set_user_nativesessionhandle_options(handle);
337     }
338 
339     /// <summary>
340     /// Sets a callback to enable custom setting of platform specific options.
341     /// </summary>
342     /// <remarks>
343     /// The native_handle is the following type depending on the underlying platform:
344     ///     Windows Desktop, WinHTTP - HINTERNET
345     ///     Windows Runtime, WinRT - IXMLHTTPRequest2 *
346     ///     All other platforms, Boost.Asio:
347     ///         https - boost::asio::ssl::stream<boost::asio::ip::tcp::socket &> *
348     ///         http - boost::asio::ip::tcp::socket *
349     /// </remarks>
350     /// <param name="callback">A user callback allowing for customization of the request</param>
set_nativehandle_options(const std::function<void (native_handle)> & callback)351     void set_nativehandle_options(const std::function<void(native_handle)>& callback)
352     {
353         m_set_user_nativehandle_options = callback;
354     }
355 
356     /// <summary>
357     /// Invokes a user's callback to allow for customization of the request.
358     /// </summary>
359     /// <param name="handle">A internal implementation handle.</param>
invoke_nativehandle_options(native_handle handle)360     void invoke_nativehandle_options(native_handle handle) const
361     {
362         if (m_set_user_nativehandle_options) m_set_user_nativehandle_options(handle);
363     }
364 
365 #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
366     /// <summary>
367     /// Sets a callback to enable custom setting of the ssl context, at construction time.
368     /// </summary>
369     /// <param name="callback">A user callback allowing for customization of the ssl context at construction
370     /// time.</param>
set_ssl_context_callback(const std::function<void (boost::asio::ssl::context &)> & callback)371     void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& callback)
372     {
373         m_ssl_context_callback = callback;
374     }
375 
376     /// <summary>
377     /// Gets the user's callback to allow for customization of the ssl context.
378     /// </summary>
get_ssl_context_callback()379     const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
380     {
381         return m_ssl_context_callback;
382     }
383 
384     /// <summary>
385     /// Gets the TLS extension server name indication (SNI) status.
386     /// </summary>
387     /// <returns>True if TLS server name indication is enabled, false otherwise.</returns>
is_tlsext_sni_enabled()388     bool is_tlsext_sni_enabled() const { return m_tlsext_sni_enabled; }
389 
390     /// <summary>
391     /// Sets the TLS extension server name indication (SNI) status.
392     /// </summary>
393     /// <param name="tlsext_sni_enabled">False to disable the TLS (ClientHello) extension for server name indication,
394     /// true otherwise.</param> <remarks>Note: This setting is enabled by default as it is required in most virtual
395     /// hosting scenarios.</remarks>
set_tlsext_sni_enabled(bool tlsext_sni_enabled)396     void set_tlsext_sni_enabled(bool tlsext_sni_enabled) { m_tlsext_sni_enabled = tlsext_sni_enabled; }
397 #endif
398 
399 private:
400 #if _WIN32_WINNT >= _WIN32_WINNT_VISTA
401     std::shared_ptr<oauth1::experimental::oauth1_config> m_oauth1;
402 #endif
403 
404     std::shared_ptr<oauth2::experimental::oauth2_config> m_oauth2;
405     web_proxy m_proxy;
406     http::client::credentials m_credentials;
407     // Whether or not to guarantee ordering, i.e. only using one underlying TCP connection.
408     bool m_guarantee_order;
409 
410     std::chrono::microseconds m_timeout;
411     size_t m_chunksize;
412     bool m_request_compressed;
413 
414 #if !defined(__cplusplus_winrt)
415     // IXmlHttpRequest2 doesn't allow configuration of certificate verification.
416     bool m_validate_certificates;
417 #endif
418 
419     std::function<void(native_handle)> m_set_user_nativehandle_options;
420     std::function<void(native_handle)> m_set_user_nativesessionhandle_options;
421 
422 #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
423     std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
424     bool m_tlsext_sni_enabled;
425 #endif
426 #if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
427     bool m_buffer_request;
428 #endif
429 
430     size_t m_max_redirects;
431     bool m_https_to_http_redirects;
432 };
433 
434 class http_pipeline;
435 
436 /// <summary>
437 /// HTTP client class, used to maintain a connection to an HTTP service for an extended session.
438 /// </summary>
439 class http_client
440 {
441 public:
442     /// <summary>
443     /// Creates a new http_client connected to specified uri.
444     /// </summary>
445     /// <param name="base_uri">A string representation of the base uri to be used for all requests. Must start with
446     /// either "http://" or "https://"</param>
447     _ASYNCRTIMP http_client(const uri& base_uri);
448 
449     /// <summary>
450     /// Creates a new http_client connected to specified uri.
451     /// </summary>
452     /// <param name="base_uri">A string representation of the base uri to be used for all requests. Must start with
453     /// either "http://" or "https://"</param> <param name="client_config">The http client configuration object
454     /// containing the possible configuration options to initialize the <c>http_client</c>. </param>
455     _ASYNCRTIMP http_client(const uri& base_uri, const http_client_config& client_config);
456 
457     /// <summary>
458     /// Note the destructor doesn't necessarily close the connection and release resources.
459     /// The connection is reference counted with the http_responses.
460     /// </summary>
461     _ASYNCRTIMP ~http_client() CPPREST_NOEXCEPT;
462 
463     /// <summary>
464     /// Gets the base URI.
465     /// </summary>
466     /// <returns>
467     /// A base URI initialized in constructor
468     /// </returns>
469     _ASYNCRTIMP const uri& base_uri() const;
470 
471     /// <summary>
472     /// Get client configuration object
473     /// </summary>
474     /// <returns>A reference to the client configuration object.</returns>
475     _ASYNCRTIMP const http_client_config& client_config() const;
476 
477     /// <summary>
478     /// Adds an HTTP pipeline stage to the client.
479     /// </summary>
480     /// <param name="handler">A function object representing the pipeline stage.</param>
481     _ASYNCRTIMP void add_handler(const std::function<pplx::task<http_response> __cdecl(
482                                      http_request, std::shared_ptr<http::http_pipeline_stage>)>& handler);
483 
484     /// <summary>
485     /// Adds an HTTP pipeline stage to the client.
486     /// </summary>
487     /// <param name="stage">A shared pointer to a pipeline stage.</param>
488     _ASYNCRTIMP void add_handler(const std::shared_ptr<http::http_pipeline_stage>& stage);
489 
490     /// <summary>
491     /// Asynchronously sends an HTTP request.
492     /// </summary>
493     /// <param name="request">Request to send.</param>
494     /// <param name="token">Cancellation token for cancellation of this request operation.</param>
495     /// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
496     _ASYNCRTIMP pplx::task<http_response> request(
497         http_request request, const pplx::cancellation_token& token = pplx::cancellation_token::none());
498 
499     /// <summary>
500     /// Asynchronously sends an HTTP request.
501     /// </summary>
502     /// <param name="mtd">HTTP request method.</param>
503     /// <param name="token">Cancellation token for cancellation of this request operation.</param>
504     /// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
505     pplx::task<http_response> request(const method& mtd,
506                                       const pplx::cancellation_token& token = pplx::cancellation_token::none())
507     {
508         http_request msg(mtd);
509         return request(msg, token);
510     }
511 
512     /// <summary>
513     /// Asynchronously sends an HTTP request.
514     /// </summary>
515     /// <param name="mtd">HTTP request method.</param>
516     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
517     /// base URI.</param> <param name="token">Cancellation token for cancellation of this request operation.</param>
518     /// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
519     pplx::task<http_response> request(const method& mtd,
520                                       const utility::string_t& path_query_fragment,
521                                       const pplx::cancellation_token& token = pplx::cancellation_token::none())
522     {
523         http_request msg(mtd);
524         msg.set_request_uri(path_query_fragment);
525         return request(msg, token);
526     }
527 
528     /// <summary>
529     /// Asynchronously sends an HTTP request.
530     /// </summary>
531     /// <param name="mtd">HTTP request method.</param>
532     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
533     /// base URI.</param> <param name="body_data">The data to be used as the message body, represented using the json
534     /// object library.</param> <param name="token">Cancellation token for cancellation of this request
535     /// operation.</param> <returns>An asynchronous operation that is completed once a response from the request is
536     /// received.</returns>
537     pplx::task<http_response> request(const method& mtd,
538                                       const utility::string_t& path_query_fragment,
539                                       const json::value& body_data,
540                                       const pplx::cancellation_token& token = pplx::cancellation_token::none())
541     {
542         http_request msg(mtd);
543         msg.set_request_uri(path_query_fragment);
544         msg.set_body(body_data);
545         return request(msg, token);
546     }
547 
548     /// <summary>
549     /// Asynchronously sends an HTTP request with a string body. Assumes the
550     /// character encoding of the string is UTF-8.
551     /// </summary>
552     /// <param name="mtd">HTTP request method.</param>
553     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
554     /// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
555     /// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
556     /// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
557     /// once a response from the request is received.</returns>
558     pplx::task<http_response> request(const method& mtd,
559                                       const utf8string& path_query_fragment,
560                                       const utf8string& body_data,
561                                       const utf8string& content_type = "text/plain; charset=utf-8",
562                                       const pplx::cancellation_token& token = pplx::cancellation_token::none())
563     {
564         http_request msg(mtd);
565         msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
566         msg.set_body(body_data, content_type);
567         return request(msg, token);
568     }
569 
570     /// <summary>
571     /// Asynchronously sends an HTTP request with a string body. Assumes the
572     /// character encoding of the string is UTF-8.
573     /// </summary>
574     /// <param name="mtd">HTTP request method.</param>
575     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
576     /// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
577     /// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
578     /// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
579     /// once a response from the request is received.</returns>
580     pplx::task<http_response> request(const method& mtd,
581                                       const utf8string& path_query_fragment,
582                                       utf8string&& body_data,
583                                       const utf8string& content_type = "text/plain; charset=utf-8",
584                                       const pplx::cancellation_token& token = pplx::cancellation_token::none())
585     {
586         http_request msg(mtd);
587         msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
588         msg.set_body(std::move(body_data), content_type);
589         return request(msg, token);
590     }
591 
592     /// <summary>
593     /// Asynchronously sends an HTTP request with a string body. Assumes the
594     /// character encoding of the string is UTF-16 will perform conversion to UTF-8.
595     /// </summary>
596     /// <param name="mtd">HTTP request method.</param>
597     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
598     /// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
599     /// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
600     /// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
601     /// once a response from the request is received.</returns>
602     pplx::task<http_response> request(
603         const method& mtd,
604         const utf16string& path_query_fragment,
605         const utf16string& body_data,
606         const utf16string& content_type = utility::conversions::to_utf16string("text/plain"),
607         const pplx::cancellation_token& token = pplx::cancellation_token::none())
608     {
609         http_request msg(mtd);
610         msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
611         msg.set_body(body_data, content_type);
612         return request(msg, token);
613     }
614 
615     /// <summary>
616     /// Asynchronously sends an HTTP request with a string body. Assumes the
617     /// character encoding of the string is UTF-8.
618     /// </summary>
619     /// <param name="mtd">HTTP request method.</param>
620     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
621     /// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
622     /// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
623     /// operation that is completed once a response from the request is received.</returns>
request(const method & mtd,const utf8string & path_query_fragment,const utf8string & body_data,const pplx::cancellation_token & token)624     pplx::task<http_response> request(const method& mtd,
625                                       const utf8string& path_query_fragment,
626                                       const utf8string& body_data,
627                                       const pplx::cancellation_token& token)
628     {
629         return request(mtd, path_query_fragment, body_data, "text/plain; charset=utf-8", token);
630     }
631 
632     /// <summary>
633     /// Asynchronously sends an HTTP request with a string body. Assumes the
634     /// character encoding of the string is UTF-8.
635     /// </summary>
636     /// <param name="mtd">HTTP request method.</param>
637     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
638     /// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
639     /// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
640     /// operation that is completed once a response from the request is received.</returns>
request(const method & mtd,const utf8string & path_query_fragment,utf8string && body_data,const pplx::cancellation_token & token)641     pplx::task<http_response> request(const method& mtd,
642                                       const utf8string& path_query_fragment,
643                                       utf8string&& body_data,
644                                       const pplx::cancellation_token& token)
645     {
646         http_request msg(mtd);
647         msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
648         msg.set_body(std::move(body_data), "text/plain; charset=utf-8");
649         return request(msg, token);
650     }
651 
652     /// <summary>
653     /// Asynchronously sends an HTTP request with a string body. Assumes
654     /// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
655     /// </summary>
656     /// <param name="mtd">HTTP request method.</param>
657     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
658     /// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
659     /// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
660     /// operation that is completed once a response from the request is received.</returns>
request(const method & mtd,const utf16string & path_query_fragment,const utf16string & body_data,const pplx::cancellation_token & token)661     pplx::task<http_response> request(const method& mtd,
662                                       const utf16string& path_query_fragment,
663                                       const utf16string& body_data,
664                                       const pplx::cancellation_token& token)
665     {
666         return request(
667             mtd, path_query_fragment, body_data, ::utility::conversions::to_utf16string("text/plain"), token);
668     }
669 
670 #if !defined(__cplusplus_winrt)
671     /// <summary>
672     /// Asynchronously sends an HTTP request.
673     /// </summary>
674     /// <param name="mtd">HTTP request method.</param>
675     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
676     /// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
677     /// name="content_type">A string holding the MIME type of the message body.</param> <param name="token">Cancellation
678     /// token for cancellation of this request operation.</param> <returns>A task that is completed once a response from
679     /// the request is received.</returns>
680     pplx::task<http_response> request(const method& mtd,
681                                       const utility::string_t& path_query_fragment,
682                                       const concurrency::streams::istream& body,
683                                       const utility::string_t& content_type = _XPLATSTR("application/octet-stream"),
684                                       const pplx::cancellation_token& token = pplx::cancellation_token::none())
685     {
686         http_request msg(mtd);
687         msg.set_request_uri(path_query_fragment);
688         msg.set_body(body, content_type);
689         return request(msg, token);
690     }
691 
692     /// <summary>
693     /// Asynchronously sends an HTTP request.
694     /// </summary>
695     /// <param name="mtd">HTTP request method.</param>
696     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
697     /// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
698     /// name="token">Cancellation token for cancellation of this request operation.</param> <returns>A task that is
699     /// completed once a response from the request is received.</returns>
request(const method & mtd,const utility::string_t & path_query_fragment,const concurrency::streams::istream & body,const pplx::cancellation_token & token)700     pplx::task<http_response> request(const method& mtd,
701                                       const utility::string_t& path_query_fragment,
702                                       const concurrency::streams::istream& body,
703                                       const pplx::cancellation_token& token)
704     {
705         return request(mtd, path_query_fragment, body, _XPLATSTR("application/octet-stream"), token);
706     }
707 #endif // __cplusplus_winrt
708 
709     /// <summary>
710     /// Asynchronously sends an HTTP request.
711     /// </summary>
712     /// <param name="mtd">HTTP request method.</param>
713     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
714     /// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
715     /// name="content_length">Size of the message body.</param> <param name="content_type">A string holding the MIME
716     /// type of the message body.</param> <param name="token">Cancellation token for cancellation of this request
717     /// operation.</param> <returns>A task that is completed once a response from the request is received.</returns>
718     /// <remarks>Winrt requires to provide content_length.</remarks>
719     pplx::task<http_response> request(const method& mtd,
720                                       const utility::string_t& path_query_fragment,
721                                       const concurrency::streams::istream& body,
722                                       size_t content_length,
723                                       const utility::string_t& content_type = _XPLATSTR("application/octet-stream"),
724                                       const pplx::cancellation_token& token = pplx::cancellation_token::none())
725     {
726         http_request msg(mtd);
727         msg.set_request_uri(path_query_fragment);
728         msg.set_body(body, content_length, content_type);
729         return request(msg, token);
730     }
731 
732     /// <summary>
733     /// Asynchronously sends an HTTP request.
734     /// </summary>
735     /// <param name="mtd">HTTP request method.</param>
736     /// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
737     /// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
738     /// name="content_length">Size of the message body.</param> <param name="token">Cancellation token for cancellation
739     /// of this request operation.</param> <returns>A task that is completed once a response from the request is
740     /// received.</returns> <remarks>Winrt requires to provide content_length.</remarks>
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)741     pplx::task<http_response> request(const method& mtd,
742                                       const utility::string_t& path_query_fragment,
743                                       const concurrency::streams::istream& body,
744                                       size_t content_length,
745                                       const pplx::cancellation_token& token)
746     {
747         return request(mtd, path_query_fragment, body, content_length, _XPLATSTR("application/octet-stream"), token);
748     }
749 
750 private:
751     std::shared_ptr<::web::http::client::http_pipeline> m_pipeline;
752 };
753 
754 namespace details
755 {
756 #if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
757 extern const utility::char_t* get_with_body_err_msg;
758 #endif
759 
760 } // namespace details
761 
762 } // namespace client
763 } // namespace http
764 } // namespace web
765 
766 #endif
767