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