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 #include "stdafx.h"
14
15 #include "../common/internal_http_helpers.h"
16 #include "cpprest/producerconsumerstream.h"
17 #include <sstream>
18
19 using namespace web;
20 using namespace utility;
21 using namespace concurrency;
22 using namespace utility::conversions;
23 using namespace http::details;
24
25 namespace web
26 {
27 namespace http
28 {
29 #define CRLF _XPLATSTR("\r\n")
30
content_type() const31 utility::string_t http_headers::content_type() const
32 {
33 utility::string_t result;
34 match(http::header_names::content_type, result);
35 return result;
36 }
37
38 /// Helper functions to convert a series of bytes from a charset to utf-8 or utf-16.
39 /// These APIs deal with checking for and handling byte order marker (BOM).
40 namespace
41 {
42 enum endianness
43 {
44 little_endian,
45 big_endian,
46 unknown
47 };
check_byte_order_mark(const utf16string & str)48 endianness check_byte_order_mark(const utf16string& str)
49 {
50 if (str.empty())
51 {
52 return unknown;
53 }
54 const unsigned char* src = reinterpret_cast<const unsigned char*>(str.data());
55
56 // little endian
57 if (src[0] == 0xFF && src[1] == 0xFE)
58 {
59 return little_endian;
60 }
61
62 // big endian
63 else if (src[0] == 0xFE && src[1] == 0xFF)
64 {
65 return big_endian;
66 }
67
68 return unknown;
69 }
70
convert_utf16le_to_utf8(utf16string src,bool erase_bom)71 std::string convert_utf16le_to_utf8(utf16string src, bool erase_bom)
72 {
73 if (erase_bom && !src.empty())
74 {
75 src.erase(0, 1);
76 }
77 return utf16_to_utf8(std::move(src));
78 }
79
convert_utf16le_to_string_t(utf16string src,bool erase_bom)80 utility::string_t convert_utf16le_to_string_t(utf16string src, bool erase_bom)
81 {
82 if (erase_bom && !src.empty())
83 {
84 src.erase(0, 1);
85 }
86 #ifdef _UTF16_STRINGS
87 return src;
88 #else
89 return utf16_to_utf8(std::move(src));
90 #endif
91 }
92
93 // Helper function to change endian ness from big endian to little endian
big_endian_to_little_endian(utf16string src,bool erase_bom)94 utf16string big_endian_to_little_endian(utf16string src, bool erase_bom)
95 {
96 if (erase_bom && !src.empty())
97 {
98 src.erase(0, 1);
99 }
100 if (src.empty())
101 {
102 return src;
103 }
104
105 const size_t size = src.size();
106 for (size_t i = 0; i < size; ++i)
107 {
108 utf16char ch = src[i];
109 src[i] = static_cast<utf16char>(ch << 8);
110 src[i] = static_cast<utf16char>(src[i] | ch >> 8);
111 }
112
113 return src;
114 }
115
convert_utf16be_to_utf8(utf16string src,bool erase_bom)116 std::string convert_utf16be_to_utf8(utf16string src, bool erase_bom)
117 {
118 return utf16_to_utf8(big_endian_to_little_endian(std::move(src), erase_bom));
119 }
120
convert_utf16be_to_utf16le(utf16string src,bool erase_bom)121 utf16string convert_utf16be_to_utf16le(utf16string src, bool erase_bom)
122 {
123 return big_endian_to_little_endian(std::move(src), erase_bom);
124 }
125
convert_utf16be_to_string_t(utf16string src,bool erase_bom)126 utility::string_t convert_utf16be_to_string_t(utf16string src, bool erase_bom)
127 {
128 #ifdef _UTF16_STRINGS
129 return convert_utf16be_to_utf16le(std::move(src), erase_bom);
130 #else
131 return convert_utf16be_to_utf8(std::move(src), erase_bom);
132 #endif
133 }
134
convert_utf16_to_utf8(utf16string src)135 std::string convert_utf16_to_utf8(utf16string src)
136 {
137 const endianness endian = check_byte_order_mark(src);
138 switch (endian)
139 {
140 case little_endian: return convert_utf16le_to_utf8(std::move(src), true);
141 case big_endian: return convert_utf16be_to_utf8(std::move(src), true);
142 case unknown:
143 // unknown defaults to big endian.
144 return convert_utf16be_to_utf8(std::move(src), false);
145 }
146 __assume(0);
147 }
148
convert_utf16_to_utf16(utf16string src)149 utf16string convert_utf16_to_utf16(utf16string src)
150 {
151 const endianness endian = check_byte_order_mark(src);
152 switch (endian)
153 {
154 case little_endian: src.erase(0, 1); return src;
155 case big_endian: return convert_utf16be_to_utf16le(std::move(src), true);
156 case unknown:
157 // unknown defaults to big endian.
158 return convert_utf16be_to_utf16le(std::move(src), false);
159 }
160 __assume(0);
161 }
convert_utf16_to_string_t(utf16string src)162 utility::string_t convert_utf16_to_string_t(utf16string src)
163 {
164 #ifdef _UTF16_STRINGS
165 return convert_utf16_to_utf16(std::move(src));
166 #else
167 return convert_utf16_to_utf8(std::move(src));
168 #endif
169 }
170 } // namespace
171
set_content_type(utility::string_t type)172 void http_headers::set_content_type(utility::string_t type)
173 {
174 m_headers[http::header_names::content_type] = std::move(type);
175 }
176
cache_control() const177 utility::string_t http_headers::cache_control() const
178 {
179 utility::string_t result;
180 match(http::header_names::cache_control, result);
181 return result;
182 }
183
set_cache_control(utility::string_t control)184 void http_headers::set_cache_control(utility::string_t control)
185 {
186 add(http::header_names::cache_control, std::move(control));
187 }
188
date() const189 utility::string_t http_headers::date() const
190 {
191 utility::string_t result;
192 match(http::header_names::date, result);
193 return result;
194 }
195
set_date(const utility::datetime & date)196 void http_headers::set_date(const utility::datetime& date)
197 {
198 m_headers[http::header_names::date] = date.to_string(utility::datetime::RFC_1123);
199 }
200
content_length() const201 utility::size64_t http_headers::content_length() const
202 {
203 utility::size64_t length = 0;
204 match(http::header_names::content_length, length);
205 return length;
206 }
207
set_content_length(utility::size64_t length)208 void http_headers::set_content_length(utility::size64_t length)
209 {
210 m_headers[http::header_names::content_length] = utility::conversions::details::to_string_t(length);
211 }
212
213 namespace details
214 {
flatten_http_headers(const http_headers & headers)215 utility::string_t flatten_http_headers(const http_headers& headers)
216 {
217 utility::string_t flattened_headers;
218 for (auto iter = headers.begin(); iter != headers.end(); ++iter)
219 {
220 flattened_headers.append(iter->first);
221 flattened_headers.push_back(':');
222 flattened_headers.append(iter->second);
223 flattened_headers.append(CRLF);
224 }
225 return flattened_headers;
226 }
227
parse_headers_string(_Inout_z_ utility::char_t * headersStr,web::http::http_headers & headers)228 void parse_headers_string(_Inout_z_ utility::char_t* headersStr, web::http::http_headers& headers)
229 {
230 utility::string_t str(headersStr);
231 std::size_t pos = str.find_first_of(_XPLATSTR("\r\n"));
232 std::size_t startpos = 0;
233 while (pos!=std::string::npos)
234 {
235 const utility::string_t header_line(str, startpos, pos - startpos);
236 const size_t colonIndex = header_line.find_first_of(_XPLATSTR(":"));
237 if (colonIndex != utility::string_t::npos)
238 {
239 utility::string_t key = header_line.substr(0, colonIndex);
240 utility::string_t value = header_line.substr(colonIndex + 1, header_line.length() - colonIndex - 1);
241 web::http::details::trim_whitespace(key);
242 web::http::details::trim_whitespace(value);
243 headers.add(key, value);
244 }
245 startpos = pos + 1;
246 pos = str.find_first_of(_XPLATSTR("\r\n"), pos + 1);
247 }
248 }
249
250 } // namespace details
251
from_string(const std::string & http_version_string)252 http_version __cdecl http_version::from_string(const std::string& http_version_string)
253 {
254 std::istringstream str(http_version_string);
255 str.imbue(std::locale::classic());
256
257 std::string http;
258 std::getline(str, http, '/');
259 unsigned int major = 0;
260 str >> major;
261 char dot = '\0';
262 str >> dot;
263 unsigned int minor = 0;
264 str >> minor;
265
266 // check no failure, fully consumed, and correct fixed text
267 if (!str.fail() && str.eof() && "HTTP" == http && '.' == dot)
268 {
269 return {(uint8_t)major, (uint8_t)minor};
270 }
271 return {0, 0};
272 }
273
to_utf8string() const274 std::string http_version::to_utf8string() const
275 {
276 std::string ret;
277 ret.reserve(8);
278 ret.append("HTTP/");
279 ret.append(std::to_string(static_cast<unsigned int>(major)));
280 ret.append(".");
281 ret.append(std::to_string(static_cast<unsigned int>(minor)));
282 return ret;
283 }
284
285 static const utility::char_t* stream_was_set_explicitly =
286 _XPLATSTR("A stream was set on the message and extraction is not possible");
287 static const utility::char_t* unsupported_charset =
288 _XPLATSTR("Charset must be iso-8859-1, utf-8, utf-16, utf-16le, or utf-16be to be extracted.");
289
http_msg_base()290 http_msg_base::http_msg_base() : m_http_version(http::http_version {0, 0}), m_headers(), m_default_outstream(false) {}
291
_prepare_to_receive_data()292 void http_msg_base::_prepare_to_receive_data()
293 {
294 // See if the user specified an outstream
295 if (!outstream())
296 {
297 // The user did not specify an outstream.
298 // We will create one...
299 concurrency::streams::producer_consumer_buffer<uint8_t> buf;
300 set_outstream(buf.create_ostream(), true);
301
302 // Since we are creating the streambuffer, set the input stream
303 // so that the user can retrieve the data.
304 set_instream(buf.create_istream());
305 }
306
307 // If the user did specify an outstream we leave the instream
308 // as invalid. It is assumed that user either has a read head
309 // to the out streambuffer or the data is streamed into a container
310 // or media (like file) that the user can read from...
311 }
312
_get_stream_length()313 size_t http_msg_base::_get_stream_length()
314 {
315 auto& stream = instream();
316
317 if (stream.can_seek())
318 {
319 auto offset = stream.tell();
320 auto end = stream.seek(0, std::ios_base::end);
321 stream.seek(offset);
322 return static_cast<size_t>(end - offset);
323 }
324
325 return (std::numeric_limits<size_t>::max)();
326 }
327
_get_content_length(bool honor_compression)328 size_t http_msg_base::_get_content_length(bool honor_compression)
329 {
330 // An invalid response_stream indicates that there is no body
331 if ((bool)instream())
332 {
333 size_t content_length;
334 utility::string_t transfer_encoding;
335
336 if (headers().match(header_names::transfer_encoding, transfer_encoding))
337 {
338 // Transfer encoding is set; it trumps any content length that may or may not be present
339 if (honor_compression && m_compressor)
340 {
341 http::http_headers tmp;
342
343 // Build a header for comparison with the existing one
344 tmp.add(header_names::transfer_encoding, m_compressor->algorithm());
345 tmp.add(header_names::transfer_encoding, _XPLATSTR("chunked"));
346
347 if (!utility::details::str_iequal(transfer_encoding, tmp[header_names::transfer_encoding]))
348 {
349 // Some external entity added this header, and it doesn't match our
350 // expectations; bail out, since the caller's intentions are not clear
351 throw http_exception("Transfer-Encoding header is internally managed when compressing");
352 }
353 }
354
355 return (std::numeric_limits<size_t>::max)();
356 }
357
358 if (honor_compression && m_compressor)
359 {
360 // A compressor is set; this implies transfer encoding, since we don't know the compressed length
361 // up front for content encoding. We return the uncompressed length if we can figure it out.
362 headers().add(header_names::transfer_encoding, m_compressor->algorithm());
363 headers().add(header_names::transfer_encoding, _XPLATSTR("chunked"));
364 return (std::numeric_limits<size_t>::max)();
365 }
366
367 if (headers().match(header_names::content_length, content_length))
368 {
369 // An explicit content length is set; trust it, since we
370 // may not be required to send the stream's entire contents
371 return content_length;
372 }
373
374 content_length = _get_stream_length();
375 if (content_length != (std::numeric_limits<size_t>::max)())
376 {
377 // The content length wasn't explicitly set, but we figured it out;
378 // use it, since sending this way is more efficient than chunking
379 headers().add(header_names::content_length, content_length);
380 return content_length;
381 }
382
383 // We don't know the content length; we'll chunk the stream
384 headers().add(header_names::transfer_encoding, _XPLATSTR("chunked"));
385 return (std::numeric_limits<size_t>::max)();
386 }
387
388 // There is no content
389 return 0;
390 }
391
_get_content_length_and_set_compression()392 size_t http_msg_base::_get_content_length_and_set_compression() { return _get_content_length(true); }
393
_get_content_length()394 size_t http_msg_base::_get_content_length() { return _get_content_length(false); }
395
396 // Helper function to inline continuation if possible.
397 struct inline_continuation
398 {
inline_continuationweb::http::inline_continuation399 inline_continuation(pplx::task<void>& prev, const std::function<void(pplx::task<void>)>& next)
400 : m_prev(prev), m_next(next)
401 {
402 }
~inline_continuationweb::http::inline_continuation403 ~inline_continuation()
404 {
405 if (m_prev.is_done())
406 {
407 m_next(m_prev);
408 }
409 else
410 {
411 m_prev.then(m_next);
412 }
413 }
414 pplx::task<void>& m_prev;
415 std::function<void(pplx::task<void>)> m_next;
416
417 private:
418 inline_continuation(const inline_continuation&);
419 inline_continuation& operator=(const inline_continuation&);
420 };
421
_complete(utility::size64_t body_size,const std::exception_ptr & exceptionPtr)422 void http_msg_base::_complete(utility::size64_t body_size, const std::exception_ptr& exceptionPtr)
423 {
424 const auto& completionEvent = _get_data_available();
425 auto closeTask = pplx::task_from_result();
426 if (m_default_outstream)
427 {
428 // if the outstream is one we created by default on the customer's behalf, try to close it
429 auto& out = outstream();
430 if (out.is_valid())
431 {
432 if (exceptionPtr == std::exception_ptr())
433 {
434 closeTask = out.close();
435 }
436 else
437 {
438 closeTask = out.close(exceptionPtr);
439 }
440 }
441 }
442
443 if (exceptionPtr == std::exception_ptr())
444 {
445 inline_continuation(closeTask, [completionEvent, body_size](pplx::task<void> t) {
446 try
447 {
448 t.get();
449 completionEvent.set(body_size);
450 }
451 catch (...)
452 {
453 // If close throws an exception report back to user.
454 completionEvent.set_exception(std::current_exception());
455 pplx::create_task(completionEvent).then([](pplx::task<utility::size64_t> t) {
456 try
457 {
458 t.get();
459 }
460 catch (...)
461 {
462 }
463 });
464 }
465 });
466 }
467 else
468 {
469 inline_continuation(closeTask, [completionEvent, exceptionPtr](pplx::task<void> t) {
470 // If closing stream throws an exception ignore since we already have an error.
471 try
472 {
473 t.get();
474 }
475 catch (...)
476 {
477 }
478 completionEvent.set_exception(exceptionPtr);
479 pplx::create_task(completionEvent).then([](pplx::task<utility::size64_t> t) {
480 try
481 {
482 t.get();
483 }
484 catch (...)
485 {
486 }
487 });
488 });
489 }
490 }
491
is_content_type_one_of(const utility::string_t * first,const utility::string_t * last,const utility::string_t & value)492 static bool is_content_type_one_of(const utility::string_t* first,
493 const utility::string_t* last,
494 const utility::string_t& value)
495 {
496 while (first != last)
497 {
498 if (utility::details::str_iequal(*first, value))
499 {
500 return true;
501 }
502 ++first;
503 }
504 return false;
505 }
506
507 // Remove once VS 2013 is no longer supported.
508 #if defined(_WIN32) && _MSC_VER < 1900
509 // Not referring to mime_types to avoid static initialization order fiasco.
510 static const utility::string_t textual_types[] = {U("message/http"),
511 U("application/json"),
512 U("application/xml"),
513 U("application/atom+xml"),
514 U("application/http"),
515 U("application/x-www-form-urlencoded")};
516 #endif
517
518 /// <summary>
519 /// Determines whether or not the given content type is 'textual' according the feature specifications.
520 /// </summary>
is_content_type_textual(const utility::string_t & content_type)521 static bool is_content_type_textual(const utility::string_t& content_type)
522 {
523 #if !defined(_WIN32) || _MSC_VER >= 1900
524 static const utility::string_t textual_types[] = {mime_types::message_http,
525 mime_types::application_json,
526 mime_types::application_xml,
527 mime_types::application_atom_xml,
528 mime_types::application_http,
529 mime_types::application_x_www_form_urlencoded};
530 #endif
531
532 if (content_type.size() >= 4 && utility::details::str_iequal(content_type.substr(0, 4), _XPLATSTR("text")))
533 {
534 return true;
535 }
536 return (is_content_type_one_of(std::begin(textual_types), std::end(textual_types), content_type));
537 }
538
539 // Remove once VS 2013 is no longer supported.
540 #if defined(_WIN32) && _MSC_VER < 1900
541 // Not referring to mime_types to avoid static initialization order fiasco.
542 static const utility::string_t json_types[] = {U("application/json"),
543 U("application/x-json"),
544 U("text/json"),
545 U("text/x-json"),
546 U("text/javascript"),
547 U("text/x-javascript"),
548 U("application/javascript"),
549 U("application/x-javascript")};
550 #endif
551
552 /// <summary>
553 /// Determines whether or not the given content type is JSON according the feature specifications.
554 /// </summary>
is_content_type_json(const utility::string_t & content_type)555 static bool is_content_type_json(const utility::string_t& content_type)
556 {
557 #if !defined(_WIN32) || _MSC_VER >= 1900
558 static const utility::string_t json_types[] = {mime_types::application_json,
559 mime_types::application_xjson,
560 mime_types::text_json,
561 mime_types::text_xjson,
562 mime_types::text_javascript,
563 mime_types::text_xjavascript,
564 mime_types::application_javascript,
565 mime_types::application_xjavascript};
566 #endif
567
568 return (is_content_type_one_of(std::begin(json_types), std::end(json_types), content_type));
569 }
570
571 /// <summary>
572 /// Gets the default charset for given content type. If the MIME type is not textual or recognized Latin1 will be
573 /// returned.
574 /// </summary>
get_default_charset(const utility::string_t & content_type)575 static utility::string_t get_default_charset(const utility::string_t& content_type)
576 {
577 // We are defaulting everything to Latin1 except JSON which is utf-8.
578 if (is_content_type_json(content_type))
579 {
580 return charset_types::utf8;
581 }
582 else
583 {
584 return charset_types::latin1;
585 }
586 }
587
588 /// <summary>
589 /// Parses the given Content-Type header value to get out actual content type and charset.
590 /// If the charset isn't specified the default charset for the content type will be set.
591 /// </summary>
parse_content_type_and_charset(const utility::string_t & content_type,utility::string_t & content,utility::string_t & charset)592 static void parse_content_type_and_charset(const utility::string_t& content_type,
593 utility::string_t& content,
594 utility::string_t& charset)
595 {
596 const size_t semi_colon_index = content_type.find_first_of(_XPLATSTR(";"));
597
598 // No charset specified.
599 if (semi_colon_index == utility::string_t::npos)
600 {
601 content = content_type;
602 trim_whitespace(content);
603 charset = get_default_charset(content);
604 return;
605 }
606
607 // Split into content type and second part which could be charset.
608 content = content_type.substr(0, semi_colon_index);
609 trim_whitespace(content);
610 utility::string_t possible_charset = content_type.substr(semi_colon_index + 1);
611 trim_whitespace(possible_charset);
612 const size_t equals_index = possible_charset.find_first_of(_XPLATSTR("="));
613
614 // No charset specified.
615 if (equals_index == utility::string_t::npos)
616 {
617 charset = get_default_charset(content);
618 return;
619 }
620
621 // Split and make sure 'charset'
622 utility::string_t charset_key = possible_charset.substr(0, equals_index);
623 trim_whitespace(charset_key);
624 if (!utility::details::str_iequal(charset_key, _XPLATSTR("charset")))
625 {
626 charset = get_default_charset(content);
627 return;
628 }
629 charset = possible_charset.substr(equals_index + 1);
630 // Remove the redundant ';' at the end of charset.
631 while (charset.back() == ';')
632 {
633 charset.pop_back();
634 }
635 trim_whitespace(charset);
636 if (charset.front() == _XPLATSTR('"') && charset.back() == _XPLATSTR('"'))
637 {
638 charset = charset.substr(1, charset.size() - 2);
639 trim_whitespace(charset);
640 }
641 }
642
parse_and_check_content_type(bool ignore_content_type,const std::function<bool (const utility::string_t &)> & check_content_type)643 utility::string_t details::http_msg_base::parse_and_check_content_type(
644 bool ignore_content_type, const std::function<bool(const utility::string_t&)>& check_content_type)
645 {
646 if (!instream())
647 {
648 throw http_exception(stream_was_set_explicitly);
649 }
650
651 utility::string_t content, charset = charset_types::utf8;
652 if (!ignore_content_type)
653 {
654 parse_content_type_and_charset(headers().content_type(), content, charset);
655
656 // If no Content-Type or empty body then just return an empty string.
657 if (content.empty() || instream().streambuf().in_avail() == 0)
658 {
659 return utility::string_t();
660 }
661
662 if (!check_content_type(content))
663 {
664 throw http_exception(
665 _XPLATSTR("Incorrect Content-Type: must be textual to extract_string, JSON to extract_json."));
666 }
667 }
668 return charset;
669 }
670
extract_utf8string(bool ignore_content_type)671 utf8string details::http_msg_base::extract_utf8string(bool ignore_content_type)
672 {
673 const auto& charset = parse_and_check_content_type(ignore_content_type, is_content_type_textual);
674 if (charset.empty())
675 {
676 return utf8string();
677 }
678 auto buf_r = instream().streambuf();
679
680 // Perform the correct character set conversion if one is necessary.
681 if (utility::details::str_iequal(charset, charset_types::utf8) ||
682 utility::details::str_iequal(charset, charset_types::usascii) ||
683 utility::details::str_iequal(charset, charset_types::ascii))
684 {
685 std::string body;
686 body.resize((std::string::size_type)buf_r.in_avail());
687 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
688 .get(); // There is no risk of blocking.
689 return body;
690 }
691
692 // Latin1
693 else if (utility::details::str_iequal(charset, charset_types::latin1))
694 {
695 std::string body;
696 body.resize((std::string::size_type)buf_r.in_avail());
697 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
698 .get(); // There is no risk of blocking.
699 return latin1_to_utf8(std::move(body));
700 }
701
702 // utf-16
703 else if (utility::details::str_iequal(charset, charset_types::utf16))
704 {
705 utf16string body;
706 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
707 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
708 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
709 return convert_utf16_to_utf8(std::move(body));
710 }
711
712 // utf-16le
713 else if (utility::details::str_iequal(charset, charset_types::utf16le))
714 {
715 utf16string body;
716 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
717 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
718 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
719 return utility::conversions::utf16_to_utf8(std::move(body));
720 }
721
722 // utf-16be
723 else if (utility::details::str_iequal(charset, charset_types::utf16be))
724 {
725 utf16string body;
726 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
727 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
728 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
729 return convert_utf16be_to_utf8(std::move(body), false);
730 }
731
732 else
733 {
734 throw http_exception(unsupported_charset);
735 }
736 }
737
extract_utf16string(bool ignore_content_type)738 utf16string details::http_msg_base::extract_utf16string(bool ignore_content_type)
739 {
740 const auto& charset = parse_and_check_content_type(ignore_content_type, is_content_type_textual);
741 if (charset.empty())
742 {
743 return utf16string();
744 }
745 auto buf_r = instream().streambuf();
746
747 // Perform the correct character set conversion if one is necessary.
748 if (utility::details::str_iequal(charset, charset_types::utf16le))
749 {
750 utf16string body;
751 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
752 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
753 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
754 return body;
755 }
756
757 // utf-8, ascii
758 else if (utility::details::str_iequal(charset, charset_types::utf8) ||
759 utility::details::str_iequal(charset, charset_types::usascii) ||
760 utility::details::str_iequal(charset, charset_types::ascii))
761 {
762 std::string body;
763 body.resize((std::string::size_type)buf_r.in_avail());
764 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
765 .get(); // There is no risk of blocking.
766 return utility::conversions::utf8_to_utf16(std::move(body));
767 }
768
769 // Latin1
770 else if (utility::details::str_iequal(charset, charset_types::latin1))
771 {
772 std::string body;
773 body.resize((std::string::size_type)buf_r.in_avail());
774 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
775 .get(); // There is no risk of blocking.
776 return latin1_to_utf16(std::move(body));
777 }
778
779 // utf-16
780 else if (utility::details::str_iequal(charset, charset_types::utf16))
781 {
782 utf16string body;
783 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
784 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
785 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
786 return convert_utf16_to_utf16(std::move(body));
787 }
788
789 // utf-16be
790 else if (utility::details::str_iequal(charset, charset_types::utf16be))
791 {
792 utf16string body;
793 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
794 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
795 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
796 return convert_utf16be_to_utf16le(std::move(body), false);
797 }
798
799 else
800 {
801 throw http_exception(unsupported_charset);
802 }
803 }
804
extract_string(bool ignore_content_type)805 utility::string_t details::http_msg_base::extract_string(bool ignore_content_type)
806 {
807 const auto& charset = parse_and_check_content_type(ignore_content_type, is_content_type_textual);
808 if (charset.empty())
809 {
810 return utility::string_t();
811 }
812 auto buf_r = instream().streambuf();
813
814 // Perform the correct character set conversion if one is necessary.
815 if (utility::details::str_iequal(charset, charset_types::usascii) ||
816 utility::details::str_iequal(charset, charset_types::ascii))
817 {
818 std::string body;
819 body.resize((std::string::size_type)buf_r.in_avail());
820 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
821 .get(); // There is no risk of blocking.
822 return to_string_t(std::move(body));
823 }
824
825 // Latin1
826 if (utility::details::str_iequal(charset, charset_types::latin1))
827 {
828 std::string body;
829 body.resize((std::string::size_type)buf_r.in_avail());
830 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
831 .get(); // There is no risk of blocking.
832 // Could optimize for linux in the future if a latin1_to_utf8 function was written.
833 return to_string_t(latin1_to_utf16(std::move(body)));
834 }
835
836 // utf-8.
837 else if (utility::details::str_iequal(charset, charset_types::utf8))
838 {
839 std::string body;
840 body.resize((std::string::size_type)buf_r.in_avail());
841 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
842 .get(); // There is no risk of blocking.
843 return to_string_t(std::move(body));
844 }
845
846 // utf-16.
847 else if (utility::details::str_iequal(charset, charset_types::utf16))
848 {
849 utf16string body;
850 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
851 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
852 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
853 return convert_utf16_to_string_t(std::move(body));
854 }
855
856 // utf-16le
857 else if (utility::details::str_iequal(charset, charset_types::utf16le))
858 {
859 utf16string body;
860 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
861 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
862 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
863 return convert_utf16le_to_string_t(std::move(body), false);
864 }
865
866 // utf-16be
867 else if (utility::details::str_iequal(charset, charset_types::utf16be))
868 {
869 utf16string body;
870 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
871 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
872 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
873 return convert_utf16be_to_string_t(std::move(body), false);
874 }
875
876 else
877 {
878 throw http_exception(unsupported_charset);
879 }
880 }
881
_extract_json(bool ignore_content_type)882 json::value details::http_msg_base::_extract_json(bool ignore_content_type)
883 {
884 const auto& charset = parse_and_check_content_type(ignore_content_type, is_content_type_json);
885 if (charset.empty())
886 {
887 return json::value();
888 }
889 auto buf_r = instream().streambuf();
890
891 // Latin1
892 if (utility::details::str_iequal(charset, charset_types::latin1))
893 {
894 std::string body;
895 body.resize(buf_r.in_avail());
896 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
897 .get(); // There is no risk of blocking.
898 // On Linux could optimize in the future if a latin1_to_utf8 function is written.
899 return json::value::parse(to_string_t(latin1_to_utf16(std::move(body))));
900 }
901
902 // utf-8, usascii and ascii
903 else if (utility::details::str_iequal(charset, charset_types::utf8) ||
904 utility::details::str_iequal(charset, charset_types::usascii) ||
905 utility::details::str_iequal(charset, charset_types::ascii))
906 {
907 std::string body;
908 body.resize(buf_r.in_avail());
909 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), body.size())
910 .get(); // There is no risk of blocking.
911 return json::value::parse(to_string_t(std::move(body)));
912 }
913
914 // utf-16.
915 else if (utility::details::str_iequal(charset, charset_types::utf16))
916 {
917 utf16string body;
918 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
919 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
920 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
921 return json::value::parse(convert_utf16_to_string_t(std::move(body)));
922 }
923
924 // utf-16le
925 else if (utility::details::str_iequal(charset, charset_types::utf16le))
926 {
927 utf16string body;
928 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
929 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
930 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
931 return json::value::parse(convert_utf16le_to_string_t(std::move(body), false));
932 }
933
934 // utf-16be
935 else if (utility::details::str_iequal(charset, charset_types::utf16be))
936 {
937 utf16string body;
938 body.resize(buf_r.in_avail() / sizeof(utf16string::value_type));
939 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())),
940 body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking.
941 return json::value::parse(convert_utf16be_to_string_t(std::move(body), false));
942 }
943
944 else
945 {
946 throw http_exception(unsupported_charset);
947 }
948 }
949
_extract_vector()950 std::vector<uint8_t> details::http_msg_base::_extract_vector()
951 {
952 if (!instream())
953 {
954 throw http_exception(stream_was_set_explicitly);
955 }
956
957 std::vector<uint8_t> body;
958 auto buf_r = instream().streambuf();
959 const size_t size = buf_r.in_avail();
960 body.resize(size);
961 buf_r.getn(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(body.data())), size)
962 .get(); // There is no risk of blocking.
963
964 return body;
965 }
966
967 // Helper function to convert message body without extracting.
convert_body_to_string_t(const utility::string_t & content_type,concurrency::streams::istream instream)968 static utility::string_t convert_body_to_string_t(const utility::string_t& content_type,
969 concurrency::streams::istream instream)
970 {
971 if (!instream)
972 {
973 // The instream is not yet set
974 return utility::string_t();
975 }
976
977 concurrency::streams::streambuf<uint8_t> streambuf = instream.streambuf();
978
979 _ASSERTE((bool)streambuf);
980 _ASSERTE(streambuf.is_open());
981 _ASSERTE(streambuf.can_read());
982
983 utility::string_t content, charset;
984 parse_content_type_and_charset(content_type, content, charset);
985
986 // Content-Type must have textual type.
987 if (!is_content_type_textual(content) || streambuf.in_avail() == 0)
988 {
989 return utility::string_t();
990 }
991
992 // Latin1
993 if (utility::details::str_iequal(charset, charset_types::latin1))
994 {
995 std::string body;
996 body.resize(streambuf.in_avail());
997 if (streambuf.scopy((unsigned char*)&body[0], body.size()) == 0) return string_t();
998 return to_string_t(latin1_to_utf16(std::move(body)));
999 }
1000
1001 // utf-8.
1002 else if (utility::details::str_iequal(charset, charset_types::utf8))
1003 {
1004 std::string body;
1005 body.resize(streambuf.in_avail());
1006 if (streambuf.scopy((unsigned char*)&body[0], body.size()) == 0) return string_t();
1007 return to_string_t(std::move(body));
1008 }
1009
1010 // utf-16.
1011 else if (utility::details::str_iequal(charset, charset_types::utf16))
1012 {
1013 utf16string body;
1014 body.resize(streambuf.in_avail() / sizeof(utf16string::value_type));
1015 if (streambuf.scopy((unsigned char*)&body[0], body.size() * sizeof(utf16string::value_type)) == 0)
1016 return string_t();
1017 return convert_utf16_to_string_t(std::move(body));
1018 }
1019
1020 // utf-16le
1021 else if (utility::details::str_iequal(charset, charset_types::utf16le))
1022 {
1023 utf16string body;
1024 body.resize(streambuf.in_avail() / sizeof(utf16string::value_type));
1025 if (streambuf.scopy((unsigned char*)&body[0], body.size() * sizeof(utf16string::value_type)) == 0)
1026 return string_t();
1027 return convert_utf16le_to_string_t(std::move(body), false);
1028 }
1029
1030 // utf-16be
1031 else if (utility::details::str_iequal(charset, charset_types::utf16be))
1032 {
1033 utf16string body;
1034 body.resize(streambuf.in_avail() / sizeof(utf16string::value_type));
1035 if (streambuf.scopy((unsigned char*)&body[0], body.size() * sizeof(utf16string::value_type)) == 0)
1036 return string_t();
1037 return convert_utf16be_to_string_t(std::move(body), false);
1038 }
1039
1040 else
1041 {
1042 return utility::string_t();
1043 }
1044 }
1045
1046 //
1047 // Helper function to generate a wstring from given http_headers and message body.
1048 //
http_headers_body_to_string(const http_headers & headers,concurrency::streams::istream instream)1049 static utility::string_t http_headers_body_to_string(const http_headers& headers,
1050 concurrency::streams::istream instream)
1051 {
1052 utility::string_t result;
1053 for (const auto& header : headers)
1054 {
1055 result += header.first;
1056 result += _XPLATSTR(": ");
1057 result += header.second;
1058 result += CRLF;
1059 }
1060
1061 result += CRLF;
1062
1063 utility::string_t content_type;
1064 if (headers.match(http::header_names::content_type, content_type))
1065 {
1066 result += convert_body_to_string_t(content_type, instream);
1067 }
1068
1069 return result;
1070 }
1071
to_string() const1072 utility::string_t details::http_msg_base::to_string() const
1073 {
1074 return http_headers_body_to_string(m_headers, instream());
1075 }
1076
set_content_type_if_not_present(http::http_headers & headers,const utility::string_t & content_type)1077 static void set_content_type_if_not_present(http::http_headers& headers, const utility::string_t& content_type)
1078 {
1079 utility::string_t temp;
1080 if (!headers.match(http::header_names::content_type, temp))
1081 {
1082 headers.add(http::header_names::content_type, content_type);
1083 }
1084 }
1085
set_body(const streams::istream & instream,const utf8string & contentType)1086 void details::http_msg_base::set_body(const streams::istream& instream, const utf8string& contentType)
1087 {
1088 set_content_type_if_not_present(headers(),
1089 #ifdef _UTF16_STRINGS
1090 utility::conversions::utf8_to_utf16(contentType));
1091 #else
1092 contentType);
1093 #endif
1094 set_instream(instream);
1095 }
1096
set_body(const streams::istream & instream,const utf16string & contentType)1097 void details::http_msg_base::set_body(const streams::istream& instream, const utf16string& contentType)
1098 {
1099 set_content_type_if_not_present(headers(),
1100 #ifdef _UTF16_STRINGS
1101 contentType);
1102 #else
1103 utility::conversions::utf16_to_utf8(contentType));
1104 #endif
1105 set_instream(instream);
1106 }
1107
set_body(const streams::istream & instream,utility::size64_t contentLength,const utf8string & contentType)1108 void details::http_msg_base::set_body(const streams::istream& instream,
1109 utility::size64_t contentLength,
1110 const utf8string& contentType)
1111 {
1112 headers().set_content_length(contentLength);
1113 set_body(instream, contentType);
1114 m_data_available.set(contentLength);
1115 }
1116
set_body(const concurrency::streams::istream & instream,utility::size64_t contentLength,const utf16string & contentType)1117 void details::http_msg_base::set_body(const concurrency::streams::istream& instream,
1118 utility::size64_t contentLength,
1119 const utf16string& contentType)
1120 {
1121 headers().set_content_length(contentLength);
1122 set_body(instream, contentType);
1123 m_data_available.set(contentLength);
1124 }
1125
_http_request(http::method mtd)1126 details::_http_request::_http_request(http::method mtd)
1127 : m_method(std::move(mtd))
1128 , m_initiated_response(0)
1129 , m_server_context()
1130 , m_cancellationToken(pplx::cancellation_token::none())
1131 {
1132 if (m_method.empty())
1133 {
1134 throw std::invalid_argument("Invalid HTTP method specified. Method can't be an empty string.");
1135 }
1136 }
1137
_http_request(std::unique_ptr<http::details::_http_server_context> server_context)1138 details::_http_request::_http_request(std::unique_ptr<http::details::_http_server_context> server_context)
1139 : m_initiated_response(0)
1140 , m_server_context(std::move(server_context))
1141 , m_cancellationToken(pplx::cancellation_token::none())
1142 {
1143 }
1144
set_decompress_factories()1145 void http_request::set_decompress_factories()
1146 {
1147 return _m_impl->set_decompress_factories(compression::details::builtin::get_decompress_factories());
1148 }
1149
1150 const http_version http_versions::HTTP_0_9 = {0, 9};
1151 const http_version http_versions::HTTP_1_0 = {1, 0};
1152 const http_version http_versions::HTTP_1_1 = {1, 1};
1153
1154 #define _METHODS
1155 #define DAT(a, b) const method methods::a = b;
1156 #include "cpprest/details/http_constants.dat"
1157 #undef _METHODS
1158 #undef DAT
1159
1160 #define _HEADER_NAMES
1161 #define DAT(a, b) const utility::string_t header_names::a = _XPLATSTR(b);
1162 #include "cpprest/details/http_constants.dat"
1163 #undef _HEADER_NAMES
1164 #undef DAT
1165
1166 #define _MIME_TYPES
1167 #define DAT(a, b) const utility::string_t mime_types::a = _XPLATSTR(b);
1168 #include "cpprest/details/http_constants.dat"
1169 #undef _MIME_TYPES
1170 #undef DAT
1171
1172 #define _CHARSET_TYPES
1173 #define DAT(a, b) const utility::string_t charset_types::a = _XPLATSTR(b);
1174 #include "cpprest/details/http_constants.dat"
1175 #undef _CHARSET_TYPES
1176 #undef DAT
1177
1178 // This is necessary for Linux because of a bug in GCC 4.7
1179 #ifndef _WIN32
1180 #define _PHRASES
1181 #define DAT(a, b, c) const status_code status_codes::a;
1182 #include "cpprest/details/http_constants.dat"
1183 #undef _PHRASES
1184 #undef DAT
1185 #endif
1186 } // namespace http
1187 } // namespace web
1188