
2015-08-13 18:38 GMT+03:00 VinÃcius dos Santos Oliveira < vini.ipsmaker@gmail.com>:
This idea is horrible. An HTTP message is not a std::string. HTTP does have a string representation, the HTTP wire format from HTTP 1.1, which is different than HTTP 2.0, which is different than FastCGI, which would be different than a ZeroMQ-based approach and so on and so on.
You've totally misunderstood the example. Let's take a look at it: 1) For simplicity let's assume that we are working with HTTP1.1 and HTTP1.0 only. In that case: namespace http { typedef boost::asio::tcp::tcp::socket socket; typedef boost::asio::tcp::tcp::acceptor acceptor; } 2) Here's the part that takes care of communications only: #include <ctime> #include <iostream> #include <string> #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/asio.hpp> std::string make_daytime_string() { using namespace std; // For time_t, time and ctime; time_t now = time(0); return ctime(&now); } class http_connection : public boost::enable_shared_from_this<http_connection> { public: typedef boost::shared_ptr<http_connection> pointer; static pointer create(boost::asio::io_service& io_service) { return pointer(new http_connection(io_service)); } http::socket& socket() { return socket_; } void start() { message_.resize(4 * 1024); boost::asio::async_read(socket_, boost::asio::buffer(message_), http::completions::full(message_), // read until all the whole request is in `message_` boost::bind(&http_connection::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } private: http_connection(boost::asio::io_service& io_service) : socket_(io_service) {} void handle_write(const boost::system::error_code& /*error*/, size_t /*bytes_transferred*/) {} void handle_read(const boost::system::error_code& error, size_t bytes_transferred); http::socket socket_; // manages the connection only std::string message_; }; class http_server { public: http_server(boost::asio::io_service& io_service) : acceptor_(io_service, http::endpoint(http::all_versions, tcp::v4(), 80)) { start_accept(); } private: void start_accept() { http_connection::pointer new_connection = http_connection::create(acceptor_.get_io_service()); acceptor_.async_accept(new_connection->socket(), boost::bind(&http_server::handle_accept, this, new_connection, boost::asio::placeholders::error)); } void handle_accept(http_connection::pointer new_connection, const boost::system::error_code& error) { if (!error) { new_connection->start(); } start_accept(); } http::acceptor acceptor_; }; int main() { try { boost::asio::io_service io_service; http_server server(io_service); io_service.run(); } catch (std::exception& e) { std::cerr << e.what() << std::endl; } return 0; } Assuming that http::socket is a tcp::socket, that example is EXACTLY the same code that is in ASIO example at http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/tutorial/tutdaytime... message_ is just a BUFFER for data. You can use std::vector<char>, std::vector<unsigned char> or std::array<> here. It's not a string, it's the buffer! http::completions::full(message_) is a functor, that returns 'Stop reading data' when our buffer (message_) contains the whole HTTP request. So, what does it mean? It means that when http_connection::handle_read function is called, message_ contains whole data received from socket. 3) http::view NEWER downloads data. http::view is a parser, that represents an input data as an HTTP message and provides a user friendly collection of methods to READ data. It means that it could be used just like this: const char data[] = "GET /wiki/HTTP HTTP/1.0\r\n" "Host: ru.wikipedia.org\r\n\r\n" ; http::view v(data); assert(v.version() == "HTTP1.0"); You've been mislead by http::view::read_state() function. It does not investigate sockets. It determinates the state from the message content: const char data1[] = "GET /wiki/HTTP HTTP/1.0\r\n" "Content-Length: 0\r\n" "Host: ru.wikipedia.org\r\n\r\n" ; http::view v1(data1); assert(v1.read_state() == http::read_state::empty); const char data2[] = "GET /wiki/HTTP HTTP/1.0\r\n" "Content-Length: 12312310\r\n" "Host: ru.wikipedia.org\r\n\r\n" "Hello word! This is a part of the message, because it is not totally recei" ; http::view v2(data2); assert(v2.read_state() != http::read_state::empty); 4) Why this approach is better? * It explicitly allows user to manage networking and memory. * It separates work with network and work with HTTP message. * It reuses ASIO interface * It does not implicitly allocates memory * It can be used separately from ASIO. http::view and http::generator do not use ASIO at all. * Your assumption that there is always a requirement to read headers and body separately is very wrong. HTTP headers are not so big and usually the whole HTTP message could be accepted by a single read. So when you force user to read headers and body separately you're forcing user to have more system calls/locks/context switches. However read with http::completions::full(message_) in most cases will result in a single read inside ASIO and a single call to http::completions::full::operator(). This will result in better performance. * If there's a user requirement to read headers separately from body, this could be achieved by http::completions::body + http::completions::headers. * Want to take care of memory and networking in Boost.HTTP library just like cpp-netlib does? That's simple, make a wrapper for beginners: struct handler { // this handler is wirtten by user void operator() (http::view const &request, http::generator &response) { response = http::response::stock_reply( http_server::response::ok, "Hello, world!"); } void log(std::string const &info) { std::cerr << "ERROR: " << info << '\n'; } }; http_connection::handle_read(const boost::system::error_code& error, size_t bytes_transferred) { if (error) { user_handler.log(error.message()); // user provided `struct handler` return; } std::vector<char> response_holder(1024); { boost::system::error_code e; http::view v(message_, e); // represent buffer as http message http::generator response(response_holder); user_handler(v, response); // user provided `struct handler` } boost::asio::async_write(socket_, boost::asio::buffer(response_holder), boost::bind(&http_connection::handle_write, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } The `struct handler` above is taken from cpp-netlib example: http://cpp-netlib.org/0.11.1/index.html To sum up. If you're attempting to compete with cpp-netlib, then you've started wrong: * cpp-netlib is very simple to use. If you always require io_service and coroutines then your library is hard to use. * headers/body separate reads is not what really required * no advantages over cpp-netlib If I've missed some advantages of your library - please highlight them. How to fix things? * Provide more functionality than cpp-netlib: * Allow users to manipulate memory and networking. * Untie the library from networking and allow parts of it to be used on raw data (http::view/http::generate). * ASIO interfaces re-usage and simple migration for users that already use ASIO for HTTP. (tcp::socket -> http::socket) * HTTP2.0 ? * Simple interface for beginners. If your first example consumes two screens of text, while cpp-netlib's example consumes 0.5 screen - you'll loose -- Best regards, Antony Polukhin