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