c++ - boost::asio read on socket to server blocks all incoming connection on client socket to be accepted -
i'm quite new boost::asio, writing http proxy (at moment testing on windows 7, boost 1.52.0 ). faced behavior don't understand please me it.
i'm using synchronios functions in current implementation. here server class:
#include "server.h" namespace proxy { sync_accept_server::sync_accept_server(const std::string& address, int port) : _io_service(), _acceptor(_io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port )), _connection_id(0), _new_connection() { _acceptor.set_option(boost::asio::ip::tcp::no_delay(true)); } void sync_accept_server::start_accept() { boost::system::error_code error; // wait incoming connections (;;) { // create new connection //_new_connection.reset(new connection(_p_io_service_pool->get_io_service(), ++_connection_id)); _new_connection.reset(new sync_connection(++_connection_id)); std::cout << "start waiting incoming connection" << std::endl; _acceptor.accept(_new_connection->get_socket(), error); if ( !error ) { // create new thread boost::thread new_thread(boost::bind(&sync_connection::start, _new_connection)); new_thread.detach(); //_new_connection->start(); std::cout << " new connection started, id: " << _connection_id << std::endl; } else { std::cout << "error during incoming connection acceptance: " << error.message(); } } } } // namespace proxy
here connection class reads headers browser, setting connection server, sends data server host, reads response , write data browser
#include <boost/assert.hpp> #include <boost/lexical_cast.hpp> #include "sync_connection.h" namespace { std::string streambuf_to_string(boost::asio::streambuf &buf) { std::ostringstream string_stream; string_stream << &buf; return std::string(string_stream.str()); } } // anonym namespace namespace proxy { sync_connection::sync_connection(int id) : _io_service(), _server_io_service(), _bsocket(_io_service), _bbuffer(), _ssocket(_server_io_service), _sbuffer(), _resolver(_server_io_service), _streambuf(), _headers(), _content_length(-1), _was_read(0), _id(id), _new_url(), end_sign("\r\n\r\n") { std::cout << "connection created, id: " << _id << std::endl; } void sync_connection::start() { boost::system::error_code error; // read headers browser socket // note functoin read more data return number of bytes , including end_sign std::size_t len = boost::asio::read_until(_bsocket, _streambuf, end_sign, error); boost_assert(!error); // conver sream buf string - parse _headers = streambuf_to_string(_streambuf); // parse headers _request.feed(_headers.c_str(), len); // start connect server start_connect(); } void sync_connection::start_connect() { // parse url http::url url(_request.url()); std::string port = url.port().empty() ? "80" : url.port(); std::string server = url.host(); // build new url _new_url = url.path(); std::string query = url.query(); if ( !query.empty() ) { _new_url += ( "?" + query ); } // host has '/' sign @ begining of string might cos // connection troubles server = server.substr(1, server.npos); boost_assert(!server.empty()); if ( server.empty() ) { return; } std::cout << _id << " server: " << server << std::endl; boost::system::error_code error; // create query able resolve hostname ip , port boost::asio::ip::tcp::resolver::query resolver_query(server, port); // resolve list of enpoints boost::asio::ip::tcp::resolver::iterator endpoint_iterator = _resolver.resolve(resolver_query, error); boost_assert(!error); // till not connected ot out of enpoints bool is_connected = false; while ( endpoint_iterator != boost::asio::ip::tcp::resolver::iterator() && !is_connected ) { // let's try connect here _ssocket.connect(*endpoint_iterator, error); //( error ) ? ( ++endpoint_iterator ) : ( is_connected = true ); if ( error ) { std::cout << _id << " error occurs: " << error.message() << " try new endpoint" << std::endl; ++endpoint_iterator; } else { is_connected = true; } } // raise exception in case did not manage connect server if ( !is_connected ) { std::cout << _id << " did not manage connect server, error: " << error.message() << std::endl; boost_assert(false); } else { std::cout << _id << " connected now!" << std::endl; _ssocket.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); start_write_to_server(); } } void sync_connection::start_write_to_server() { // prepare request send server std::string _proxy_request = _request.method_name(); _proxy_request += " "; _proxy_request += _new_url; _proxy_request += " http/"; _proxy_request += "1.1"; // todo - have protocol version requiest _proxy_request += "\r\n"; // end of request line // other headers should same in browser request _proxy_request += _headers; boost::system::error_code error; // can send data boost::asio::write(_ssocket, boost::asio::buffer(_proxy_request), error); boost_assert_msg(!error, error.message().c_str()); _headers.clear(); // read response start_read_from_server(); } void sync_connection::start_read_from_server() { // lets read headers @ begining // clean streambuf since contain end_sign _streambuf.consume(_streambuf.size()); boost::system::error_code error; std::size_t len = boost::asio::read_until(_ssocket, _streambuf, end_sign, error); boost_assert_msg(!error, error.message().c_str()); // headers _headers = streambuf_to_string(_streambuf); boost_assert_msg(!_headers.empty(), "empty response server, went wrong"); // parse response server _response.feed(_headers.c_str(), len); std::cout << "headers:\n" << _headers << std::endl; // check content-length header if ( _response.has_header("content-length") ) { std::cout << _id << " content-length present" << std::endl; _content_length = boost::lexical_cast<int>(_response.header("content-length")); } std::cout << _id << " content length " << _response.has_header("content-length") << std::endl; // since read_until read more data till end_sign // need calculate how many bytes of body read // if there bunch of data after end_sign if ( len < _headers.size() ) { _was_read = _headers.size() - len; } start_write_to_browser(); } void sync_connection::start_write_to_browser() { // write response browser boost::system::error_code error_server; boost::system::error_code error_browser; boost::asio::write(_bsocket, boost::asio::buffer(_headers), error_browser); boost_assert_msg(!error_browser, error_browser.message().c_str()); // need read server body ? std::cout << _id << " content-lenght: " << _content_length << std::endl; std::size_t len = 0; if ( _content_length == -1 || _was_read < _content_length ) { bool done = false; { std::cout << _id << " start reading" << std::endl; len = _ssocket.read_some(boost::asio::buffer(_sbuffer), error_server); _was_read += len; std::cout << _id << " bytes read: " << _was_read << std::endl; boost::asio::write(_bsocket, boost::asio::buffer(_sbuffer, len), error_browser); boost_assert_msg( !error_browser, error_browser.message().c_str() ); if ( error_server ) { std::cout << _id << "error, stop reading: " << error_server.message() << std::endl; break; } std::cout << _id << " data written browser" << std::endl; } while ( _content_length == -1 || _was_read < _content_length ); } std::cout << _id << " done connection, close sockets" << std::endl; shutdown(); } void sync_connection::shutdown() { _ssocket.close(); _bsocket.close(); } } // namespace proxy
the problem when content-length not present in server response ( chunked transfer encoding ) - in case don't know how many data should read browser, , call read_some method when there no more data on socket. call hangs ~10-15 sec till connection closed server (eof). in general i'm fine behavior.
but problem last read_some call on server socket blocks new incoming connection browser accepted acceptor. new connections not accepted till read_some on server socket return eof.
i've done lot of experements already, , interesting thing same blocking call on browser socket not block other incoming connections accepted. if call
_bsocket.read_some(...)
and there no data read , server keeping connection - new incoming connections accepted fine.
i can't understand why work in way, don't have relations between server , browser socket in implementation, initialized different io_service objects.
is there wrong in implementation? i'm facing same issue async functions. tips helpful.
Comments
Post a Comment