/* * Copyright (c) 2011, Peter Thorson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the WebSocket++ Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef WEBSOCKETPP_BROADCAST_ADMIN_HANDLER_HPP #define WEBSOCKETPP_BROADCAST_ADMIN_HANDLER_HPP #include "../../src/sockets/tls.hpp" #include "../../src/websocketpp.hpp" #include "broadcast_handler.hpp" #include #include #include #include namespace websocketpp { namespace broadcast { template class admin_handler : public endpoint_type::handler { public: typedef admin_handler type; typedef boost::shared_ptr ptr; typedef typename endpoint_type::handler_ptr handler_ptr; typedef typename handler::ptr broadcast_handler_ptr; typedef typename endpoint_type::connection_ptr connection_ptr; admin_handler() : m_epoch(boost::posix_time::time_from_string("1970-01-01 00:00:00.000")) {} void on_open(connection_ptr connection) { if (!m_timer) { m_timer.reset(new boost::asio::deadline_timer(connection->get_io_service(),boost::posix_time::seconds(0))); m_timer->expires_from_now(boost::posix_time::milliseconds(250)); m_timer->async_wait(boost::bind(&type::on_timer,this,boost::asio::placeholders::error)); } m_connections.insert(connection); } // this dummy tls init function will cause all TLS connections to fail. // TLS handling for broadcast::handler is usually done by a lobby handler. // If you want to use the broadcast handler alone with TLS then return the // appropriately filled in context here. boost::shared_ptr on_tls_init() { return boost::shared_ptr(); } void on_load(connection_ptr connection, handler_ptr old_handler) { this->on_open(connection); m_lobby = old_handler; } void track(broadcast_handler_ptr target) { m_broadcast_handler = target; } void on_close(connection_ptr connection) { m_connections.erase(connection); } void on_message(connection_ptr connection,websocketpp::message::data_ptr msg) { typename std::set::iterator it; wscmd::cmd command = wscmd::parse(msg->get_payload()); if (command.command == "close") { handle_close(connection,command); } else { command_error(connection,"Invalid Command"); } } void command_error(connection_ptr connection,const std::string msg) { std::string str = "{\"type\":\"error\",\"value\":\""+msg+"\"}"; connection->send(str); } // close: - close this connection // close:all; close all connections void handle_close(connection_ptr connection,const wscmd::cmd& command) { if (!m_broadcast_handler) { // Unable to connect to local broadcast handler return; } m_broadcast_handler->close_connection(connection_ptr()); } long get_ms(boost::posix_time::ptime s) const { boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time(); boost::posix_time::time_period period(s,now); return period.length().total_milliseconds(); } void on_timer(const boost::system::error_code& error) { if (!m_broadcast_handler) { // Unable to connect to local broadcast handler return; } if (m_connections.size() > 0) { long milli_seconds = get_ms(m_epoch); std::stringstream update; update << "{\"type\":\"stats\"" << ",\"timestamp\":" << milli_seconds << ",\"connections\":" << m_broadcast_handler->get_connection_count() << ",\"admin_connections\":" << m_connections.size() << ",\"messages\":["; const msg_map& m = m_broadcast_handler->get_message_stats(); msg_map::const_iterator msg_it; msg_map::const_iterator last = m.end(); if (m.size() > 0) { last--; } for (msg_it = m.begin(); msg_it != m.end(); msg_it++) { update << "{\"id\":" << (*msg_it).second.id << ",\"hash\":\"" << (*msg_it).second.hash << "\"" << ",\"sent\":" << (*msg_it).second.sent << ",\"acked\":" << (*msg_it).second.acked << ",\"size\":" << (*msg_it).second.size << ",\"time\":" << (*msg_it).second.time << "}" << (msg_it == last ? "" : ","); } update << "]}"; m_broadcast_handler->clear_message_stats(); typename std::set::iterator it; websocketpp::message::data_ptr msg = (*m_connections.begin())->get_data_message(); if (msg) { msg->reset(frame::opcode::TEXT); msg->set_payload(update.str()); for (it = m_connections.begin(); it != m_connections.end(); it++) { (*it)->send(msg); } } else { // error no avaliable message buffers } } m_timer->expires_from_now(boost::posix_time::milliseconds(250)); m_timer->async_wait( boost::bind( &type::on_timer, this, boost::asio::placeholders::error ) ); } private: handler_ptr m_lobby; broadcast_handler_ptr m_broadcast_handler; std::set m_connections; boost::posix_time::ptime m_epoch; boost::shared_ptr m_timer; }; } // namespace broadcast } // namespace websocketpp #endif // WEBSOCKETPP_BROADCAST_ADMIN_HANDLER_HPP