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