1 #ifndef BOOST_NETWORK_PROTOCOL_HTTP_IMPL_HTTP_ASYNC_PROTOCOL_HANDLER_HPP_ 2 #define BOOST_NETWORK_PROTOCOL_HTTP_IMPL_HTTP_ASYNC_PROTOCOL_HANDLER_HPP_ 3 4 // Copyright 2010 (C) Dean Michael Berris 5 // Copyright 2011 Dean Michael Berris (dberris@google.com). 6 // Copyright 2011 Google, Inc. 7 // Distributed under the Boost Software License, Version 1.0. 8 // (See accompanying file LICENSE_1_0.txt or copy at 9 // http://www.boost.org/LICENSE_1_0.txt) 10 11 #include <iterator> 12 #include <cstdint> 13 #include <array> 14 #include <boost/logic/tribool.hpp> 15 #include <boost/network/detail/debug.hpp> 16 #include <boost/network/protocol/http/client/macros.hpp> 17 #include <boost/network/protocol/http/algorithms/linearize.hpp> 18 #include <boost/network/protocol/http/parser/incremental.hpp> 19 #include <boost/network/protocol/http/request_parser.hpp> 20 #include <boost/network/traits/string.hpp> 21 #include <boost/thread/future.hpp> 22 23 namespace boost { 24 namespace network { 25 namespace http { 26 namespace impl { 27 28 template <class Tag, unsigned version_major, unsigned version_minor> 29 struct http_async_protocol_handler { 30 protected: 31 typedef typename string<Tag>::type string_type; 32 33 #ifdef BOOST_NETWORK_DEBUG 34 struct debug_escaper { 35 string_type& string_; debug_escaperboost::network::http::impl::http_async_protocol_handler::debug_escaper36 explicit debug_escaper(string_type& string) : string_(string) {} 37 debug_escaper(debug_escaper const&) = default; 38 debug_escaper(debug_escaper&&) noexcept = default; operator ()boost::network::http::impl::http_async_protocol_handler::debug_escaper39 void operator()(typename string_type::value_type input) { 40 if (!algorithm::is_print()(input)) { 41 typename ostringstream<Tag>::type escaped_stream; 42 if (input == '\r') { 43 string_.append("\\r"); 44 } else if (input == '\n') { 45 string_.append("\\n"); 46 } else { 47 escaped_stream << "\\x" << static_cast<int>(input); 48 string_.append(escaped_stream.str()); 49 } 50 } else { 51 string_.push_back(input); 52 } 53 } 54 }; 55 #endif 56 57 template <class ResponseType> init_responseboost::network::http::impl::http_async_protocol_handler58 void init_response(ResponseType& response_, bool get_body) { 59 // TODO(dberris): review parameter necessity. 60 (void)get_body; 61 62 boost::shared_future<string_type> source_future( 63 source_promise.get_future()); 64 source(response_, source_future); 65 66 boost::shared_future<string_type> destination_future( 67 destination_promise.get_future()); 68 destination(response_, destination_future); 69 70 boost::shared_future<typename headers_container<Tag>::type> headers_future( 71 headers_promise.get_future()); 72 headers(response_, headers_future); 73 74 boost::shared_future<string_type> body_future(body_promise.get_future()); 75 body(response_, body_future); 76 77 boost::shared_future<string_type> version_future( 78 version_promise.get_future()); 79 version(response_, version_future); 80 81 boost::shared_future<std::uint16_t> status_future( 82 status_promise.get_future()); 83 status(response_, status_future); 84 85 boost::shared_future<string_type> status_message_future( 86 status_message_promise.get_future()); 87 status_message(response_, status_message_future); 88 } 89 90 struct to_http_headers { 91 typedef typename string<Tag>::type string_type; 92 template <class U> operator ()boost::network::http::impl::http_async_protocol_handler::to_http_headers93 string_type const operator()(U const& pair) const { 94 typedef typename ostringstream<Tag>::type ostringstream_type; 95 typedef constants<Tag> constants; 96 ostringstream_type header_line; 97 header_line << pair.first << constants::colon() << constants::space() 98 << pair.second << constants::crlf(); 99 return header_line.str(); 100 } 101 }; 102 103 template <class Delegate, class Callback> parse_versionboost::network::http::impl::http_async_protocol_handler104 logic::tribool parse_version(Delegate& delegate_, Callback callback, 105 size_t bytes) { 106 logic::tribool parsed_ok; 107 part_begin = part.begin(); 108 typename buffer_type::const_iterator part_end = part.begin(); 109 std::advance(part_end, bytes); 110 typename boost::iterator_range<typename buffer_type::const_iterator> 111 result_range, 112 input_range = boost::make_iterator_range(part_begin, part_end); 113 std::tie(parsed_ok, result_range) = response_parser_.parse_until( 114 response_parser_type::http_version_done, input_range); 115 if (parsed_ok == true) { 116 string_type version; 117 std::swap(version, partial_parsed); 118 version.append(std::begin(result_range), std::end(result_range)); 119 algorithm::trim(version); 120 version_promise.set_value(version); 121 part_begin = std::end(result_range); 122 } else if (parsed_ok == false) { 123 #ifdef BOOST_NETWORK_DEBUG 124 string_type escaped; 125 debug_escaper escaper(escaped); 126 std::for_each(part_begin, part_end, escaper); 127 BOOST_NETWORK_MESSAGE("[parser:" << response_parser_.state() 128 << "] buffer contents: \"" << escaped 129 << "\""); 130 #endif 131 std::runtime_error error("Invalid Version Part."); 132 version_promise.set_exception(error); 133 status_promise.set_exception(error); 134 status_message_promise.set_exception(error); 135 headers_promise.set_exception(error); 136 source_promise.set_exception(error); 137 destination_promise.set_exception(error); 138 body_promise.set_exception(error); 139 } else { 140 partial_parsed.append(std::begin(result_range), 141 std::end(result_range)); 142 part_begin = part.begin(); 143 delegate_->read_some( 144 boost::asio::mutable_buffers_1(part.data(), part.size()), 145 callback); 146 } 147 return parsed_ok; 148 } 149 150 template <class Delegate, class Callback> parse_statusboost::network::http::impl::http_async_protocol_handler151 logic::tribool parse_status(Delegate& delegate_, Callback callback, 152 size_t bytes) { 153 logic::tribool parsed_ok; 154 typename buffer_type::const_iterator part_end = part.begin(); 155 std::advance(part_end, bytes); 156 typename boost::iterator_range<typename buffer_type::const_iterator> 157 result_range, 158 input_range = boost::make_iterator_range(part_begin, part_end); 159 std::tie(parsed_ok, result_range) = response_parser_.parse_until( 160 response_parser_type::http_status_done, input_range); 161 if (parsed_ok == true) { 162 string_type status; 163 std::swap(status, partial_parsed); 164 status.append(std::begin(result_range), std::end(result_range)); 165 trim(status); 166 std::uint16_t status_int = std::stoi(status); 167 status_promise.set_value(status_int); 168 part_begin = std::end(result_range); 169 } else if (parsed_ok == false) { 170 #ifdef BOOST_NETWORK_DEBUG 171 string_type escaped; 172 debug_escaper escaper(escaped); 173 std::for_each(part_begin, part_end, escaper); 174 BOOST_NETWORK_MESSAGE("[parser:" << response_parser_.state() 175 << "] buffer contents: \"" << escaped 176 << "\""); 177 #endif 178 std::runtime_error error("Invalid status part."); 179 status_promise.set_exception(error); 180 status_message_promise.set_exception(error); 181 headers_promise.set_exception(error); 182 source_promise.set_exception(error); 183 destination_promise.set_exception(error); 184 body_promise.set_exception(error); 185 } else { 186 partial_parsed.append(std::begin(result_range), 187 std::end(result_range)); 188 part_begin = part.begin(); 189 delegate_->read_some( 190 boost::asio::mutable_buffers_1(part.data(), part.size()), 191 callback); 192 } 193 return parsed_ok; 194 } 195 196 template <class Delegate, class Callback> parse_status_messageboost::network::http::impl::http_async_protocol_handler197 logic::tribool parse_status_message(Delegate& delegate_, Callback callback, 198 size_t bytes) { 199 logic::tribool parsed_ok; 200 typename buffer_type::const_iterator part_end = part.begin(); 201 std::advance(part_end, bytes); 202 typename boost::iterator_range<typename buffer_type::const_iterator> 203 result_range, 204 input_range = boost::make_iterator_range(part_begin, part_end); 205 std::tie(parsed_ok, result_range) = response_parser_.parse_until( 206 response_parser_type::http_status_message_done, input_range); 207 if (parsed_ok == true) { 208 string_type status_message; 209 std::swap(status_message, partial_parsed); 210 status_message.append(std::begin(result_range), 211 std::end(result_range)); 212 algorithm::trim(status_message); 213 status_message_promise.set_value(status_message); 214 part_begin = std::end(result_range); 215 } else if (parsed_ok == false) { 216 #ifdef BOOST_NETWORK_DEBUG 217 string_type escaped; 218 debug_escaper escaper(escaped); 219 std::for_each(part_begin, part_end, escaper); 220 BOOST_NETWORK_MESSAGE("[parser:" << response_parser_.state() 221 << "] buffer contents: \"" << escaped 222 << "\""); 223 #endif 224 std::runtime_error error("Invalid status message part."); 225 status_message_promise.set_exception(error); 226 headers_promise.set_exception(error); 227 source_promise.set_exception(error); 228 destination_promise.set_exception(error); 229 body_promise.set_exception(error); 230 } else { 231 partial_parsed.append(std::begin(result_range), 232 std::end(result_range)); 233 part_begin = part.begin(); 234 delegate_->read_some( 235 boost::asio::mutable_buffers_1(part.data(), part.size()), 236 callback); 237 } 238 return parsed_ok; 239 } 240 parse_headers_realboost::network::http::impl::http_async_protocol_handler241 void parse_headers_real(string_type& headers_part) { 242 typename boost::iterator_range<typename string_type::const_iterator> 243 input_range = boost::make_iterator_range(headers_part), 244 result_range; 245 logic::tribool parsed_ok; 246 response_parser_type headers_parser( 247 response_parser_type::http_header_line_done); 248 typename headers_container<Tag>::type headers; 249 std::pair<string_type, string_type> header_pair; 250 //init params 251 is_content_length = false; 252 content_length = -1; 253 is_chunk_end = false; 254 while (!boost::empty(input_range)) { 255 std::tie(parsed_ok, result_range) = headers_parser.parse_until( 256 response_parser_type::http_header_colon, input_range); 257 if (headers_parser.state() != response_parser_type::http_header_colon) 258 break; 259 header_pair.first = 260 string_type(std::begin(result_range), std::end(result_range)); 261 input_range.advance_begin(boost::distance(result_range)); 262 std::tie(parsed_ok, result_range) = headers_parser.parse_until( 263 response_parser_type::http_header_line_done, input_range); 264 header_pair.second = 265 string_type(std::begin(result_range), std::end(result_range)); 266 input_range.advance_begin(boost::distance(result_range)); 267 268 trim(header_pair.first); 269 if (header_pair.first.size() > 1) { 270 header_pair.first.erase(header_pair.first.size() - 1); 271 } 272 trim(header_pair.second); 273 headers.insert(header_pair); 274 if (!is_content_length && 275 boost::iequals(header_pair.first, "Content-Length")) { 276 try { 277 content_length = std::stoll(header_pair.second); 278 is_content_length = true; 279 } 280 catch (std::exception&) { 281 //is_content_length = false; 282 } 283 } 284 } 285 // determine if the body parser will need to handle chunked encoding 286 typename headers_range<basic_response<Tag> >::type transfer_encoding_range = 287 headers.equal_range("Transfer-Encoding"); 288 is_chunk_encoding = 289 !boost::empty(transfer_encoding_range) && 290 boost::iequals(std::begin(transfer_encoding_range)->second, 291 "chunked"); 292 headers_promise.set_value(headers); 293 } 294 295 template <class Delegate, class Callback> parse_headersboost::network::http::impl::http_async_protocol_handler296 std::tuple<logic::tribool, size_t> parse_headers(Delegate& delegate_, 297 Callback callback, 298 size_t bytes) { 299 logic::tribool parsed_ok; 300 typename buffer_type::const_iterator part_end = part.begin(); 301 std::advance(part_end, bytes); 302 typename boost::iterator_range<typename buffer_type::const_iterator> 303 result_range, 304 input_range = boost::make_iterator_range(part_begin, part_end); 305 std::tie(parsed_ok, result_range) = response_parser_.parse_until( 306 response_parser_type::http_headers_done, input_range); 307 if (parsed_ok == true) { 308 string_type headers_string; 309 std::swap(headers_string, partial_parsed); 310 headers_string.append(std::begin(result_range), 311 std::end(result_range)); 312 part_begin = std::end(result_range); 313 this->parse_headers_real(headers_string); 314 } else if (parsed_ok == false) { 315 // We want to output the contents of the buffer that caused 316 // the error in debug builds. 317 #ifdef BOOST_NETWORK_DEBUG 318 string_type escaped; 319 debug_escaper escaper(escaped); 320 std::for_each(part_begin, part_end, escaper); 321 BOOST_NETWORK_MESSAGE("[parser:" << response_parser_.state() 322 << "] buffer contents: \"" << escaped 323 << "\" consumed length: " 324 << boost::distance(result_range)); 325 #endif 326 std::runtime_error error("Invalid header part."); 327 headers_promise.set_exception(error); 328 body_promise.set_exception(error); 329 source_promise.set_exception(error); 330 destination_promise.set_exception(error); 331 } else { 332 partial_parsed.append(std::begin(result_range), 333 std::end(result_range)); 334 part_begin = part.begin(); 335 delegate_->read_some( 336 boost::asio::mutable_buffers_1(part.data(), part.size()), 337 callback); 338 } 339 return std::make_tuple( 340 parsed_ok, std::distance(std::end(result_range), part_end)); 341 } 342 check_parse_body_completeboost::network::http::impl::http_async_protocol_handler343 inline bool check_parse_body_complete() const { 344 if (this->is_chunk_encoding) { 345 return parse_chunk_encoding_complete(); 346 } 347 if (this->is_content_length && this->content_length >= 0) { 348 return parse_content_length_complete(); 349 } 350 return false; 351 } 352 parse_content_length_completeboost::network::http::impl::http_async_protocol_handler353 inline bool parse_content_length_complete() const { 354 return this->partial_parsed.length() >= this-> content_length; 355 } 356 parse_chunk_encoding_completeboost::network::http::impl::http_async_protocol_handler357 bool parse_chunk_encoding_complete() const { 358 string_type body; 359 string_type crlf = "\r\n"; 360 361 typename string_type::const_iterator begin = partial_parsed.begin(); 362 for (typename string_type::const_iterator iter = 363 std::search(begin, partial_parsed.end(), crlf.begin(), crlf.end()); 364 iter != partial_parsed.end(); 365 iter = std::search(begin, partial_parsed.end(), crlf.begin(), crlf.end())) { 366 string_type line(begin, iter); 367 if (line.empty()) { 368 std::advance(iter, 2); 369 begin = iter; 370 continue; 371 } 372 std::stringstream stream(line); 373 int len; 374 stream >> std::hex >> len; 375 std::advance(iter, 2); 376 if (!len) return true; 377 if (len <= partial_parsed.end() - iter) { 378 std::advance(iter, len); 379 } else { 380 return false; 381 } 382 begin = iter; 383 } 384 return false; 385 } 386 387 template <class Delegate, class Callback> parse_bodyboost::network::http::impl::http_async_protocol_handler388 void parse_body(Delegate& delegate_, Callback callback, size_t bytes) { 389 // TODO(dberris): we should really not use a string for the partial body 390 // buffer. 391 auto it = part_begin; 392 std::advance(it, bytes); 393 partial_parsed.append(part_begin, it); 394 part_begin = part.begin(); 395 if (check_parse_body_complete()) { 396 callback(boost::asio::error::eof, 0); 397 } else { 398 delegate_->read_some( 399 boost::asio::mutable_buffers_1(part.data(), part.size()), callback); 400 } 401 } 402 403 typedef response_parser<Tag> response_parser_type; 404 typedef std::array<typename char_<Tag>::type, 405 BOOST_NETWORK_HTTP_CLIENT_CONNECTION_BUFFER_SIZE> buffer_type; 406 407 response_parser_type response_parser_; 408 boost::promise<string_type> version_promise; 409 boost::promise<std::uint16_t> status_promise; 410 boost::promise<string_type> status_message_promise; 411 boost::promise<typename headers_container<Tag>::type> headers_promise; 412 boost::promise<string_type> source_promise; 413 boost::promise<string_type> destination_promise; 414 boost::promise<string_type> body_promise; 415 buffer_type part; 416 typename buffer_type::const_iterator part_begin; 417 string_type partial_parsed; 418 bool is_chunk_encoding; 419 bool is_chunk_end; 420 bool is_content_length; 421 long long unsigned content_length; 422 }; 423 424 } // namespace impl 425 } // namespace http 426 } // namespace network 427 } // namespace boost 428 429 #endif // BOOST_NETWORK_PROTOCOL_HTTP_IMPL_HTTP_ASYNC_PROTOCOL_HANDLER_HPP_20101015 430