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: Request and reply message definitions.
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 #include "cpprest/asyncrt_utils.h"
16 #include "cpprest/containerstream.h"
17 #include "cpprest/details/cpprest_compat.h"
18 #include "cpprest/http_compression.h"
19 #include "cpprest/http_headers.h"
20 #include "cpprest/json.h"
21 #include "cpprest/streams.h"
22 #include "cpprest/uri.h"
23 #include "pplx/pplxtasks.h"
24 #include <map>
25 #include <memory>
26 #include <string>
27 #include <system_error>
28 #include <vector>
29 
30 namespace web
31 {
32 namespace http
33 {
34 // URI class has been moved from web::http namespace to web namespace.
35 // The below using declarations ensure we don't break existing code.
36 // Please use the web::uri class going forward.
37 using web::uri;
38 using web::uri_builder;
39 
40 namespace client
41 {
42 class http_client;
43 }
44 
45 /// <summary>
46 /// Represents the HTTP protocol version of a message, as {major, minor}.
47 /// </summary>
48 struct http_version
49 {
50     uint8_t major;
51     uint8_t minor;
52 
53     inline bool operator==(const http_version& other) const { return major == other.major && minor == other.minor; }
54     inline bool operator<(const http_version& other) const
55     {
56         return major < other.major || (major == other.major && minor < other.minor);
57     }
58 
59     inline bool operator!=(const http_version& other) const { return !(*this == other); }
60     inline bool operator>=(const http_version& other) const { return !(*this < other); }
61     inline bool operator>(const http_version& other) const { return !(*this < other || *this == other); }
62     inline bool operator<=(const http_version& other) const { return *this < other || *this == other; }
63 
64     /// <summary>
65     /// Creates <c>http_version</c> from an HTTP-Version string, "HTTP" "/" 1*DIGIT "." 1*DIGIT.
66     /// </summary>
67     /// <returns>Returns a <c>http_version</c> of {0, 0} if not successful.</returns>
68     static _ASYNCRTIMP http_version __cdecl from_string(const std::string& http_version_string);
69 
70     /// <summary>
71     /// Returns the string representation of the <c>http_version</c>.
72     /// </summary>
73     _ASYNCRTIMP std::string to_utf8string() const;
74 };
75 
76 /// <summary>
77 /// Predefined HTTP protocol versions.
78 /// </summary>
79 class http_versions
80 {
81 public:
82     _ASYNCRTIMP static const http_version HTTP_0_9;
83     _ASYNCRTIMP static const http_version HTTP_1_0;
84     _ASYNCRTIMP static const http_version HTTP_1_1;
85 };
86 
87 /// <summary>
88 /// Predefined method strings for the standard HTTP methods mentioned in the
89 /// HTTP 1.1 specification.
90 /// </summary>
91 typedef utility::string_t method;
92 
93 /// <summary>
94 /// Common HTTP methods.
95 /// </summary>
96 class methods
97 {
98 public:
99 #define _METHODS
100 #define DAT(a, b) _ASYNCRTIMP const static method a;
101 #include "cpprest/details/http_constants.dat"
102 #undef _METHODS
103 #undef DAT
104 };
105 
106 typedef unsigned short status_code;
107 
108 /// <summary>
109 /// Predefined values for all of the standard HTTP 1.1 response status codes.
110 /// </summary>
111 class status_codes
112 {
113 public:
114 #define _PHRASES
115 #define DAT(a, b, c) const static status_code a = b;
116 #include "cpprest/details/http_constants.dat"
117 #undef _PHRASES
118 #undef DAT
119 };
120 
121 namespace details
122 {
123 /// <summary>
124 /// Constants for MIME types.
125 /// </summary>
126 class mime_types
127 {
128 public:
129 #define _MIME_TYPES
130 #define DAT(a, b) _ASYNCRTIMP const static utility::string_t a;
131 #include "cpprest/details/http_constants.dat"
132 #undef _MIME_TYPES
133 #undef DAT
134 };
135 
136 /// <summary>
137 /// Constants for charset types.
138 /// </summary>
139 class charset_types
140 {
141 public:
142 #define _CHARSET_TYPES
143 #define DAT(a, b) _ASYNCRTIMP const static utility::string_t a;
144 #include "cpprest/details/http_constants.dat"
145 #undef _CHARSET_TYPES
146 #undef DAT
147 };
148 
149 } // namespace details
150 
151 /// Message direction
152 namespace message_direction
153 {
154 /// <summary>
155 /// Enumeration used to denote the direction of a message: a request with a body is
156 /// an upload, a response with a body is a download.
157 /// </summary>
158 enum direction
159 {
160     upload,
161     download
162 };
163 } // namespace message_direction
164 
165 typedef utility::string_t reason_phrase;
166 typedef std::function<void(message_direction::direction, utility::size64_t)> progress_handler;
167 
168 struct http_status_to_phrase
169 {
170     unsigned short id;
171     reason_phrase phrase;
172 };
173 
174 /// <summary>
175 /// Constants for the HTTP headers mentioned in RFC 2616.
176 /// </summary>
177 class header_names
178 {
179 public:
180 #define _HEADER_NAMES
181 #define DAT(a, b) _ASYNCRTIMP const static utility::string_t a;
182 #include "cpprest/details/http_constants.dat"
183 #undef _HEADER_NAMES
184 #undef DAT
185 };
186 
187 /// <summary>
188 /// Represents an HTTP error. This class holds an error message and an optional error code.
189 /// </summary>
190 class http_exception : public std::exception
191 {
192 public:
193     /// <summary>
194     /// Creates an <c>http_exception</c> with just a string message and no error code.
195     /// </summary>
196     /// <param name="whatArg">Error message string.</param>
http_exception(const utility::string_t & whatArg)197     http_exception(const utility::string_t& whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {}
198 
199 #ifdef _WIN32
200     /// <summary>
201     /// Creates an <c>http_exception</c> with just a string message and no error code.
202     /// </summary>
203     /// <param name="whatArg">Error message string.</param>
http_exception(std::string whatArg)204     http_exception(std::string whatArg) : m_msg(std::move(whatArg)) {}
205 #endif
206 
207     /// <summary>
208     /// Creates an <c>http_exception</c> with from a error code using the current platform error category.
209     /// The message of the error code will be used as the what() string message.
210     /// </summary>
211     /// <param name="errorCode">Error code value.</param>
http_exception(int errorCode)212     http_exception(int errorCode) : m_errorCode(utility::details::create_error_code(errorCode))
213     {
214         m_msg = m_errorCode.message();
215     }
216 
217     /// <summary>
218     /// Creates an <c>http_exception</c> with from a error code using the current platform error category.
219     /// </summary>
220     /// <param name="errorCode">Error code value.</param>
221     /// <param name="whatArg">Message to use in what() string.</param>
http_exception(int errorCode,const utility::string_t & whatArg)222     http_exception(int errorCode, const utility::string_t& whatArg)
223         : m_errorCode(utility::details::create_error_code(errorCode))
224         , m_msg(utility::conversions::to_utf8string(whatArg))
225     {
226     }
227 
228 #ifdef _WIN32
229     /// <summary>
230     /// Creates an <c>http_exception</c> with from a error code using the current platform error category.
231     /// </summary>
232     /// <param name="errorCode">Error code value.</param>
233     /// <param name="whatArg">Message to use in what() string.</param>
http_exception(int errorCode,std::string whatArg)234     http_exception(int errorCode, std::string whatArg)
235         : m_errorCode(utility::details::create_error_code(errorCode)), m_msg(std::move(whatArg))
236     {
237     }
238 #endif
239 
240     /// <summary>
241     /// Creates an <c>http_exception</c> with from a error code and category. The message of the error code will be used
242     /// as the <c>what</c> string message.
243     /// </summary>
244     /// <param name="errorCode">Error code value.</param>
245     /// <param name="cat">Error category for the code.</param>
http_exception(int errorCode,const std::error_category & cat)246     http_exception(int errorCode, const std::error_category& cat) : m_errorCode(std::error_code(errorCode, cat))
247     {
248         m_msg = m_errorCode.message();
249     }
250 
251     /// <summary>
252     /// Creates an <c>http_exception</c> with from a error code with a category, and a string message.
253     /// </summary>
254     /// <param name="errorCode">Error code value.</param>
255     /// <param name="whatArg">Error message string.</param>
http_exception(std::error_code errorCode,const utility::string_t & whatArg)256     http_exception(std::error_code errorCode, const utility::string_t& whatArg)
257         : m_errorCode(std::move(errorCode)), m_msg(utility::conversions::to_utf8string(whatArg))
258     {
259     }
260 
261 #ifdef _WIN32
262     /// <summary>
263     /// Creates an <c>http_exception</c> with from a error code with a category, and a string message.
264     /// </summary>
265     /// <param name="errorCode">Error code value.</param>
266     /// <param name="whatArg">Error message string.</param>
http_exception(std::error_code errorCode,std::string whatArg)267     http_exception(std::error_code errorCode, std::string whatArg)
268         : m_errorCode(std::move(errorCode)), m_msg(std::move(whatArg))
269     {
270     }
271 #endif
272 
273     /// <summary>
274     /// Gets a string identifying the cause of the exception.
275     /// </summary>
276     /// <returns>A null terminated character string.</returns>
what()277     const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
278 
279     /// <summary>
280     /// Retrieves the underlying error code causing this exception.
281     /// </summary>
282     /// <returns>A std::error_code.</returns>
error_code()283     const std::error_code& error_code() const { return m_errorCode; }
284 
285 private:
286     std::error_code m_errorCode;
287     std::string m_msg;
288 };
289 
290 namespace details
291 {
292 /// <summary>
293 /// Base class for HTTP messages.
294 /// This class is to store common functionality so it isn't duplicated on
295 /// both the request and response side.
296 /// </summary>
297 class http_msg_base
298 {
299 public:
300     friend class http::client::http_client;
301 
302     _ASYNCRTIMP http_msg_base();
303 
~http_msg_base()304     virtual ~http_msg_base() {}
305 
http_version()306     http::http_version http_version() const { return m_http_version; }
307 
headers()308     http_headers& headers() { return m_headers; }
309 
310     _ASYNCRTIMP void set_body(const concurrency::streams::istream& instream, const utf8string& contentType);
311     _ASYNCRTIMP void set_body(const concurrency::streams::istream& instream, const utf16string& contentType);
312     _ASYNCRTIMP void set_body(const concurrency::streams::istream& instream,
313                               utility::size64_t contentLength,
314                               const utf8string& contentType);
315     _ASYNCRTIMP void set_body(const concurrency::streams::istream& instream,
316                               utility::size64_t contentLength,
317                               const utf16string& contentType);
318 
319     /// <summary>
320     /// Helper function for extract functions. Parses the Content-Type header and check to make sure it matches,
321     /// throws an exception if not.
322     /// </summary>
323     /// <param name="ignore_content_type">If true ignores the Content-Type header value.</param>
324     /// <param name="check_content_type">Function to verify additional information on Content-Type.</param>
325     /// <returns>A string containing the charset, an empty string if no Content-Type header is empty.</returns>
326     utility::string_t parse_and_check_content_type(
327         bool ignore_content_type, const std::function<bool(const utility::string_t&)>& check_content_type);
328 
329     _ASYNCRTIMP utf8string extract_utf8string(bool ignore_content_type = false);
330     _ASYNCRTIMP utf16string extract_utf16string(bool ignore_content_type = false);
331     _ASYNCRTIMP utility::string_t extract_string(bool ignore_content_type = false);
332 
333     _ASYNCRTIMP json::value _extract_json(bool ignore_content_type = false);
334     _ASYNCRTIMP std::vector<unsigned char> _extract_vector();
335 
336     virtual _ASYNCRTIMP utility::string_t to_string() const;
337 
338     /// <summary>
339     /// Completes this message
340     /// </summary>
341     virtual _ASYNCRTIMP void _complete(utility::size64_t bodySize,
342                                        const std::exception_ptr& exceptionPtr = std::exception_ptr());
343 
344     /// <summary>
345     /// Set the stream through which the message body could be read
346     /// </summary>
set_instream(const concurrency::streams::istream & instream)347     void set_instream(const concurrency::streams::istream& instream) { m_inStream = instream; }
348 
349     /// <summary>
350     /// Get the stream through which the message body could be read
351     /// </summary>
instream()352     const concurrency::streams::istream& instream() const { return m_inStream; }
353 
354     /// <summary>
355     /// Set the stream through which the message body could be written
356     /// </summary>
set_outstream(const concurrency::streams::ostream & outstream,bool is_default)357     void set_outstream(const concurrency::streams::ostream& outstream, bool is_default)
358     {
359         m_outStream = outstream;
360         m_default_outstream = is_default;
361     }
362 
363     /// <summary>
364     /// Get the stream through which the message body could be written
365     /// </summary>
outstream()366     const concurrency::streams::ostream& outstream() const { return m_outStream; }
367 
368     /// <summary>
369     /// Sets the compressor for the message body
370     /// </summary>
set_compressor(std::unique_ptr<http::compression::compress_provider> compressor)371     void set_compressor(std::unique_ptr<http::compression::compress_provider> compressor)
372     {
373         m_compressor = std::move(compressor);
374     }
375 
376     /// <summary>
377     /// Gets the compressor for the message body, if any
378     /// </summary>
compressor()379     std::unique_ptr<http::compression::compress_provider>& compressor() { return m_compressor; }
380 
381     /// <summary>
382     /// Sets the collection of factory classes for decompressors for use with the message body
383     /// </summary>
set_decompress_factories(const std::vector<std::shared_ptr<http::compression::decompress_factory>> & factories)384     void set_decompress_factories(const std::vector<std::shared_ptr<http::compression::decompress_factory>>& factories)
385     {
386         m_decompressors = factories;
387     }
388 
389     /// <summary>
390     /// Gets the collection of factory classes for decompressors to be used to decompress the message body, if any
391     /// </summary>
decompress_factories()392     const std::vector<std::shared_ptr<http::compression::decompress_factory>>& decompress_factories()
393     {
394         return m_decompressors;
395     }
396 
_get_data_available()397     const pplx::task_completion_event<utility::size64_t>& _get_data_available() const { return m_data_available; }
398 
399     /// <summary>
400     /// Prepare the message with an output stream to receive network data
401     /// </summary>
402     _ASYNCRTIMP void _prepare_to_receive_data();
403 
404     /// <summary>
405     /// Determine the remaining input stream length
406     /// </summary>
407     /// <returns>
408     /// std::numeric_limits<size_t>::max() if the stream's remaining length cannot be determined
409     /// length      if the stream's remaining length (which may be 0) can be determined
410     /// </returns>
411     /// <remarks>
412     /// This routine should only be called after a msg (request/response) has been
413     /// completely constructed.
414     /// </remarks>
415     _ASYNCRTIMP size_t _get_stream_length();
416 
417     /// <summary>
418     /// Determine the content length
419     /// </summary>
420     /// <returns>
421     /// std::numeric_limits<size_t>::max() if there is content with unknown length (transfer_encoding:chunked)
422     /// 0           if there is no content
423     /// length      if there is content with known length
424     /// </returns>
425     /// <remarks>
426     /// This routine should only be called after a msg (request/response) has been
427     /// completely constructed.
428     /// </remarks>
429     _ASYNCRTIMP size_t _get_content_length();
430 
431     /// <summary>
432     /// Determine the content length, and, if necessary, manage compression in the Transfer-Encoding header
433     /// </summary>
434     /// <returns>
435     /// std::numeric_limits<size_t>::max() if there is content with unknown length (transfer_encoding:chunked)
436     /// 0           if there is no content
437     /// length      if there is content with known length
438     /// </returns>
439     /// <remarks>
440     /// This routine is like _get_content_length, except that it adds a compression algorithm to
441     /// the Trasfer-Length header if compression is configured.  It throws if a Transfer-Encoding
442     /// header exists and does not match the one it generated.
443     /// </remarks>
444     _ASYNCRTIMP size_t _get_content_length_and_set_compression();
445 
_set_http_version(const http::http_version & http_version)446     void _set_http_version(const http::http_version& http_version) { m_http_version = http_version; }
447 
448 protected:
449     std::unique_ptr<http::compression::compress_provider> m_compressor;
450     std::unique_ptr<http::compression::decompress_provider> m_decompressor;
451     std::vector<std::shared_ptr<http::compression::decompress_factory>> m_decompressors;
452 
453     /// <summary>
454     /// Stream to read the message body.
455     /// By default this is an invalid stream. The user could set the instream on
456     /// a request by calling set_request_stream(...). This would also be set when
457     /// set_body() is called - a stream from the body is constructed and set.
458     /// Even in the presence of msg body this stream could be invalid. An example
459     /// would be when the user sets an ostream for the response. With that API the
460     /// user does not provide the ability to read the msg body.
461     /// Thus m_instream is valid when there is a msg body and it can actually be read
462     /// </summary>
463     concurrency::streams::istream m_inStream;
464 
465     /// <summary>
466     /// stream to write the msg body
467     /// By default this is an invalid stream. The user could set this on the response
468     /// (for http_client). In all the other cases we would construct one to transfer
469     /// the data from the network into the message body.
470     /// </summary>
471     concurrency::streams::ostream m_outStream;
472 
473     http::http_version m_http_version;
474     http_headers m_headers;
475     bool m_default_outstream;
476 
477     /// <summary> The TCE is used to signal the availability of the message body. </summary>
478     pplx::task_completion_event<utility::size64_t> m_data_available;
479 
480     size_t _get_content_length(bool honor_compression);
481 };
482 
483 /// <summary>
484 /// Base structure for associating internal server information
485 /// with an HTTP request/response.
486 /// </summary>
487 class _http_server_context
488 {
489 public:
_http_server_context()490     _http_server_context() {}
~_http_server_context()491     virtual ~_http_server_context() {}
492 
493 private:
494 };
495 
496 /// <summary>
497 /// Internal representation of an HTTP response.
498 /// </summary>
499 class _http_response final : public http::details::http_msg_base
500 {
501 public:
_http_response()502     _http_response() : m_status_code((std::numeric_limits<uint16_t>::max)()) {}
503 
_http_response(http::status_code code)504     _http_response(http::status_code code) : m_status_code(code) {}
505 
status_code()506     http::status_code status_code() const { return m_status_code; }
507 
set_status_code(http::status_code code)508     void set_status_code(http::status_code code) { m_status_code = code; }
509 
reason_phrase()510     const http::reason_phrase& reason_phrase() const { return m_reason_phrase; }
511 
set_reason_phrase(const http::reason_phrase & reason)512     void set_reason_phrase(const http::reason_phrase& reason) { m_reason_phrase = reason; }
513 
514     _ASYNCRTIMP utility::string_t to_string() const;
515 
_get_server_context()516     _http_server_context* _get_server_context() const { return m_server_context.get(); }
517 
_set_server_context(std::unique_ptr<details::_http_server_context> server_context)518     void _set_server_context(std::unique_ptr<details::_http_server_context> server_context)
519     {
520         m_server_context = std::move(server_context);
521     }
522 
523 private:
524     std::unique_ptr<_http_server_context> m_server_context;
525 
526     http::status_code m_status_code;
527     http::reason_phrase m_reason_phrase;
528 };
529 
530 } // namespace details
531 
532 /// <summary>
533 /// Represents an HTTP response.
534 /// </summary>
535 class http_response
536 {
537 public:
538     /// <summary>
539     /// Constructs a response with an empty status code, no headers, and no body.
540     /// </summary>
541     /// <returns>A new HTTP response.</returns>
http_response()542     http_response() : _m_impl(std::make_shared<details::_http_response>()) {}
543 
544     /// <summary>
545     /// Constructs a response with given status code, no headers, and no body.
546     /// </summary>
547     /// <param name="code">HTTP status code to use in response.</param>
548     /// <returns>A new HTTP response.</returns>
http_response(http::status_code code)549     http_response(http::status_code code) : _m_impl(std::make_shared<details::_http_response>(code)) {}
550 
551     /// <summary>
552     /// Gets the status code of the response message.
553     /// </summary>
554     /// <returns>status code.</returns>
status_code()555     http::status_code status_code() const { return _m_impl->status_code(); }
556 
557     /// <summary>
558     /// Sets the status code of the response message.
559     /// </summary>
560     /// <param name="code">Status code to set.</param>
561     /// <remarks>
562     /// This will overwrite any previously set status code.
563     /// </remarks>
set_status_code(http::status_code code)564     void set_status_code(http::status_code code) const { _m_impl->set_status_code(code); }
565 
566     /// <summary>
567     /// Gets the reason phrase of the response message.
568     /// If no reason phrase is set it will default to the standard one corresponding to the status code.
569     /// </summary>
570     /// <returns>Reason phrase.</returns>
reason_phrase()571     const http::reason_phrase& reason_phrase() const { return _m_impl->reason_phrase(); }
572 
573     /// <summary>
574     /// Sets the reason phrase of the response message.
575     /// If no reason phrase is set it will default to the standard one corresponding to the status code.
576     /// </summary>
577     /// <param name="reason">The reason phrase to set.</param>
set_reason_phrase(const http::reason_phrase & reason)578     void set_reason_phrase(const http::reason_phrase& reason) const { _m_impl->set_reason_phrase(reason); }
579 
580     /// <summary>
581     /// Gets the headers of the response message.
582     /// </summary>
583     /// <returns>HTTP headers for this response.</returns>
584     /// <remarks>
585     /// Use the <seealso cref="http_headers::add Method"/> to fill in desired headers.
586     /// </remarks>
headers()587     http_headers& headers() { return _m_impl->headers(); }
588 
589     /// <summary>
590     /// Gets a const reference to the headers of the response message.
591     /// </summary>
592     /// <returns>HTTP headers for this response.</returns>
headers()593     const http_headers& headers() const { return _m_impl->headers(); }
594 
595     /// <summary>
596     /// Generates a string representation of the message, including the body when possible.
597     /// Mainly this should be used for debugging purposes as it has to copy the
598     /// message body and doesn't have excellent performance.
599     /// </summary>
600     /// <returns>A string representation of this HTTP request.</returns>
601     /// <remarks>Note this function is synchronous and doesn't wait for the
602     /// entire message body to arrive. If the message body has arrived by the time this
603     /// function is called and it is has a textual Content-Type it will be included.
604     /// Otherwise just the headers will be present.</remarks>
to_string()605     utility::string_t to_string() const { return _m_impl->to_string(); }
606 
607     /// <summary>
608     /// Extracts the body of the response message as a string value, checking that the content type is a MIME text type.
609     /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
610     /// </summary>
611     /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes text.</param>
612     /// <returns>String containing body of the message.</returns>
613     pplx::task<utility::string_t> extract_string(bool ignore_content_type = false) const
614     {
615         auto impl = _m_impl;
616         return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
617             return impl->extract_string(ignore_content_type);
618         });
619     }
620 
621     /// <summary>
622     /// Extracts the body of the response message as a UTF-8 string value, checking that the content type is a MIME text
623     /// type. A body can only be extracted once because in some cases an optimization is made where the data is 'moved'
624     /// out.
625     /// </summary>
626     /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes text.</param>
627     /// <returns>String containing body of the message.</returns>
628     pplx::task<utf8string> extract_utf8string(bool ignore_content_type = false) const
629     {
630         auto impl = _m_impl;
631         return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
632             return impl->extract_utf8string(ignore_content_type);
633         });
634     }
635 
636     /// <summary>
637     /// Extracts the body of the response message as a UTF-16 string value, checking that the content type is a MIME
638     /// text type. A body can only be extracted once because in some cases an optimization is made where the data is
639     /// 'moved' out.
640     /// </summary>
641     /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes text.</param>
642     /// <returns>String containing body of the message.</returns>
643     pplx::task<utf16string> extract_utf16string(bool ignore_content_type = false) const
644     {
645         auto impl = _m_impl;
646         return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
647             return impl->extract_utf16string(ignore_content_type);
648         });
649     }
650 
651     /// <summary>
652     /// Extracts the body of the response message into a json value, checking that the content type is application/json.
653     /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
654     /// </summary>
655     /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes json.</param>
656     /// <returns>JSON value from the body of this message.</returns>
657     pplx::task<json::value> extract_json(bool ignore_content_type = false) const
658     {
659         auto impl = _m_impl;
660         return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
661             return impl->_extract_json(ignore_content_type);
662         });
663     }
664 
665     /// <summary>
666     /// Extracts the body of the response message into a vector of bytes.
667     /// </summary>
668     /// <returns>The body of the message as a vector of bytes.</returns>
extract_vector()669     pplx::task<std::vector<unsigned char>> extract_vector() const
670     {
671         auto impl = _m_impl;
672         return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) {
673             return impl->_extract_vector();
674         });
675     }
676 
677     /// <summary>
678     /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
679     /// the character encoding of the string is UTF-8.
680     /// </summary>
681     /// <param name="body_text">String containing body text.</param>
682     /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
683     /// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
684     /// </remarks>
685     void set_body(utf8string&& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
686     {
687         const auto length = body_text.size();
688         _m_impl->set_body(
689             concurrency::streams::bytestream::open_istream<std::string>(std::move(body_text)), length, content_type);
690     }
691 
692     /// <summary>
693     /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
694     /// the character encoding of the string is UTF-8.
695     /// </summary>
696     /// <param name="body_text">String containing body text.</param>
697     /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
698     /// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
699     /// </remarks>
700     void set_body(const utf8string& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
701     {
702         _m_impl->set_body(
703             concurrency::streams::bytestream::open_istream<std::string>(body_text), body_text.size(), content_type);
704     }
705 
706     /// <summary>
707     /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
708     /// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
709     /// </summary>
710     /// <param name="body_text">String containing body text.</param>
711     /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain".</param>
712     /// <remarks>
713     /// This will overwrite any previously set body data and "Content-Type" header.
714     /// </remarks>
715     void set_body(const utf16string& body_text,
716                   utf16string content_type = utility::conversions::to_utf16string("text/plain"))
717     {
718         if (content_type.find(::utility::conversions::to_utf16string("charset=")) != content_type.npos)
719         {
720             throw std::invalid_argument("content_type can't contain a 'charset'.");
721         }
722 
723         auto utf8body = utility::conversions::utf16_to_utf8(body_text);
724         auto length = utf8body.size();
725         _m_impl->set_body(concurrency::streams::bytestream::open_istream<std::string>(std::move(utf8body)),
726                           length,
727                           std::move(content_type.append(::utility::conversions::to_utf16string("; charset=utf-8"))));
728     }
729 
730     /// <summary>
731     /// Sets the body of the message to contain json value. If the 'Content-Type'
732     /// header hasn't already been set it will be set to 'application/json'.
733     /// </summary>
734     /// <param name="body_text">json value.</param>
735     /// <remarks>
736     /// This will overwrite any previously set body data.
737     /// </remarks>
set_body(const json::value & body_data)738     void set_body(const json::value& body_data)
739     {
740         auto body_text = utility::conversions::to_utf8string(body_data.serialize());
741         auto length = body_text.size();
742         set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)),
743                  length,
744                  _XPLATSTR("application/json"));
745     }
746 
747     /// <summary>
748     /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
749     /// header hasn't already been set it will be set to 'application/octet-stream'.
750     /// </summary>
751     /// <param name="body_data">Vector containing body data.</param>
752     /// <remarks>
753     /// This will overwrite any previously set body data.
754     /// </remarks>
set_body(std::vector<unsigned char> && body_data)755     void set_body(std::vector<unsigned char>&& body_data)
756     {
757         auto length = body_data.size();
758         set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)), length);
759     }
760 
761     /// <summary>
762     /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
763     /// header hasn't already been set it will be set to 'application/octet-stream'.
764     /// </summary>
765     /// <param name="body_data">Vector containing body data.</param>
766     /// <remarks>
767     /// This will overwrite any previously set body data.
768     /// </remarks>
set_body(const std::vector<unsigned char> & body_data)769     void set_body(const std::vector<unsigned char>& body_data)
770     {
771         set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size());
772     }
773 
774     /// <summary>
775     /// Defines a stream that will be relied on to provide the body of the HTTP message when it is
776     /// sent.
777     /// </summary>
778     /// <param name="stream">A readable, open asynchronous stream.</param>
779     /// <param name="content_type">A string holding the MIME type of the message body.</param>
780     /// <remarks>
781     /// This cannot be used in conjunction with any external means of setting the body of the request.
782     /// The stream will not be read until the message is sent.
783     /// </remarks>
784     void set_body(const concurrency::streams::istream& stream,
785                   const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
786     {
787         _m_impl->set_body(stream, content_type);
788     }
789 
790     /// <summary>
791     /// Defines a stream that will be relied on to provide the body of the HTTP message when it is
792     /// sent.
793     /// </summary>
794     /// <param name="stream">A readable, open asynchronous stream.</param>
795     /// <param name="content_length">The size of the data to be sent in the body.</param>
796     /// <param name="content_type">A string holding the MIME type of the message body.</param>
797     /// <remarks>
798     /// This cannot be used in conjunction with any external means of setting the body of the request.
799     /// The stream will not be read until the message is sent.
800     /// </remarks>
801     void set_body(const concurrency::streams::istream& stream,
802                   utility::size64_t content_length,
803                   const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
804     {
805         _m_impl->set_body(stream, content_length, content_type);
806     }
807 
808     /// <summary>
809     /// Produces a stream which the caller may use to retrieve data from an incoming request.
810     /// </summary>
811     /// <returns>A readable, open asynchronous stream.</returns>
812     /// <remarks>
813     /// This cannot be used in conjunction with any other means of getting the body of the request.
814     /// It is not necessary to wait until the message has been sent before starting to write to the
815     /// stream, but it is advisable to do so, since it will allow the network I/O to start earlier
816     /// and the work of sending data can be overlapped with the production of more data.
817     /// </remarks>
body()818     concurrency::streams::istream body() const { return _m_impl->instream(); }
819 
820     /// <summary>
821     /// Signals the user (client) when all the data for this response message has been received.
822     /// </summary>
823     /// <returns>A <c>task</c> which is completed when all of the response body has been received.</returns>
content_ready()824     pplx::task<http::http_response> content_ready() const
825     {
826         http_response resp = *this;
827         return pplx::create_task(_m_impl->_get_data_available()).then([resp](utility::size64_t) mutable {
828             return resp;
829         });
830     }
831 
_get_impl()832     std::shared_ptr<http::details::_http_response> _get_impl() const { return _m_impl; }
833 
_get_server_context()834     http::details::_http_server_context* _get_server_context() const { return _m_impl->_get_server_context(); }
_set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)835     void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)
836     {
837         _m_impl->_set_server_context(std::move(server_context));
838     }
839 
840 private:
841     std::shared_ptr<http::details::_http_response> _m_impl;
842 };
843 
844 namespace details
845 {
846 /// <summary>
847 /// Internal representation of an HTTP request message.
848 /// </summary>
849 class _http_request final : public http::details::http_msg_base, public std::enable_shared_from_this<_http_request>
850 {
851 public:
852     _ASYNCRTIMP _http_request(http::method mtd);
853 
854     _ASYNCRTIMP _http_request(std::unique_ptr<http::details::_http_server_context> server_context);
855 
~_http_request()856     virtual ~_http_request() {}
857 
method()858     http::method& method() { return m_method; }
859 
request_uri()860     uri& request_uri() { return m_uri; }
861 
862     _ASYNCRTIMP uri absolute_uri() const;
863 
864     _ASYNCRTIMP uri relative_uri() const;
865 
866     _ASYNCRTIMP void set_request_uri(const uri&);
867 
remote_address()868     const utility::string_t& remote_address() const { return m_remote_address; }
869 
cancellation_token()870     const pplx::cancellation_token& cancellation_token() const { return m_cancellationToken; }
871 
set_cancellation_token(const pplx::cancellation_token & token)872     void set_cancellation_token(const pplx::cancellation_token& token) { m_cancellationToken = token; }
873 
874     _ASYNCRTIMP utility::string_t to_string() const;
875 
876     _ASYNCRTIMP pplx::task<void> reply(const http_response& response);
877 
get_response()878     pplx::task<http_response> get_response() { return pplx::task<http_response>(m_response); }
879 
880     _ASYNCRTIMP pplx::task<void> _reply_if_not_already(http::status_code status);
881 
set_response_stream(const concurrency::streams::ostream & stream)882     void set_response_stream(const concurrency::streams::ostream& stream) { m_response_stream = stream; }
883 
set_progress_handler(const progress_handler & handler)884     void set_progress_handler(const progress_handler& handler)
885     {
886         m_progress_handler = std::make_shared<progress_handler>(handler);
887     }
888 
_response_stream()889     const concurrency::streams::ostream& _response_stream() const { return m_response_stream; }
890 
_progress_handler()891     const std::shared_ptr<progress_handler>& _progress_handler() const { return m_progress_handler; }
892 
_get_server_context()893     http::details::_http_server_context* _get_server_context() const { return m_server_context.get(); }
894 
_set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)895     void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)
896     {
897         m_server_context = std::move(server_context);
898     }
899 
_set_listener_path(const utility::string_t & path)900     void _set_listener_path(const utility::string_t& path) { m_listener_path = path; }
901 
_set_base_uri(const http::uri & base_uri)902     void _set_base_uri(const http::uri& base_uri) { m_base_uri = base_uri; }
903 
_set_remote_address(const utility::string_t & remote_address)904     void _set_remote_address(const utility::string_t& remote_address) { m_remote_address = remote_address; }
905 
906 private:
907     // Actual initiates sending the response, without checking if a response has already been sent.
908     pplx::task<void> _reply_impl(http_response response);
909 
910     http::method m_method;
911 
912     // Tracks whether or not a response has already been started for this message.
913     // 0 = No reply sent
914     // 1 = Usual reply sent
915     // 2 = Reply aborted by another thread; e.g. server shutdown
916     pplx::details::atomic_long m_initiated_response;
917 
918     std::unique_ptr<http::details::_http_server_context> m_server_context;
919 
920     pplx::cancellation_token m_cancellationToken;
921 
922     http::uri m_base_uri;
923     http::uri m_uri;
924     utility::string_t m_listener_path;
925 
926     concurrency::streams::ostream m_response_stream;
927 
928     std::shared_ptr<progress_handler> m_progress_handler;
929 
930     pplx::task_completion_event<http_response> m_response;
931 
932     utility::string_t m_remote_address;
933 };
934 
935 } // namespace details
936 
937 /// <summary>
938 /// Represents an HTTP request.
939 /// </summary>
940 class http_request
941 {
942 public:
943     /// <summary>
944     /// Constructs a new HTTP request with the 'GET' method.
945     /// </summary>
http_request()946     http_request() : _m_impl(std::make_shared<http::details::_http_request>(methods::GET)) {}
947 
948     /// <summary>
949     /// Constructs a new HTTP request with the given request method.
950     /// </summary>
951     /// <param name="mtd">Request method.</param>
http_request(http::method mtd)952     http_request(http::method mtd) : _m_impl(std::make_shared<http::details::_http_request>(std::move(mtd))) {}
953 
954     /// <summary>
955     /// Destructor frees any held resources.
956     /// </summary>
~http_request()957     ~http_request() {}
958 
959     /// <summary>
960     /// Get the method (GET/PUT/POST/DELETE) of the request message.
961     /// </summary>
962     /// <returns>Request method of this HTTP request.</returns>
method()963     const http::method& method() const { return _m_impl->method(); }
964 
965     /// <summary>
966     /// Set the method (GET/PUT/POST/DELETE) of the request message.
967     /// </summary>
968     /// <param name="method">Request method of this HTTP request.</param>
set_method(const http::method & method)969     void set_method(const http::method& method) const { _m_impl->method() = method; }
970 
971     /// <summary>
972     /// Get the underling URI of the request message.
973     /// </summary>
974     /// <returns>The uri of this message.</returns>
request_uri()975     const uri& request_uri() const { return _m_impl->request_uri(); }
976 
977     /// <summary>
978     /// Set the underling URI of the request message.
979     /// </summary>
980     /// <param name="uri">The uri for this message.</param>
set_request_uri(const uri & uri)981     void set_request_uri(const uri& uri) { return _m_impl->set_request_uri(uri); }
982 
983     /// <summary>
984     /// Gets a reference the URI path, query, and fragment part of this request message.
985     /// This will be appended to the base URI specified at construction of the http_client.
986     /// </summary>
987     /// <returns>A string.</returns>
988     /// <remarks>When the request is the one passed to a listener's handler, the
989     /// relative URI is the request URI less the listener's path. In all other circumstances,
990     /// request_uri() and relative_uri() will return the same value.
991     /// </remarks>
relative_uri()992     uri relative_uri() const { return _m_impl->relative_uri(); }
993 
994     /// <summary>
995     /// Get an absolute URI with scheme, host, port, path, query, and fragment part of
996     /// the request message.
997     /// </summary>
998     /// <remarks>Absolute URI is only valid after this http_request object has been passed
999     /// to http_client::request().
1000     /// </remarks>
absolute_uri()1001     uri absolute_uri() const { return _m_impl->absolute_uri(); }
1002 
1003     /// <summary>
1004     /// Gets a reference to the headers of the response message.
1005     /// </summary>
1006     /// <returns>HTTP headers for this response.</returns>
1007     /// <remarks>
1008     /// Use the http_headers::add to fill in desired headers.
1009     /// </remarks>
headers()1010     http_headers& headers() { return _m_impl->headers(); }
1011 
1012     /// <summary>
1013     /// Gets a const reference to the headers of the response message.
1014     /// </summary>
1015     /// <returns>HTTP headers for this response.</returns>
1016     /// <remarks>
1017     /// Use the http_headers::add to fill in desired headers.
1018     /// </remarks>
headers()1019     const http_headers& headers() const { return _m_impl->headers(); }
1020 
1021     /// <summary>
1022     /// Returns the HTTP protocol version of this request message.
1023     /// </summary>
1024     /// <returns>The HTTP protocol version.</returns>
http_version()1025     http::http_version http_version() const { return _m_impl->http_version(); }
1026 
1027     /// <summary>
1028     /// Returns a string representation of the remote IP address.
1029     /// </summary>
1030     /// <returns>The remote IP address.</returns>
remote_address()1031     const utility::string_t& remote_address() const { return _m_impl->remote_address(); }
1032 
1033     CASABLANCA_DEPRECATED("Use `remote_address()` instead.")
get_remote_address()1034     const utility::string_t& get_remote_address() const { return _m_impl->remote_address(); }
1035 
1036     /// <summary>
1037     /// Extract the body of the request message as a string value, checking that the content type is a MIME text type.
1038     /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
1039     /// </summary>
1040     /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-8.</param>
1041     /// <returns>String containing body of the message.</returns>
1042     pplx::task<utility::string_t> extract_string(bool ignore_content_type = false)
1043     {
1044         auto impl = _m_impl;
1045         return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
1046             return impl->extract_string(ignore_content_type);
1047         });
1048     }
1049 
1050     /// <summary>
1051     /// Extract the body of the request message as a UTF-8 string value, checking that the content type is a MIME text
1052     /// type. A body can only be extracted once because in some cases an optimization is made where the data is 'moved'
1053     /// out.
1054     /// </summary>
1055     /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-8.</param>
1056     /// <returns>String containing body of the message.</returns>
1057     pplx::task<utf8string> extract_utf8string(bool ignore_content_type = false)
1058     {
1059         auto impl = _m_impl;
1060         return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
1061             return impl->extract_utf8string(ignore_content_type);
1062         });
1063     }
1064 
1065     /// <summary>
1066     /// Extract the body of the request message as a UTF-16 string value, checking that the content type is a MIME text
1067     /// type. A body can only be extracted once because in some cases an optimization is made where the data is 'moved'
1068     /// out.
1069     /// </summary>
1070     /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-16.</param>
1071     /// <returns>String containing body of the message.</returns>
1072     pplx::task<utf16string> extract_utf16string(bool ignore_content_type = false)
1073     {
1074         auto impl = _m_impl;
1075         return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
1076             return impl->extract_utf16string(ignore_content_type);
1077         });
1078     }
1079 
1080     /// <summary>
1081     /// Extracts the body of the request message into a json value, checking that the content type is application/json.
1082     /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
1083     /// </summary>
1084     /// <param name="ignore_content_type">If true, ignores the Content-Type header and assumes UTF-8.</param>
1085     /// <returns>JSON value from the body of this message.</returns>
1086     pplx::task<json::value> extract_json(bool ignore_content_type = false) const
1087     {
1088         auto impl = _m_impl;
1089         return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) {
1090             return impl->_extract_json(ignore_content_type);
1091         });
1092     }
1093 
1094     /// <summary>
1095     /// Extract the body of the response message into a vector of bytes. Extracting a vector can be done on
1096     /// </summary>
1097     /// <returns>The body of the message as a vector of bytes.</returns>
extract_vector()1098     pplx::task<std::vector<unsigned char>> extract_vector() const
1099     {
1100         auto impl = _m_impl;
1101         return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) {
1102             return impl->_extract_vector();
1103         });
1104     }
1105 
1106     /// <summary>
1107     /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
1108     /// the character encoding of the string is UTF-8.
1109     /// </summary>
1110     /// <param name="body_text">String containing body text.</param>
1111     /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
1112     /// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
1113     /// </remarks>
1114     void set_body(utf8string&& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
1115     {
1116         const auto length = body_text.size();
1117         _m_impl->set_body(
1118             concurrency::streams::bytestream::open_istream<std::string>(std::move(body_text)), length, content_type);
1119     }
1120 
1121     /// <summary>
1122     /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
1123     /// the character encoding of the string is UTF-8.
1124     /// </summary>
1125     /// <param name="body_text">String containing body text.</param>
1126     /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain;
1127     /// charset=utf-8".</param> <remarks> This will overwrite any previously set body data and "Content-Type" header.
1128     /// </remarks>
1129     void set_body(const utf8string& body_text, const utf8string& content_type = utf8string("text/plain; charset=utf-8"))
1130     {
1131         _m_impl->set_body(
1132             concurrency::streams::bytestream::open_istream<std::string>(body_text), body_text.size(), content_type);
1133     }
1134 
1135     /// <summary>
1136     /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes
1137     /// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
1138     /// </summary>
1139     /// </summary>
1140     /// <param name="body_text">String containing body text.</param>
1141     /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain".</param>
1142     /// <remarks>
1143     /// This will overwrite any previously set body data and "Content-Type" header.
1144     /// </remarks>
1145     void set_body(const utf16string& body_text,
1146                   utf16string content_type = utility::conversions::to_utf16string("text/plain"))
1147     {
1148         if (content_type.find(::utility::conversions::to_utf16string("charset=")) != content_type.npos)
1149         {
1150             throw std::invalid_argument("content_type can't contain a 'charset'.");
1151         }
1152 
1153         auto utf8body = utility::conversions::utf16_to_utf8(body_text);
1154         auto length = utf8body.size();
1155         _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(utf8body)),
1156                           length,
1157                           std::move(content_type.append(::utility::conversions::to_utf16string("; charset=utf-8"))));
1158     }
1159 
1160     /// <summary>
1161     /// Sets the body of the message to contain json value. If the 'Content-Type'
1162     /// header hasn't already been set it will be set to 'application/json'.
1163     /// </summary>
1164     /// <param name="body_data">json value.</param>
1165     /// <remarks>
1166     /// This will overwrite any previously set body data.
1167     /// </remarks>
set_body(const json::value & body_data)1168     void set_body(const json::value& body_data)
1169     {
1170         auto body_text = utility::conversions::to_utf8string(body_data.serialize());
1171         auto length = body_text.size();
1172         _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)),
1173                           length,
1174                           _XPLATSTR("application/json"));
1175     }
1176 
1177     /// <summary>
1178     /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
1179     /// header hasn't already been set it will be set to 'application/octet-stream'.
1180     /// </summary>
1181     /// <param name="body_data">Vector containing body data.</param>
1182     /// <remarks>
1183     /// This will overwrite any previously set body data.
1184     /// </remarks>
set_body(std::vector<unsigned char> && body_data)1185     void set_body(std::vector<unsigned char>&& body_data)
1186     {
1187         auto length = body_data.size();
1188         _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)),
1189                           length,
1190                           _XPLATSTR("application/octet-stream"));
1191     }
1192 
1193     /// <summary>
1194     /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
1195     /// header hasn't already been set it will be set to 'application/octet-stream'.
1196     /// </summary>
1197     /// <param name="body_data">Vector containing body data.</param>
1198     /// <remarks>
1199     /// This will overwrite any previously set body data.
1200     /// </remarks>
set_body(const std::vector<unsigned char> & body_data)1201     void set_body(const std::vector<unsigned char>& body_data)
1202     {
1203         set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size());
1204     }
1205 
1206     /// <summary>
1207     /// Defines a stream that will be relied on to provide the body of the HTTP message when it is
1208     /// sent.
1209     /// </summary>
1210     /// <param name="stream">A readable, open asynchronous stream.</param>
1211     /// <param name="content_type">A string holding the MIME type of the message body.</param>
1212     /// <remarks>
1213     /// This cannot be used in conjunction with any other means of setting the body of the request.
1214     /// The stream will not be read until the message is sent.
1215     /// </remarks>
1216     void set_body(const concurrency::streams::istream& stream,
1217                   const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
1218     {
1219         _m_impl->set_body(stream, content_type);
1220     }
1221 
1222     /// <summary>
1223     /// Defines a stream that will be relied on to provide the body of the HTTP message when it is
1224     /// sent.
1225     /// </summary>
1226     /// <param name="stream">A readable, open asynchronous stream.</param>
1227     /// <param name="content_length">The size of the data to be sent in the body.</param>
1228     /// <param name="content_type">A string holding the MIME type of the message body.</param>
1229     /// <remarks>
1230     /// This cannot be used in conjunction with any other means of setting the body of the request.
1231     /// The stream will not be read until the message is sent.
1232     /// </remarks>
1233     void set_body(const concurrency::streams::istream& stream,
1234                   utility::size64_t content_length,
1235                   const utility::string_t& content_type = _XPLATSTR("application/octet-stream"))
1236     {
1237         _m_impl->set_body(stream, content_length, content_type);
1238     }
1239 
1240     /// <summary>
1241     /// Produces a stream which the caller may use to retrieve data from an incoming request.
1242     /// </summary>
1243     /// <returns>A readable, open asynchronous stream.</returns>
1244     /// <remarks>
1245     /// This cannot be used in conjunction with any other means of getting the body of the request.
1246     /// It is not necessary to wait until the message has been sent before starting to write to the
1247     /// stream, but it is advisable to do so, since it will allow the network I/O to start earlier
1248     /// and the work of sending data can be overlapped with the production of more data.
1249     /// </remarks>
body()1250     concurrency::streams::istream body() const { return _m_impl->instream(); }
1251 
1252     /// <summary>
1253     /// Defines a stream that will be relied on to hold the body of the HTTP response message that
1254     /// results from the request.
1255     /// </summary>
1256     /// <param name="stream">A writable, open asynchronous stream.</param>
1257     /// <remarks>
1258     /// If this function is called, the body of the response should not be accessed in any other
1259     /// way.
1260     /// </remarks>
set_response_stream(const concurrency::streams::ostream & stream)1261     void set_response_stream(const concurrency::streams::ostream& stream)
1262     {
1263         return _m_impl->set_response_stream(stream);
1264     }
1265 
1266     /// <summary>
1267     /// Sets a compressor that will be used to compress the body of the HTTP message as it is sent.
1268     /// </summary>
1269     /// <param name="compressor">A pointer to an instantiated compressor of the desired type.</param>
1270     /// <remarks>
1271     /// This cannot be used in conjunction with any external means of compression.  The Transfer-Encoding
1272     /// header will be managed internally, and must not be set by the client.
1273     /// </remarks>
set_compressor(std::unique_ptr<http::compression::compress_provider> compressor)1274     void set_compressor(std::unique_ptr<http::compression::compress_provider> compressor)
1275     {
1276         return _m_impl->set_compressor(std::move(compressor));
1277     }
1278 
1279     /// <summary>
1280     /// Sets a compressor that will be used to compress the body of the HTTP message as it is sent.
1281     /// </summary>
1282     /// <param name="algorithm">The built-in compression algorithm to use.</param>
1283     /// <returns>
1284     /// True if a built-in compressor was instantiated, otherwise false.
1285     /// </returns>
1286     /// <remarks>
1287     /// This cannot be used in conjunction with any external means of compression.  The Transfer-Encoding
1288     /// header will be managed internally, and must not be set by the client.
1289     /// </remarks>
set_compressor(utility::string_t algorithm)1290     bool set_compressor(utility::string_t algorithm)
1291     {
1292         _m_impl->set_compressor(http::compression::builtin::make_compressor(algorithm));
1293         return (bool)_m_impl->compressor();
1294     }
1295 
1296     /// <summary>
1297     /// Gets the compressor to be used to compress the message body, if any.
1298     /// </summary>
1299     /// <returns>
1300     /// The compressor itself.
1301     /// </returns>
compressor()1302     std::unique_ptr<http::compression::compress_provider>& compressor() { return _m_impl->compressor(); }
1303 
1304     /// <summary>
1305     /// Sets the default collection of built-in factory classes for decompressors that may be used to
1306     /// decompress the body of the HTTP message as it is received, effectively enabling decompression.
1307     /// </summary>
1308     /// <param name="factories">The collection of factory classes for allowable decompressors. The
1309     /// supplied vector itself need not remain valid after the call returns.</param>
1310     /// <remarks>
1311     /// This default collection is implied if request_compressed_response() is set in the associated
1312     /// <c>client::http_client_config</c> and neither overload of this method has been called.
1313     ///
1314     /// This cannot be used in conjunction with any external means of decompression.  The TE and Accept-Encoding
1315     /// headers must not be set by the client, as they will be managed internally as appropriate.
1316     /// </remarks>
1317     _ASYNCRTIMP void set_decompress_factories();
1318 
1319     /// <summary>
1320     /// Sets a collection of factory classes for decompressors that may be used to decompress the
1321     /// body of the HTTP message as it is received, effectively enabling decompression.
1322     /// </summary>
1323     /// <remarks>
1324     /// If set, this collection takes the place of the built-in compression providers.  It may contain
1325     /// custom factory classes and/or factory classes for built-in providers, and may be used to adjust
1326     /// the weights of the built-in providers, which default to 500 (i.e. "q=0.500").
1327     ///
1328     /// This cannot be used in conjunction with any external means of decompression.  The TE and Accept-Encoding
1329     /// headers must not be set by the client, as they will be managed internally as appropriate.
1330     /// </remarks>
set_decompress_factories(const std::vector<std::shared_ptr<http::compression::decompress_factory>> & factories)1331     void set_decompress_factories(const std::vector<std::shared_ptr<http::compression::decompress_factory>>& factories)
1332     {
1333         return _m_impl->set_decompress_factories(factories);
1334     }
1335 
1336     /// <summary>
1337     /// Gets the collection of factory classes for decompressors to be used to decompress the message body, if any.
1338     /// </summary>
1339     /// <returns>
1340     /// The collection of factory classes itself.
1341     /// </returns>
1342     /// <remarks>
1343     /// This cannot be used in conjunction with any external means of decompression.  The TE
1344     /// header must not be set by the client, as it will be managed internally.
1345     /// </remarks>
decompress_factories()1346     const std::vector<std::shared_ptr<http::compression::decompress_factory>>& decompress_factories() const
1347     {
1348         return _m_impl->decompress_factories();
1349     }
1350 
1351     /// <summary>
1352     /// Defines a callback function that will be invoked for every chunk of data uploaded or downloaded
1353     /// as part of the request.
1354     /// </summary>
1355     /// <param name="handler">A function representing the progress handler. It's parameters are:
1356     ///    up:       a <c>message_direction::direction</c> value  indicating the direction of the message
1357     ///              that is being reported.
1358     ///    progress: the number of bytes that have been processed so far.
1359     /// </param>
1360     /// <remarks>
1361     ///   This function will be called at least once for upload and at least once for
1362     ///   the download body, unless there is some exception generated. An HTTP message with an error
1363     ///   code is not an exception. This means, that even if there is no body, the progress handler
1364     ///   will be called.
1365     ///
1366     ///   Setting the chunk size on the http_client does not guarantee that the client will be using
1367     ///   exactly that increment for uploading and downloading data.
1368     ///
1369     ///   The handler will be called only once for each combination of argument values, in order. Depending
1370     ///   on how a service responds, some download values may come before all upload values have been
1371     ///   reported.
1372     ///
1373     ///   The progress handler will be called on the thread processing the request. This means that
1374     ///   the implementation of the handler must take care not to block the thread or do anything
1375     ///   that takes significant amounts of time. In particular, do not do any kind of I/O from within
1376     ///   the handler, do not update user interfaces, and to not acquire any locks. If such activities
1377     ///   are necessary, it is the handler's responsibility to execute that work on a separate thread.
1378     /// </remarks>
set_progress_handler(const progress_handler & handler)1379     void set_progress_handler(const progress_handler& handler) { return _m_impl->set_progress_handler(handler); }
1380 
1381     /// <summary>
1382     /// Asynchronously responses to this HTTP request.
1383     /// </summary>
1384     /// <param name="response">Response to send.</param>
1385     /// <returns>An asynchronous operation that is completed once response is sent.</returns>
reply(const http_response & response)1386     pplx::task<void> reply(const http_response& response) const { return _m_impl->reply(response); }
1387 
1388     /// <summary>
1389     /// Asynchronously responses to this HTTP request.
1390     /// </summary>
1391     /// <param name="status">Response status code.</param>
1392     /// <returns>An asynchronous operation that is completed once response is sent.</returns>
reply(http::status_code status)1393     pplx::task<void> reply(http::status_code status) const { return reply(http_response(status)); }
1394 
1395     /// <summary>
1396     /// Responds to this HTTP request.
1397     /// </summary>
1398     /// <param name="status">Response status code.</param>
1399     /// <param name="body_data">Json value to use in the response body.</param>
1400     /// <returns>An asynchronous operation that is completed once response is sent.</returns>
reply(http::status_code status,const json::value & body_data)1401     pplx::task<void> reply(http::status_code status, const json::value& body_data) const
1402     {
1403         http_response response(status);
1404         response.set_body(body_data);
1405         return reply(response);
1406     }
1407 
1408     /// Responds to this HTTP request with a string.
1409     /// Assumes the character encoding of the string is UTF-8.
1410     /// </summary>
1411     /// <param name="status">Response status code.</param>
1412     /// <param name="body_data">UTF-8 string containing the text to use in the response body.</param>
1413     /// <param name="content_type">Content type of the body.</param>
1414     /// <returns>An asynchronous operation that is completed once response is sent.</returns>
1415     /// <remarks>
1416     //  Callers of this function do NOT need to block waiting for the response to be
1417     /// sent to before the body data is destroyed or goes out of scope.
1418     /// </remarks>
1419     pplx::task<void> reply(http::status_code status,
1420                            utf8string&& body_data,
1421                            const utf8string& content_type = "text/plain; charset=utf-8") const
1422     {
1423         http_response response(status);
1424         response.set_body(std::move(body_data), content_type);
1425         return reply(response);
1426     }
1427 
1428     /// <summary>
1429     /// Responds to this HTTP request with a string.
1430     /// Assumes the character encoding of the string is UTF-8.
1431     /// </summary>
1432     /// <param name="status">Response status code.</param>
1433     /// <param name="body_data">UTF-8 string containing the text to use in the response body.</param>
1434     /// <param name="content_type">Content type of the body.</param>
1435     /// <returns>An asynchronous operation that is completed once response is sent.</returns>
1436     /// <remarks>
1437     //  Callers of this function do NOT need to block waiting for the response to be
1438     /// sent to before the body data is destroyed or goes out of scope.
1439     /// </remarks>
1440     pplx::task<void> reply(http::status_code status,
1441                            const utf8string& body_data,
1442                            const utf8string& content_type = "text/plain; charset=utf-8") const
1443     {
1444         http_response response(status);
1445         response.set_body(body_data, content_type);
1446         return reply(response);
1447     }
1448 
1449     /// <summary>
1450     /// Responds to this HTTP request with a string. Assumes the character encoding
1451     /// of the string is UTF-16 will perform conversion to UTF-8.
1452     /// </summary>
1453     /// <param name="status">Response status code.</param>
1454     /// <param name="body_data">UTF-16 string containing the text to use in the response body.</param>
1455     /// <param name="content_type">Content type of the body.</param>
1456     /// <returns>An asynchronous operation that is completed once response is sent.</returns>
1457     /// <remarks>
1458     //  Callers of this function do NOT need to block waiting for the response to be
1459     /// sent to before the body data is destroyed or goes out of scope.
1460     /// </remarks>
1461     pplx::task<void> reply(http::status_code status,
1462                            const utf16string& body_data,
1463                            const utf16string& content_type = utility::conversions::to_utf16string("text/plain")) const
1464     {
1465         http_response response(status);
1466         response.set_body(body_data, content_type);
1467         return reply(response);
1468     }
1469 
1470     /// <summary>
1471     /// Responds to this HTTP request.
1472     /// </summary>
1473     /// <param name="status">Response status code.</param>
1474     /// <param name="content_type">A string holding the MIME type of the message body.</param>
1475     /// <param name="body">An asynchronous stream representing the body data.</param>
1476     /// <returns>A task that is completed once a response from the request is received.</returns>
1477     pplx::task<void> reply(status_code status,
1478                            const concurrency::streams::istream& body,
1479                            const utility::string_t& content_type = _XPLATSTR("application/octet-stream")) const
1480     {
1481         http_response response(status);
1482         response.set_body(body, content_type);
1483         return reply(response);
1484     }
1485 
1486     /// <summary>
1487     /// Responds to this HTTP request.
1488     /// </summary>
1489     /// <param name="status">Response status code.</param>
1490     /// <param name="content_length">The size of the data to be sent in the body..</param>
1491     /// <param name="content_type">A string holding the MIME type of the message body.</param>
1492     /// <param name="body">An asynchronous stream representing the body data.</param>
1493     /// <returns>A task that is completed once a response from the request is received.</returns>
1494     pplx::task<void> reply(status_code status,
1495                            const concurrency::streams::istream& body,
1496                            utility::size64_t content_length,
1497                            const utility::string_t& content_type = _XPLATSTR("application/octet-stream")) const
1498     {
1499         http_response response(status);
1500         response.set_body(body, content_length, content_type);
1501         return reply(response);
1502     }
1503 
1504     /// <summary>
1505     /// Signals the user (listener) when all the data for this request message has been received.
1506     /// </summary>
1507     /// <returns>A <c>task</c> which is completed when all of the response body has been received</returns>
content_ready()1508     pplx::task<http_request> content_ready() const
1509     {
1510         http_request req = *this;
1511         return pplx::create_task(_m_impl->_get_data_available()).then([req](utility::size64_t) mutable { return req; });
1512     }
1513 
1514     /// <summary>
1515     /// Gets a task representing the response that will eventually be sent.
1516     /// </summary>
1517     /// <returns>A task that is completed once response is sent.</returns>
get_response()1518     pplx::task<http_response> get_response() const { return _m_impl->get_response(); }
1519 
1520     /// <summary>
1521     /// Generates a string representation of the message, including the body when possible.
1522     /// Mainly this should be used for debugging purposes as it has to copy the
1523     /// message body and doesn't have excellent performance.
1524     /// </summary>
1525     /// <returns>A string representation of this HTTP request.</returns>
1526     /// <remarks>Note this function is synchronous and doesn't wait for the
1527     /// entire message body to arrive. If the message body has arrived by the time this
1528     /// function is called and it is has a textual Content-Type it will be included.
1529     /// Otherwise just the headers will be present.</remarks>
to_string()1530     utility::string_t to_string() const { return _m_impl->to_string(); }
1531 
1532     /// <summary>
1533     /// Sends a response if one has not already been sent.
1534     /// </summary>
_reply_if_not_already(status_code status)1535     pplx::task<void> _reply_if_not_already(status_code status) { return _m_impl->_reply_if_not_already(status); }
1536 
1537     /// <summary>
1538     /// Gets the server context associated with this HTTP message.
1539     /// </summary>
_get_server_context()1540     http::details::_http_server_context* _get_server_context() const { return _m_impl->_get_server_context(); }
1541 
1542     /// <summary>
1543     /// These are used for the initial creation of the HTTP request.
1544     /// </summary>
_create_request(std::unique_ptr<http::details::_http_server_context> server_context)1545     static http_request _create_request(std::unique_ptr<http::details::_http_server_context> server_context)
1546     {
1547         return http_request(std::move(server_context));
1548     }
_set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)1549     void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context)
1550     {
1551         _m_impl->_set_server_context(std::move(server_context));
1552     }
1553 
_set_listener_path(const utility::string_t & path)1554     void _set_listener_path(const utility::string_t& path) { _m_impl->_set_listener_path(path); }
1555 
_get_impl()1556     const std::shared_ptr<http::details::_http_request>& _get_impl() const { return _m_impl; }
1557 
_set_cancellation_token(const pplx::cancellation_token & token)1558     void _set_cancellation_token(const pplx::cancellation_token& token) { _m_impl->set_cancellation_token(token); }
1559 
_cancellation_token()1560     const pplx::cancellation_token& _cancellation_token() const { return _m_impl->cancellation_token(); }
1561 
_set_base_uri(const http::uri & base_uri)1562     void _set_base_uri(const http::uri& base_uri) { _m_impl->_set_base_uri(base_uri); }
1563 
1564 private:
1565     friend class http::details::_http_request;
1566     friend class http::client::http_client;
1567 
http_request(std::unique_ptr<http::details::_http_server_context> server_context)1568     http_request(std::unique_ptr<http::details::_http_server_context> server_context)
1569         : _m_impl(std::make_shared<details::_http_request>(std::move(server_context)))
1570     {
1571     }
1572 
1573     std::shared_ptr<http::details::_http_request> _m_impl;
1574 };
1575 
1576 namespace client
1577 {
1578 class http_pipeline;
1579 }
1580 
1581 /// <summary>
1582 /// HTTP client handler class, used to represent an HTTP pipeline stage.
1583 /// </summary>
1584 /// <remarks>
1585 /// When a request goes out, it passes through a series of stages, customizable by
1586 /// the application and/or libraries. The default stage will interact with lower-level
1587 /// communication layers to actually send the message on the network. When creating a client
1588 /// instance, an application may add pipeline stages in front of the already existing
1589 /// stages. Each stage has a reference to the next stage available in the <seealso cref="http_pipeline_stage::next_stage
1590 /// Method"/> value.
1591 /// </remarks>
1592 class http_pipeline_stage : public std::enable_shared_from_this<http_pipeline_stage>
1593 {
1594 public:
1595     http_pipeline_stage() = default;
1596 
1597     http_pipeline_stage& operator=(const http_pipeline_stage&) = delete;
1598     http_pipeline_stage(const http_pipeline_stage&) = delete;
1599 
1600     virtual ~http_pipeline_stage() = default;
1601 
1602     /// <summary>
1603     /// Runs this stage against the given request and passes onto the next stage.
1604     /// </summary>
1605     /// <param name="request">The HTTP request.</param>
1606     /// <returns>A task of the HTTP response.</returns>
1607     virtual pplx::task<http_response> propagate(http_request request) = 0;
1608 
1609 protected:
1610     /// <summary>
1611     /// Gets the next stage in the pipeline.
1612     /// </summary>
1613     /// <returns>A shared pointer to a pipeline stage.</returns>
next_stage()1614     const std::shared_ptr<http_pipeline_stage>& next_stage() const { return m_next_stage; }
1615 
1616     /// <summary>
1617     /// Gets a shared pointer to this pipeline stage.
1618     /// </summary>
1619     /// <returns>A shared pointer to a pipeline stage.</returns>
1620     CASABLANCA_DEPRECATED("This api is redundant. Use 'shared_from_this()' directly instead.")
current_stage()1621     std::shared_ptr<http_pipeline_stage> current_stage() { return this->shared_from_this(); }
1622 
1623 private:
1624     friend class ::web::http::client::http_pipeline;
1625 
set_next_stage(const std::shared_ptr<http_pipeline_stage> & next)1626     void set_next_stage(const std::shared_ptr<http_pipeline_stage>& next) { m_next_stage = next; }
1627 
1628     std::shared_ptr<http_pipeline_stage> m_next_stage;
1629 };
1630 
1631 } // namespace http
1632 } // namespace web
1633