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