1 #ifndef BOOST_NETWORK_PROTOCOL_HTTP_SERVER_CONNECTION_HPP_20101027
2 #define BOOST_NETWORK_PROTOCOL_HTTP_SERVER_CONNECTION_HPP_20101027
3 
4 // Copyright 2010 Dean Michael Berris.
5 // Copyright 2014 Jelle Van den Driessche.
6 // Copyright 2015 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 <list>
13 #include <vector>
14 #include <memory>
15 #include <mutex>
16 #include <array>
17 #include <functional>
18 #include <tuple>
19 #include <boost/asio/buffer.hpp>
20 #include <boost/asio/ip/tcp.hpp>
21 #include <boost/asio/strand.hpp>
22 #include <boost/asio/streambuf.hpp>
23 #include <boost/asio/write.hpp>
24 #include <boost/network/protocol/http/algorithms/linearize.hpp>
25 #include <boost/network/protocol/http/server/request_parser.hpp>
26 #include <boost/network/protocol/stream_handler.hpp>
27 #include <boost/network/utils/thread_pool.hpp>
28 #include <boost/optional.hpp>
29 #include <boost/range/adaptor/sliced.hpp>
30 #include <boost/range/algorithm/copy.hpp>
31 #include <boost/range/algorithm/transform.hpp>
32 #include <boost/range/iterator_range.hpp>
33 #include <boost/scope_exit.hpp>
34 #include <boost/throw_exception.hpp>
35 #include <boost/utility/enable_if.hpp>
36 #include <boost/utility/typed_in_place_factory.hpp>
37 
38 #ifndef BOOST_NETWORK_HTTP_SERVER_CONNECTION_HEADER_BUFFER_MAX_SIZE
39 /**
40  * Here we define a page's worth of header connection buffer data.
41  * This can be tuned to reduce the memory cost of connections, but this
42  * default size is set to be friendly to typical service applications.
43  * This is the maximum size though and Boost.Asio's internal representation
44  * of a streambuf would make appropriate decisions on how big a buffer
45  * is to begin with.
46  *
47  * This kinda assumes that a page is by default 4096. Since we're using
48  * the default allocator with the static buffers, it's not guaranteed that
49  * the static buffers will be page-aligned when they are allocated.
50  */
51 #define BOOST_NETWORK_HTTP_SERVER_CONNECTION_HEADER_BUFFER_MAX_SIZE 4096
52 #endif /* BOOST_NETWORK_HTTP_SERVER_CONNECTION_HEADER_BUFFER_MAX_SIZE */
53 
54 #ifndef BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE
55 /**
56  * We define the buffer size for each connection that we will use on the server
57  * side.
58  */
59 #define BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE 4096uL
60 #endif
61 
62 namespace boost {
63 namespace network {
64 namespace http {
65 
66 extern void parse_version(std::string const& partial_parsed,
67                           std::tuple<std::uint8_t, std::uint8_t>& version_pair);
68 extern void parse_headers(std::string const& input,
69                           std::vector<request_header_narrow>& container);
70 
71 template <class Tag, class Handler>
72 struct async_connection
73     : std::enable_shared_from_this<async_connection<Tag, Handler> > {
74   /// The set of known status codes for HTTP server responses.
75   enum status_t {
76     ok = 200,
77     created = 201,
78     accepted = 202,
79     no_content = 204,
80     partial_content = 206,
81     multiple_choices = 300,
82     moved_permanently = 301,
83     moved_temporarily = 302,
84     not_modified = 304,
85     bad_request = 400,
86     unauthorized = 401,
87     forbidden = 403,
88     not_found = 404,
89     not_supported = 405,
90     not_acceptable = 406,
91     request_timeout = 408,
92     precondition_failed = 412,
93     unsatisfiable_range = 416,
94     internal_server_error = 500,
95     not_implemented = 501,
96     bad_gateway = 502,
97     service_unavailable = 503,
98     space_unavailable = 507
99   };
100 
101   typedef typename string<Tag>::type string_type;
102   typedef basic_request<Tag> request;
103 
104   /// The connection pointer type.
105   typedef std::shared_ptr<async_connection> connection_ptr;
106 
107  private:
status_messageboost::network::http::async_connection108   static char const* status_message(status_t status) {
109     static char const ok_[] = "OK", created_[] = "Created",
110                       accepted_[] = "Accepted", no_content_[] = "No Content",
111                       multiple_choices_[] = "Multiple Choices",
112                       moved_permanently_[] = "Moved Permanently",
113                       moved_temporarily_[] = "Moved Temporarily",
114                       not_modified_[] = "Not Modified",
115                       bad_request_[] = "Bad Request",
116                       unauthorized_[] = "Unauthorized",
117                       forbidden_[] = "Fobidden", not_found_[] = "Not Found",
118                       not_supported_[] = "Not Supported",
119                       not_acceptable_[] = "Not Acceptable",
120                       internal_server_error_[] = "Internal Server Error",
121                       not_implemented_[] = "Not Implemented",
122                       bad_gateway_[] = "Bad Gateway",
123                       service_unavailable_[] = "Service Unavailable",
124                       unknown_[] = "Unknown",
125                       partial_content_[] = "Partial Content",
126                       request_timeout_[] = "Request Timeout",
127                       precondition_failed_[] = "Precondition Failed",
128                       unsatisfiable_range_[] =
129                           "Requested Range Not Satisfiable",
130                       space_unavailable_[] =
131                           "Insufficient Space to Store Resource";
132     switch (status) {
133       case ok:
134         return ok_;
135       case created:
136         return created_;
137       case accepted:
138         return accepted_;
139       case no_content:
140         return no_content_;
141       case multiple_choices:
142         return multiple_choices_;
143       case moved_permanently:
144         return moved_permanently_;
145       case moved_temporarily:
146         return moved_temporarily_;
147       case not_modified:
148         return not_modified_;
149       case bad_request:
150         return bad_request_;
151       case unauthorized:
152         return unauthorized_;
153       case forbidden:
154         return forbidden_;
155       case not_found:
156         return not_found_;
157       case not_supported:
158         return not_supported_;
159       case not_acceptable:
160         return not_acceptable_;
161       case internal_server_error:
162         return internal_server_error_;
163       case not_implemented:
164         return not_implemented_;
165       case bad_gateway:
166         return bad_gateway_;
167       case service_unavailable:
168         return service_unavailable_;
169       case partial_content:
170         return partial_content_;
171       case request_timeout:
172         return request_timeout_;
173       case precondition_failed:
174         return precondition_failed_;
175       case unsatisfiable_range:
176         return unsatisfiable_range_;
177       case space_unavailable:
178         return space_unavailable_;
179       default:
180         return unknown_;
181     }
182   }
183 
184  public:
async_connectionboost::network::http::async_connection185   async_connection(
186       boost::asio::io_service& io_service, Handler& handler,
187       utils::thread_pool& thread_pool,
188       std::shared_ptr<ssl_context> ctx = std::shared_ptr<ssl_context>())
189       : strand(io_service),
190         handler(handler),
191         thread_pool_(thread_pool),
192         headers_buffer(
193             BOOST_NETWORK_HTTP_SERVER_CONNECTION_HEADER_BUFFER_MAX_SIZE),
194 #ifdef BOOST_NETWORK_ENABLE_HTTPS
195         socket_(io_service, ctx),
196 #else
197         socket_(io_service),
198 #endif
199         handshake_done(false),
200         headers_already_sent(false),
201         headers_in_progress(false) {
202     (void)ctx;
203     new_start = read_buffer_.begin();
204     data_end = read_buffer_.begin();
205   }
206 
~async_connectionboost::network::http::async_connection207   ~async_connection() throw() {
208     boost::system::error_code ignored;
209     socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_receive, ignored);
210   }
211 
212   /**
213    * A call to set_headers takes a Range where each element models the Header
214    * concept. This Range will be linearized onto a buffer, which is then sent
215    * as soon as the first call to `write` or `flush` commences.
216    *
217    * @param[in] headers A range of Header objects to write out.
218    * @pre Headers have not been sent yet.
219    * @post Headers have been linearized to a buffer, and assumed to have been
220    *   sent already when the function exits.
221    * @throw std::logic_error when the precondition is violated.
222    */
223   template <class Range>
set_headersboost::network::http::async_connection224   void set_headers(Range headers) {
225     lock_guard lock(headers_mutex);
226     if (headers_in_progress || headers_already_sent)
227       boost::throw_exception(
228           std::logic_error("Headers have already been sent."));
229 
230     if (error_encountered)
231       boost::throw_exception(boost::system::system_error(*error_encountered));
232 
233     typedef constants<Tag> consts;
234     {
235       std::ostream stream(&headers_buffer);
236       stream << consts::http_slash() << 1 << consts::dot() << 1
237              << consts::space() << status << consts::space()
238              << status_message(status) << consts::crlf();
239       if (!boost::empty(headers)) {
240         typedef typename string<Tag>::type string_type;
241         boost::transform(headers, std::ostream_iterator<string_type>(stream),
242                          linearize_header<Tag>());
243       } else {
244         stream << consts::crlf();
245       }
246       stream << consts::crlf();
247     }
248 
249     auto self = this->shared_from_this();
250     write_headers_only([self] {});
251   }
252 
253   /**
254    * Sets the status of the response.
255    *
256    * @param[in] new_status The new status for this response.
257    * @pre Headers have not been sent.
258    * @post Status is set on the response.
259    * @throw std::logic_error when the precondition is violated.
260    */
set_statusboost::network::http::async_connection261   void set_status(status_t new_status) {
262     lock_guard lock(headers_mutex);
263     if (headers_already_sent)
264       boost::throw_exception(std::logic_error(
265           "Headers have already been sent, cannot reset status."));
266     if (error_encountered)
267       boost::throw_exception(boost::system::system_error(*error_encountered));
268 
269     status = new_status;
270   }
271 
272   /**
273    * Writes a given range of bytes out in order.
274    *
275    * Even though this function looks synchronous, all it does is schedules
276    * asynchronous writes to the connection as soon as the range is serialised
277    * into appropriately sized buffers.
278    *
279    * To use in your handler, it would look like:
280    *
281    * Example:
282    * \code{.cpp}
283    *     connection->write("Hello, world!\n");
284    *     std::string sample = "I have a string!";
285    *     connection->write(sample);
286    * \endcode
287    *
288    * Note that if you want to send custom status and headers, you MUST call
289    * set_status and/or set_headers before any calls to write.
290    *
291    * @param[in] range A Boost.Range ``Single Pass Range`` of char's for writing.
292    * @throw boost::system::system_error The encountered underlying error in previous
293    *     operations.
294    * @post Status and headers have been sent, contents in the range have been
295    *     serialized.
296    */
297   template <class Range>
writeboost::network::http::async_connection298   void write(Range const& range) {
299     lock_guard lock(headers_mutex);
300     if (error_encountered)
301       boost::throw_exception(boost::system::system_error(*error_encountered));
302     auto self = this->shared_from_this();
303     auto f = [this, self](boost::system::error_code ec) { this->default_error(ec); };
304     write_impl(boost::make_iterator_range(range), f);
305   }
306 
307   /**
308    * Writes a given range out and schedules a completion callback to be invoked
309    * when the writes are done. This works similarly to write above.
310    *
311    * This overload is useful for writing streaming applications that send out
312    * chunks of data at a time, or for writing data that may not all fit in
313    * memory at once.
314    *
315    * @param[in] range A Boost.Range ``Single Pass Range`` of char's for writing.
316    * @param[in] callback A function of type `void(boost::system::error_code)`.
317    * @throw boost::system::system_error The encountered underlying error in previous
318    *     operations.
319    * @post Status and headers have been sent, contents in the range have been
320    *     serialized and scheduled for writing through the socket.
321    */
322   template <class Range, class Callback>
323   typename disable_if<
324       is_base_of<boost::asio::const_buffer, typename Range::value_type>, void>::type
writeboost::network::http::async_connection325       write(Range const& range, Callback const& callback) {
326     lock_guard lock(headers_mutex);
327     if (error_encountered)
328       boost::throw_exception(boost::system::system_error(*error_encountered));
329     write_impl(boost::make_iterator_range(range), callback);
330   }
331 
332   /**
333    * Writes a given set of `boost::asio::const_buffer`s out using a more efficient
334    * implementation.
335    *
336    * @param[in] seq A sequence of `boost::asio::const_buffer` objects.
337    * @param[in] callback A function of type `void(boost::system::error_code)`.
338    */
339   template <class ConstBufferSeq, class Callback>
340   typename enable_if<
341       is_base_of<boost::asio::const_buffer, typename ConstBufferSeq::value_type>,
342       void>::type
writeboost::network::http::async_connection343       write(ConstBufferSeq const& seq, Callback const& callback) {
344     write_vec_impl(seq, callback, shared_array_list(), shared_buffers());
345   }
346 
347  private:
348   typedef std::array<char, BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE>
349       buffer_type;
350 
351  public:
352   /// The input range taken by ``read`` callbacks. Typically a range of
353   /// ``char``s.
354   typedef iterator_range<buffer_type::const_iterator> input_range;
355 
356   /// Type required for ``read`` callbacks. Takes an input range, an error
357   /// code, the number of bytes read, and a connection pointer.
358   typedef std::function<void(input_range, boost::system::error_code, std::size_t,
359                              connection_ptr)> read_callback_function;
360 
361   /**
362    * Schedules an asynchronous read from the connection. This is generally
363    * useful for handling POST/PUT or other requests that may have data coming
364    * in through the HTTP request's body in a streaming manner.
365    *
366    * To use this function, the caller needs to provide a callback that handles
367    * a chunk of data at a time. The signature of the function (lambda or actual
368    * function pointer) should be of the following form:
369    *
370    *     void(input_range, error_code, size_t, connection_ptr)
371    *
372    * @param[in] callback Invoked when the read has data ready for processing.
373    * @throw boost::system::system_error The underlying error encountered in previous
374    *     operations.
375    */
readboost::network::http::async_connection376   void read(read_callback_function callback) {
377     if (error_encountered)
378       boost::throw_exception(boost::system::system_error(*error_encountered));
379     if (new_start != read_buffer_.begin()) {
380       input_range input =
381           boost::make_iterator_range(new_start, data_end);
382       buffer_type::iterator start_tmp = new_start;
383       new_start = read_buffer_.begin();
384       auto self = this->shared_from_this();
385       thread_pool().post([this, self, callback, input, start_tmp] {
386         callback(input, {}, std::distance(start_tmp, data_end), self);
387       });
388       return;
389     }
390 
391     auto self = this->shared_from_this();
392     socket().async_read_some(
393         boost::asio::buffer(read_buffer_),
394         strand.wrap([this, self, callback](boost::system::error_code ec,
395                                            size_t bytes_transferred) {
396           this->wrap_read_handler(callback, ec, bytes_transferred);
397         }));
398   }
399 
400   /// Returns a reference to the underlying socket.
socketboost::network::http::async_connection401   boost::network::stream_handler& socket() { return socket_; }
402 
403   /// Returns a reference to the thread_pool running this handler.
thread_poolboost::network::http::async_connection404   utils::thread_pool& thread_pool() { return thread_pool_; }
405 
406   /// Returns whether or not there were errors encountered in previous
407   /// operations.
has_errorboost::network::http::async_connection408   bool has_error() { return (!!error_encountered); }
409 
410   /// Returns the most recent error encountered.
errorboost::network::http::async_connection411   optional<boost::system::system_error> error() { return error_encountered; }
412 
413  private:
wrap_read_handlerboost::network::http::async_connection414   void wrap_read_handler(read_callback_function callback,
415                          boost::system::error_code const& ec,
416                          std::size_t bytes_transferred) {
417     if (ec) error_encountered = in_place<boost::system::system_error>(ec);
418     buffer_type::const_iterator data_start = read_buffer_.begin(),
419                                 data_end = read_buffer_.begin();
420     std::advance(data_end, bytes_transferred);
421     auto range = boost::make_iterator_range(data_start, data_end);
422     auto self = this->shared_from_this();
423     thread_pool().post([callback, range, ec, bytes_transferred, self] {
424       callback(range, ec, bytes_transferred, self);
425     });
426   }
427 
default_errorboost::network::http::async_connection428   void default_error(boost::system::error_code const& ec) {
429     if (ec) error_encountered = in_place<boost::system::system_error>(ec);
430   }
431 
432   typedef std::array<char, BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE>
433       array;
434   typedef std::list<std::shared_ptr<array> > array_list;
435   typedef std::shared_ptr<array_list> shared_array_list;
436   typedef std::shared_ptr<std::vector<boost::asio::const_buffer> > shared_buffers;
437   typedef request_parser<Tag> request_parser_type;
438   typedef std::lock_guard<std::recursive_mutex> lock_guard;
439   typedef std::list<std::function<void()> > pending_actions_list;
440 
441   boost::asio::io_service::strand strand;
442   Handler& handler;
443   utils::thread_pool& thread_pool_;
444   boost::asio::streambuf headers_buffer;
445   boost::network::stream_handler socket_;
446   bool handshake_done;
447   volatile bool headers_already_sent, headers_in_progress;
448 
449   std::recursive_mutex headers_mutex;
450   buffer_type read_buffer_;
451   status_t status;
452   request_parser_type parser;
453   request request_;
454   buffer_type::iterator new_start, data_end;
455   string_type partial_parsed;
456   optional<boost::system::system_error> error_encountered;
457   pending_actions_list pending_actions;
458 
459   template <class, class>
460   friend struct async_server_base;
461 
462   enum state_t { method, uri, version, headers };
463 
startboost::network::http::async_connection464   void start() {
465     boost::system::error_code ec;
466     auto remote_endpoint = socket_.remote_endpoint(ec);
467 
468     if (ec) {
469       error_encountered = in_place<boost::system::system_error>(ec);
470     } else {
471       typename ostringstream<Tag>::type ip_stream;
472       ip_stream << remote_endpoint.address().to_string() << ':'
473                 << remote_endpoint.port();
474       request_.source = ip_stream.str();
475       read_more(method);
476     }
477   }
478 
read_moreboost::network::http::async_connection479   void read_more(state_t state) {
480     auto self = this->shared_from_this();
481 #ifdef BOOST_NETWORK_ENABLE_HTTPS
482     if (socket_.is_ssl_enabled() && !handshake_done) {
483       socket_.async_handshake(boost::asio::ssl::stream_base::server,
484                               [this, self, state](boost::system::error_code ec) {
485                                 handle_handshake(ec, state);
486                               });
487     } else {
488 #endif
489       socket_.async_read_some(
490           boost::asio::buffer(read_buffer_),
491           strand.wrap([this, self, state](boost::system::error_code ec,
492                                           size_t bytes_transferred) {
493             handle_read_data(state, ec, bytes_transferred);
494           }));
495 #ifdef BOOST_NETWORK_ENABLE_HTTPS
496     }
497 #endif
498   }
499 
handle_read_databoost::network::http::async_connection500   void handle_read_data(state_t state, boost::system::error_code const& ec,
501                         std::size_t bytes_transferred) {
502     if (!ec) {
503       logic::tribool parsed_ok;
504       iterator_range<buffer_type::iterator> result_range, input_range;
505       data_end = read_buffer_.begin();
506       std::advance(data_end, bytes_transferred);
507       switch (state) {
508         case method:
509           input_range = boost::make_iterator_range(new_start, data_end);
510           std::tie(parsed_ok, result_range) =
511               parser.parse_until(request_parser_type::method_done, input_range);
512           if (!parsed_ok) {
513             client_error();
514             break;
515           } else if (parsed_ok == true) {
516             swap(partial_parsed, request_.method);
517             request_.method.append(std::begin(result_range),
518                                    std::end(result_range));
519             trim(request_.method);
520             new_start = std::end(result_range);
521           } else {
522             partial_parsed.append(std::begin(result_range),
523                                   std::end(result_range));
524             new_start = read_buffer_.begin();
525             read_more(method);
526             break;
527           }
528         case uri:
529           input_range = boost::make_iterator_range(new_start, data_end);
530           std::tie(parsed_ok, result_range) =
531               parser.parse_until(request_parser_type::uri_done, input_range);
532           if (!parsed_ok) {
533             client_error();
534             break;
535           } else if (parsed_ok == true) {
536             swap(partial_parsed, request_.destination);
537             request_.destination.append(std::begin(result_range),
538                                         std::end(result_range));
539             trim(request_.destination);
540             new_start = std::end(result_range);
541           } else {
542             partial_parsed.append(std::begin(result_range),
543                                   std::end(result_range));
544             new_start = read_buffer_.begin();
545             read_more(uri);
546             break;
547           }
548         case version:
549           input_range = boost::make_iterator_range(new_start, data_end);
550           std::tie(parsed_ok, result_range) = parser.parse_until(
551               request_parser_type::version_done, input_range);
552           if (!parsed_ok) {
553             client_error();
554             break;
555           } else if (parsed_ok == true) {
556             std::tuple<std::uint8_t, std::uint8_t> version_pair;
557             partial_parsed.append(std::begin(result_range),
558                                   std::end(result_range));
559             parse_version(partial_parsed, version_pair);
560             request_.http_version_major = std::get<0>(version_pair);
561             request_.http_version_minor = std::get<1>(version_pair);
562             new_start = std::end(result_range);
563             partial_parsed.clear();
564           } else {
565             partial_parsed.append(std::begin(result_range),
566                                   std::end(result_range));
567             new_start = read_buffer_.begin();
568             read_more(version);
569             break;
570           }
571         case headers:
572           input_range = boost::make_iterator_range(new_start, data_end);
573           std::tie(parsed_ok, result_range) = parser.parse_until(
574               request_parser_type::headers_done, input_range);
575           if (!parsed_ok) {
576             client_error();
577             break;
578           } else if (parsed_ok == true) {
579             partial_parsed.append(std::begin(result_range),
580                                   std::end(result_range));
581             try {
582               parse_headers(partial_parsed, request_.headers);
583             } catch (...) {
584               client_error();
585               break;
586             }
587             new_start = std::end(result_range);
588             auto self = this->shared_from_this();
589             thread_pool().post([self] () { self->handler(self->request_, self); });
590             return;
591           } else {
592             partial_parsed.append(std::begin(result_range),
593                                   std::end(result_range));
594             new_start = read_buffer_.begin();
595             read_more(headers);
596             break;
597           }
598         default:
599           BOOST_ASSERT(false &&
600                        "This is a bug, report to the cpp-netlib devel "
601                        "mailing list!");
602           std::abort();
603       }
604     } else {
605       error_encountered = in_place<boost::system::system_error>(ec);
606     }
607   }
608 
client_errorboost::network::http::async_connection609   void client_error() {
610     static char const bad_request[] =
611         "HTTP/1.0 400 Bad Request\r\nConnection: close\r\nContent-Type: "
612         "text/plain\r\nContent-Length: 12\r\n\r\nBad Request.";
613 
614     auto self = this->shared_from_this();
615     boost::asio::async_write(
616         socket(), boost::asio::buffer(bad_request, strlen(bad_request)),
617         strand.wrap([this, self](boost::system::error_code ec, size_t bytes_transferred) {
618           client_error_sent(ec, bytes_transferred);
619         }));
620   }
621 
client_error_sentboost::network::http::async_connection622   void client_error_sent(boost::system::error_code const& ec, std::size_t) {
623     if (!ec) {
624       boost::system::error_code ignored;
625       socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored);
626       socket().close(ignored);
627     } else {
628       error_encountered = in_place<boost::system::system_error>(ec);
629     }
630   }
631 
write_headers_onlyboost::network::http::async_connection632   void write_headers_only(std::function<void()> callback) {
633     if (headers_in_progress) return;
634     headers_in_progress = true;
635     auto self = this->shared_from_this();
636     boost::asio::async_write(socket(), headers_buffer,
637                       strand.wrap([this, self, callback](
638                           boost::system::error_code ec, size_t bytes_transferred) {
639                         handle_write_headers(callback, ec, bytes_transferred);
640                       }));
641   }
642 
handle_write_headersboost::network::http::async_connection643   void handle_write_headers(std::function<void()> callback,
644                             boost::system::error_code const& ec, std::size_t) {
645     lock_guard lock(headers_mutex);
646     if (!ec) {
647       headers_buffer.consume(headers_buffer.size());
648       headers_already_sent = true;
649       thread_pool().post(callback);
650       auto start = pending_actions.begin(), end = pending_actions.end();
651       while (start != end) {
652         thread_pool().post(*start++);
653       }
654       pending_actions_list().swap(pending_actions);
655     } else {
656       error_encountered = in_place<boost::system::system_error>(ec);
657     }
658   }
659 
handle_writeboost::network::http::async_connection660   void handle_write(std::function<void(boost::system::error_code const&)> callback,
661                     shared_array_list, shared_buffers,
662                     boost::system::error_code const& ec, std::size_t) {
663     // we want to forget the temporaries and buffers
664     thread_pool().post([callback, ec] { callback(ec); });
665   }
666 
667   template <class Range>
write_implboost::network::http::async_connection668   void write_impl(Range range, std::function<void(boost::system::error_code)> callback) {
669     // linearize the whole range into a vector
670     // of fixed-sized buffers, then schedule an asynchronous
671     // write of these buffers -- make sure they are live
672     // by making these linearized buffers shared and made
673     // part of the completion handler.
674     //
675     // once the range has been linearized and sent, schedule
676     // a wrapper to be called in the io_service's thread, that
677     // will re-schedule the given callback into the thread pool
678     // referred to here so that the io_service's thread can concentrate
679     // on doing I/O.
680     //
681 
682     static std::size_t const connection_buffer_size =
683         BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE;
684     shared_array_list temporaries = std::make_shared<array_list>();
685     shared_buffers buffers =
686         std::make_shared<std::vector<boost::asio::const_buffer> >(0);
687 
688     std::size_t range_size = boost::distance(range);
689     buffers->reserve((range_size / connection_buffer_size) +
690                      ((range_size % connection_buffer_size) ? 1 : 0));
691     std::size_t slice_size = std::min(range_size, connection_buffer_size);
692     auto start = std::begin(range), end = std::end(range);
693     while (slice_size != 0) {
694       using boost::adaptors::sliced;
695       std::shared_ptr<array> new_array = std::make_shared<array>();
696       boost::copy(range | sliced(0, slice_size), new_array->begin());
697       temporaries->push_back(new_array);
698       buffers->push_back(boost::asio::buffer(new_array->data(), slice_size));
699       std::advance(start, slice_size);
700       range = boost::make_iterator_range(start, end);
701       range_size = boost::distance(range);
702       slice_size = std::min(range_size, connection_buffer_size);
703     }
704 
705     if (!buffers->empty()) {
706       write_vec_impl(*buffers, callback, temporaries, buffers);
707     }
708   }
709 
710   template <class ConstBufferSeq, class Callback>
write_vec_implboost::network::http::async_connection711   void write_vec_impl(ConstBufferSeq const& seq, Callback const& callback,
712                       shared_array_list temporaries, shared_buffers buffers) {
713     lock_guard lock(headers_mutex);
714     if (error_encountered)
715       boost::throw_exception(boost::system::system_error(*error_encountered));
716     auto self = this->shared_from_this();
717     auto continuation = [this, self, seq, callback, temporaries, buffers] {
718       write_vec_impl(seq, callback, temporaries, buffers);
719     };
720     if (!headers_already_sent && !headers_in_progress) {
721       write_headers_only(continuation);
722       return;
723     }
724     if (headers_in_progress && !headers_already_sent) {
725       pending_actions.push_back(continuation);
726       return;
727     }
728     boost::asio::async_write(
729         socket_, seq, [this, self, callback, temporaries, buffers](
730                           boost::system::error_code ec, size_t bytes_transferred) {
731           handle_write(callback, temporaries, buffers, ec, bytes_transferred);
732         });
733   }
734 
handle_handshakeboost::network::http::async_connection735   void handle_handshake(const boost::system::error_code& ec, state_t state) {
736     if (!ec) {
737       handshake_done = true;
738       read_more(state);
739     } else {
740       error_encountered = in_place<boost::system::system_error>(ec);
741     }
742   }
743 };
744 
745 }  // namespace http
746 }  // namespace network
747 }  // namespace boost
748 
749 #endif  // BOOST_NETWORK_PROTOCOL_HTTP_SERVER_CONNECTION_HPP_20101027
750