/// Connection base state machine void nextOperation() { NetworkOperation netOp = m_connHandler->nextOperation(); switch ( netOp.operation() ) { case NetworkOperation::READ: { LOG_TRACE << "Next operation: READ " << netOp.size() << " bytes from " << identifier(); if ( netOp.buffer() == NULL ) { LOG_FATAL << "Attempt to READ from " << identifier() << " to a NULL data block"; abort(); // here should be a system exception } if ( netOp.size() == 0 ) { LOG_FATAL << "Attempt to READ 0 bytes data block from " << identifier(); abort(); // here should be a system exception } if ( netOp.timeout() > 0 ) setTimeout( netOp.timeout()); m_readBuffer = netOp.buffer(); socket().async_read_some( boost::asio::buffer( m_readBuffer, netOp.size() ), m_strand.wrap( boost::bind( &ConnectionBase::handleRead, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred ))); break; } case NetworkOperation::WRITE: { LOG_TRACE << "Next operation: WRITE " << netOp.size() << " bytes to " << identifier(); if ( netOp.data() == NULL ) { LOG_FATAL << "Attempt to WRITE a NULL data block to " << identifier(); abort(); // here should be a system exception } if ( netOp.size() == 0 ) { LOG_FATAL << "Attempt to WRITE a 0 bytes data block to " << identifier(); abort(); // here should be a system exception } if ( netOp.timeout() > 0 ) setTimeout( netOp.timeout()); boost::asio::async_write( socket(), boost::asio::buffer( netOp.data(), netOp.size() ), m_strand.wrap( boost::bind( &ConnectionBase::handleWrite, this->shared_from_this(), boost::asio::placeholders::error ))); break; } case NetworkOperation::CLOSE: { LOG_TRACE << "Next operation: CLOSE connection to " << identifier(); // Initiate graceful connection closure. setTimeout( 0 ); unregister(); m_strand.post( boost::bind( &ConnectionBase::handleShutdown, this->shared_from_this() )); break; } case NetworkOperation::NOOP: LOG_TRACE << "Next operation: NOOP on connection to " << identifier(); break; } }
void justdoit() { rc_->get(strand_.wrap( boost::bind(&tormoz_get::handle_done, shared_from_this(), _1, _2)) ); timer_.expires_from_now(boost::posix_time::milliseconds(100)); timer_.async_wait(strand_.wrap(boost::bind(&tormoz_get::handle_timeout, shared_from_this(), _1))); }
void do_resolve (std::vector <std::string> const& names, HandlerType const& handler, CompletionCounter) { check_precondition (! names.empty()); if (m_called_stop.load () == 0) { // TODO NIKB use emplace_back once we move to C++11 m_work.push_back(Work(names, handler)); m_journal.debug << "Queued new job with " << names.size() << " tasks. " << m_work.size() << " jobs outstanding."; if (m_work.size() == 1) { check_precondition (m_idle); m_journal.trace << "Waking up"; m_idle = false; m_io_service.post (m_strand.wrap (boost::bind ( &NameResolverImpl::do_work, this, CompletionCounter(this)))); } } }
// Called when the timer expires. // We operate the timer continuously this simplifies the code. // void on_timer(error_code ec) { if(ec && ec != boost::asio::error::operation_aborted) return fail("timer", ec); // Verify that the timer really expired // since the deadline may have moved. // if(timer_.expires_at() <= clock_type::now()) { // Closing the socket cancels all outstanding // operations. They will complete with // boost::asio::error::operation_aborted // ws_.next_layer().close(ec); return; } // Wait on the timer timer_.async_wait( strand_.wrap(std::bind( &connection::on_timer, shared_from_this(), std::placeholders::_1))); }
void do_finish ( std::string name, boost::system::error_code const& ec, HandlerType handler, boost::asio::ip::tcp::resolver::iterator iter, CompletionCounter) { if (ec == boost::asio::error::operation_aborted) return; std::vector <beast::IP::Endpoint> addresses; // If we get an error message back, we don't return any // results that we may have gotten. if (!ec) { while (iter != boost::asio::ip::tcp::resolver::iterator()) { addresses.push_back (beast::IPAddressConversion::from_asio (*iter)); ++iter; } } handler (name, addresses); m_io_service.post (m_strand.wrap (std::bind ( &ResolverAsioImpl::do_work, this, CompletionCounter (this)))); }
void stop_async () override { if (m_stop_called.exchange (true) == false) { m_io_service.dispatch (m_strand.wrap (std::bind ( &ResolverAsioImpl::do_stop, this, CompletionCounter (this)))); JLOG(m_journal.debug()) << "Queued a stop request"; } }
void stop_async () { if (m_called_stop.exchange (1) == 0) { m_io_service.dispatch (m_strand.wrap (boost::bind ( &NameResolverImpl::do_stop, this, CompletionCounter (this)))); m_journal.debug << "Stopping"; } }
// Read a message from the websocket stream void do_read() { // Put the read on the timer timer_.expires_from_now(std::chrono::seconds(15)); // Read a message ws_.async_read(buffer_, strand_.wrap(std::bind( &connection::on_read, shared_from_this(), std::placeholders::_1))); }
void resolve ( std::vector <std::string> const& names, HandlerType const& handler) { check_precondition (m_called_stop.load () == 0); check_precondition (!names.empty()); // TODO NIKB use rvalue references to construct and move // reducing cost. m_io_service.dispatch (m_strand.wrap (boost::bind ( &NameResolverImpl::do_resolve, this, names, handler, CompletionCounter(this)))); }
inline void connection::handle_read(const boost::system::error_code& e, std::size_t bytes_transferred) { if (!e) { boost::tribool result; boost::tie(result, boost::tuples::ignore) = request_parser_.parse( *request_, buffer_.data(), buffer_.data() + bytes_transferred); if (result) { try { request_handler_.handle_request(*request_, reply_); } catch (webserver::reply rep) { reply_ = rep; } std::cerr << reply_.content << std::endl; boost::asio::async_write(socket_, reply_.to_buffers(), strand_.wrap( boost::bind(&connection::handle_write, shared_from_this(), boost::asio::placeholders::error))); } else if (!result) { reply_ = reply::stock_reply(reply::bad_request); boost::asio::async_write(socket_, reply_.to_buffers(), strand_.wrap( boost::bind(&connection::handle_write, shared_from_this(), boost::asio::placeholders::error))); } else { socket_.async_read_some(boost::asio::buffer(buffer_), strand_.wrap( boost::bind(&connection::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); } } // If an error occurs then no new asynchronous operations are started. This // means that all shared_ptr references to the connection object will // disappear and the object will be destroyed automatically after this // handler returns. The connection class's destructor closes the socket. }
void resolve ( std::vector <std::string> const& names, HandlerType const& handler) override { assert (m_stop_called == false); assert (m_stopped == true); assert (!names.empty()); // TODO NIKB use rvalue references to construct and move // reducing cost. m_io_service.dispatch (m_strand.wrap (std::bind ( &ResolverAsioImpl::do_resolve, this, names, handler, CompletionCounter (this)))); }
/// Set / reset the timeout timer void setTimeout( unsigned timeout ) { if ( timeout == 0 ) { LOG_TRACE << "Timeout for connection to " << identifier() << " reset"; m_timer.cancel(); } else { m_timer.cancel(); m_timer.expires_from_now( boost::posix_time::seconds( timeout )); m_timer.async_wait( m_strand.wrap( boost::bind( &ConnectionBase::handleTimeout, this->shared_from_this(), boost::asio::placeholders::error ))); LOG_TRACE << "Timeout for connection to " << identifier() << " set to " << timeout << "s"; } }
WSClientImpl(Config const& cfg, bool v2) : work_(ios_) , strand_(ios_) , thread_([&] { ios_.run(); }) , stream_(ios_) , ws_(stream_) { auto const ep = getEndpoint(cfg, v2); stream_.connect(ep); ws_.handshake(ep.address().to_string() + ":" + std::to_string(ep.port()), "/"); ws_.async_read(op_, rb_, strand_.wrap(std::bind(&WSClientImpl::on_read_msg, this, beast::asio::placeholders::error))); }
void handle_timeout(const boost::system::error_code& ec) { if (--count_ > 0) { rc_->stop(); rc_.reset( new rc_check(timer_.io_service(), std::string(p_.pp.login).append("@").append(p_.pp.domain), p_.pp.ukey, p_.l, 1) ); timer_.expires_from_now(boost::posix_time::milliseconds(rand_r(&seed_) % 500)); timer_.async_wait(strand_.wrap(boost::bind(&tormoz_get::handle_timeout, shared_from_this(), _1))); } else if (rc_) { rc_->stop(); rc_.reset(); } }
void onTimer (boost::system::error_code ec) { if (ec) { if (ec != boost::asio::error::operation_aborted) journal_.error << "onTimer: " << ec.message(); return; } logic_.onTimer(); timer_.expires_from_now(std::chrono::seconds(1), ec); timer_.async_wait(strand_.wrap(exec_.wrap( std::bind(&ManagerImp::onTimer, this, beast::asio::placeholders::error)))); }
void do_work (CompletionCounter) { if (m_called_stop.load () == 1) return; // We don't have any work to do at this time if (m_work.empty()) { m_idle = true; m_journal.trace << "Sleeping"; return; } if (m_work.front().names.empty()) m_work.pop_front(); std::string const name (m_work.front().names.back()); HandlerType handler (m_work.front().handler); m_work.front().names.pop_back(); HostAndPort const hp (parseName(name)); if (hp.first.empty()) { m_journal.error << "Unable to parse '" << name << "'"; m_io_service.post (m_strand.wrap (boost::bind ( &NameResolverImpl::do_work, this, CompletionCounter(this)))); return; } boost::asio::ip::tcp::resolver::query query ( hp.first, hp.second); m_resolver.async_resolve (query, boost::bind ( &NameResolverImpl::do_finish, this, name, boost::asio::placeholders::error, handler, boost::asio::placeholders::iterator, CompletionCounter(this))); }
// Called immediately after the connection is created. // We keep this separate from the constructor because // shared_from_this may not be called from constructors. void run() { // Run the timer on_timer({}); // Put the handshake on the timer timer_.expires_from_now(std::chrono::seconds(15)); // Read the websocket handshake and send the response ws_.async_accept_ex( [](websocket::response_type& res) { res.insert(http::field::server, "websocket-server-async"); }, strand_.wrap(std::bind( &connection::on_accept, shared_from_this(), std::placeholders::_1))); }
void on_read_msg(error_code const& ec) { if(ec) return; Json::Value jv; Json::Reader jr; jr.parse(buffer_string(rb_.data()), jv); rb_.consume(rb_.size()); auto m = std::make_shared<msg>( std::move(jv)); { std::lock_guard<std::mutex> lock(m_); msgs_.push_front(m); cv_.notify_all(); } ws_.async_read(op_, rb_, strand_.wrap( std::bind(&WSClientImpl::on_read_msg, this, beast::asio::placeholders::error))); }
// Called after the message read completes void on_read(error_code ec) { // This error means the other side // closed the websocket stream. if(ec == websocket::error::closed) return; if(ec) return fail("read", ec); // Put the write on the timer timer_.expires_from_now(std::chrono::seconds(15)); // Write the received message back ws_.binary(ws_.got_binary()); ws_.async_write(buffer_.data(), strand_.wrap(std::bind( &connection::on_write, shared_from_this(), std::placeholders::_1))); }
void do_resolve (std::vector <std::string> const& names, HandlerType const& handler, CompletionCounter) { assert (! names.empty()); if (m_stop_called == false) { m_work.emplace_back (names, handler); JLOG(m_journal.debug()) << "Queued new job with " << names.size() << " tasks. " << m_work.size() << " jobs outstanding."; if (m_work.size() > 0) { m_io_service.post (m_strand.wrap (std::bind ( &ResolverAsioImpl::do_work, this, CompletionCounter (this)))); } } }
void do_work (CompletionCounter) { if (m_stop_called == true) return; // We don't have any work to do at this time if (m_work.empty ()) return; std::string const name (m_work.front ().names.back()); HandlerType handler (m_work.front ().handler); m_work.front ().names.pop_back (); if (m_work.front ().names.empty ()) m_work.pop_front(); HostAndPort const hp (parseName (name)); if (hp.first.empty ()) { JLOG(m_journal.error()) << "Unable to parse '" << name << "'"; m_io_service.post (m_strand.wrap (std::bind ( &ResolverAsioImpl::do_work, this, CompletionCounter (this)))); return; } boost::asio::ip::tcp::resolver::query query ( hp.first, hp.second); m_resolver.async_resolve (query, std::bind ( &ResolverAsioImpl::do_finish, this, name, std::placeholders::_1, handler, std::placeholders::_2, CompletionCounter (this))); }
inline void connection::start() { socket_.async_read_some(boost::asio::buffer(buffer_), strand_.wrap( boost::bind(&connection::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); }