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