From c0fc7730719133a4aa123547969bf6d5399e77d3 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Sat, 10 Mar 2012 11:06:59 -0600 Subject: [PATCH] adds welcome message to wsperf, lots of wsperf cleanup --- examples/wsperf/SConscript | 3 +- examples/wsperf/case.cpp | 236 ++++++++++++++++++++ examples/wsperf/case.hpp | 208 ++--------------- examples/wsperf/generic.cpp | 114 ++++++++++ examples/wsperf/generic.hpp | 104 +-------- examples/wsperf/request.cpp | 72 ++++++ examples/wsperf/request.hpp | 90 ++------ examples/wsperf/{autobahn.cpp => wscmd.cpp} | 46 +++- examples/wsperf/wscmd.hpp | 38 ---- examples/wsperf/wsperf.cpp | 28 +-- websocketpp.xcodeproj/project.pbxproj | 8 +- 11 files changed, 524 insertions(+), 423 deletions(-) rename examples/wsperf/{autobahn.cpp => wscmd.cpp} (60%) diff --git a/examples/wsperf/SConscript b/examples/wsperf/SConscript index 7bc172397c..f3b5760f22 100644 --- a/examples/wsperf/SConscript +++ b/examples/wsperf/SConscript @@ -11,7 +11,8 @@ localenv = env.Clone () sources = ["wsperf.cpp", "request.cpp", "case.cpp", - "generic.cpp"] + "generic.cpp", + "wscmd.cpp"] LIBS = [wslib, platform_libs] + boostlibs(['system', 'date_time', diff --git a/examples/wsperf/case.cpp b/examples/wsperf/case.cpp index 65d401e974..f7a48e1073 100644 --- a/examples/wsperf/case.cpp +++ b/examples/wsperf/case.cpp @@ -24,3 +24,239 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + +#include "case.hpp" + +using wsperf::case_handler; + +// Construct a message_test from a wscmd command +/* Reads values from the wscmd object into member variables. The cmd object is + * passed to the parent constructor for extracting values common to all test + * cases. + * + * Any of the constructors may throw a `case_exception` if required parameters + * are not found or default values don't make sense. + * + * Values that message_test checks for: + * + * uri=[string]; + * Example: uri=ws://localhost:9000; + * URI of the server to connect to + * + * token=[string]; + * Example: token=foo; + * String value that will be returned in the `token` field of all test related + * messages. A separate token should be sent for each unique test. + * + * quantile_count=[integer]; + * Example: quantile_count=10; + * How many histogram quantiles to return in the test results + * + * rtts=[bool]; + * Example: rtts:true; + * Whether or not to return the full list of round trip times for each message + * primarily useful for debugging. + */ +case_handler::case_handler(wscmd::cmd& cmd) { + m_uri = extract_string(cmd,"uri"); + m_token = extract_string(cmd,"token"); + m_quantile_count = extract_number(cmd,"quantile_count"); + m_rtts = extract_bool(cmd,"rtts"); + m_bytes = 0; + m_pass = RUNNING; +} + +/// Starts a test by starting the timeout timer and marking the start time +void case_handler::start(connection_ptr con, uint64_t timeout) { + m_timer.reset(new boost::asio::deadline_timer( + con->get_io_service(), + boost::posix_time::seconds(0)) + ); + m_timer->expires_from_now(boost::posix_time::milliseconds(timeout)); + m_timer->async_wait( + boost::bind( + &type::on_timer, + this, + con, + boost::asio::placeholders::error + ) + ); + + m_start = boost::chrono::steady_clock::now(); +} + +/// Marks an incremental time point +void case_handler::mark() { + m_end.push_back(boost::chrono::steady_clock::now()); +} + +/// Ends a test +/* Ends a test by canceling the timeout timer, marking the end time, generating + * statistics and closing the websocket connection. + */ +void case_handler::end(connection_ptr con) { + std::vector avgs; + avgs.resize(m_quantile_count, 0); + + std::vector quantiles; + quantiles.resize(m_quantile_count, 0); + + double avg = 0; + double stddev = 0; + double total = 0; + double seconds = 0; + + // TODO: handle weird sizes and error conditions better + if (m_end.size() > m_quantile_count) { + boost::chrono::steady_clock::time_point last = m_start; + + // convert RTTs to microsecs + // + + std::vector::iterator it; + for (it = m_end.begin(); it != m_end.end(); ++it) { + boost::chrono::nanoseconds dur = *it - last; + m_times.push_back(static_cast (dur.count()) / 1000.); + last = *it; + } + + std::sort(m_times.begin(), m_times.end()); + + size_t samples_per_quantile = m_times.size() / m_quantile_count; + + // quantiles + for (size_t i = 0; i < m_quantile_count; ++i) { + quantiles[i] = m_times[((i + 1) * samples_per_quantile) - 1]; + } + + // total average and quantile averages + for (size_t i = 0; i < m_times.size(); ++i) { + avg += m_times[i]; + avgs[i / samples_per_quantile] + += m_times[i] / static_cast(samples_per_quantile); + } + + avg /= static_cast (m_times.size()); + + // standard dev corrected for estimation from sample + for (size_t i = 0; i < m_times.size(); ++i) { + stddev += (m_times[i] - avg) * (m_times[i] - avg); + } + + // Bessel's correction + stddev /= static_cast (m_times.size() - 1); + stddev = std::sqrt(stddev); + } else { + m_times.push_back(0); + } + + boost::chrono::nanoseconds total_dur = m_end[m_end.size()-1] - m_start; + total = static_cast (total_dur.count()) / 1000.; // microsec + seconds = total / 10000000.; + + std::stringstream s; + std::string outcome; + + switch (m_pass) { + case FAIL: + outcome = "fail"; + break; + case PASS: + outcome = "pass"; + break; + case TIME_OUT: + outcome = "time_out"; + break; + case RUNNING: + throw case_exception("end() called from RUNNING state"); + break; + } + + s << "{\"result\":\"" << outcome + << "\",\"min\":" << m_times[0] + << ",\"max\":" << m_times[m_times.size()-1] + << ",\"median\":" << m_times[(m_times.size()-1)/2] + << ",\"avg\":" << avg + << ",\"stddev\":" << stddev + << ",\"total\":" << total + << ",\"bytes\":" << m_bytes + << ",\"quantiles\":["; + + for (size_t i = 0; i < m_quantile_count; i++) { + s << (i > 0 ? "," : ""); + s << "["; + s << avgs[i] << "," << quantiles[i]; + s << "]"; + } + s << "]"; + + if (m_rtts) { + s << ",\"rtts\":["; + for (size_t i = 0; i < m_times.size(); i++) { + s << (i > 0 ? "," : "") << m_times[i]; + } + s << "]"; + }; + s << "}"; + + m_data = s.str(); + + con->close(websocketpp::close::status::NORMAL,""); +} + +/// Fills a buffer with utf8 bytes +void case_handler::fill_utf8(std::string& data,size_t size,bool random) { + if (random) { + uint32_t val; + for (size_t i = 0; i < size; i++) { + if (i%4 == 0) { + val = uint32_t(rand()); + } + + data.push_back(char(((reinterpret_cast(&val)[i%4])%95)+32)); + } + } else { + data.assign(size,'*'); + } +} + +/// Fills a buffer with bytes +void case_handler::fill_binary(std::string& data,size_t size,bool random) { + if (random) { + int32_t val; + for (size_t i = 0; i < size; i++) { + if (i%4 == 0) { + val = rand(); + } + + data.push_back((reinterpret_cast(&val))[i%4]); + } + } else { + data.assign(size,'*'); + } +} + +void case_handler::on_timer(connection_ptr con,const boost::system::error_code& error) { + if (error == boost::system::errc::operation_canceled) { + return; // timer was canceled because test finished + } + + // time out + mark(); + m_pass = TIME_OUT; + this->end(con); +} + +void case_handler::on_fail(connection_ptr con) { + m_data = "{\"result\":\"connection_failed\"}"; +} + +const std::string& case_handler::get_data() const { + return m_data; +} +const std::string& case_handler::get_token() const { + return m_token; +} +const std::string& case_handler::get_uri() const { + return m_uri; +} diff --git a/examples/wsperf/case.hpp b/examples/wsperf/case.hpp index 4926ff74ae..97947d55fd 100644 --- a/examples/wsperf/case.hpp +++ b/examples/wsperf/case.hpp @@ -57,179 +57,29 @@ class case_handler : public client::handler { public: typedef case_handler type; - void start(connection_ptr con, uint64_t timeout) { - m_timer.reset(new boost::asio::deadline_timer( - con->get_io_service(), - boost::posix_time::seconds(0)) - ); - m_timer->expires_from_now(boost::posix_time::milliseconds(timeout)); - m_timer->async_wait( - boost::bind( - &type::on_timer, - this, - con, - boost::asio::placeholders::error - ) - ); - - m_start = boost::chrono::steady_clock::now(); - m_bytes = 0; - m_pass = RUNNING; - } + /// Construct a message test from a wscmd command + explicit case_handler(wscmd::cmd& cmd); - void mark() { - m_end.push_back(boost::chrono::steady_clock::now()); - } + void start(connection_ptr con, uint64_t timeout); + void end(connection_ptr con); - void end(connection_ptr con) { - /*uint64_t avg = 0; - std::vector avgs(10); - double squaresum = 0; - uint64_t stddev = 0; - int64_t total = 0; - double seconds = 0;*/ - - std::vector avgs; - avgs.resize(m_quantile_count, 0); - - std::vector quantiles; - quantiles.resize(m_quantile_count, 0); - - double avg = 0; - double stddev = 0; - double total = 0; - double seconds = 0; - - // TODO: handle weird sizes and error conditions better - if (m_end.size() > m_quantile_count) { - boost::chrono::steady_clock::time_point last = m_start; - - // convert RTTs to microsecs - // - - std::vector::iterator it; - for (it = m_end.begin(); it != m_end.end(); ++it) { - boost::chrono::nanoseconds dur = *it - last; - m_times.push_back(static_cast (dur.count()) / 1000.); - last = *it; - } - - std::sort(m_times.begin(), m_times.end()); - - size_t samples_per_quantile = m_times.size() / m_quantile_count; - - // quantiles - for (size_t i = 0; i < m_quantile_count; ++i) { - quantiles[i] = m_times[((i + 1) * samples_per_quantile) - 1]; - } - - // total average and quantile averages - for (size_t i = 0; i < m_times.size(); ++i) { - avg += m_times[i]; - avgs[i / samples_per_quantile] - += m_times[i] / static_cast(samples_per_quantile); - } - - avg /= static_cast (m_times.size()); - - // standard dev corrected for estimation from sample - for (size_t i = 0; i < m_times.size(); ++i) { - stddev += (m_times[i] - avg) * (m_times[i] - avg); - } - - // Bessel's correction - stddev /= static_cast (m_times.size() - 1); - stddev = std::sqrt(stddev); - } else { - m_times.push_back(0); - } - - boost::chrono::nanoseconds total_dur = m_end[m_end.size()-1] - m_start; - total = static_cast (total_dur.count()) / 1000.; // microsec - seconds = total / 10000000.; - - std::stringstream s; - std::string outcome; - - switch (m_pass) { - case FAIL: - outcome = "fail"; - break; - case PASS: - outcome = "pass"; - break; - case TIME_OUT: - outcome = "time_out"; - break; - case RUNNING: - throw case_exception("end() called from RUNNING state"); - break; - } - - s << "{\"result\":\"" << outcome - << "\",\"min\":" << m_times[0] - << ",\"max\":" << m_times[m_times.size()-1] - << ",\"median\":" << m_times[(m_times.size()-1)/2] - << ",\"avg\":" << avg - << ",\"stddev\":" << stddev - << ",\"total\":" << total - << ",\"bytes\":" << m_bytes - << ",\"quantiles\":["; - - for (size_t i = 0; i < m_quantile_count; i++) { - s << (i > 0 ? "," : ""); - s << "["; - s << avgs[i] << "," << quantiles[i]; - s << "]"; - } - s << "]"; - - if (m_rtts) { - s << ",\"rtts\":["; - for (size_t i = 0; i < m_times.size(); i++) { - s << (i > 0 ? "," : "") << m_times[i]; - } - s << "]"; - }; - s << "}"; - - m_data = s.str(); - - con->close(websocketpp::close::status::NORMAL,""); - } + void mark(); // Just does random ascii right now. True random UTF8 with multi-byte stuff // would probably be better - void fill_utf8(std::string& data,size_t size,bool random = true) { - if (random) { - uint32_t val; - for (size_t i = 0; i < size; i++) { - if (i%4 == 0) { - val = uint32_t(rand()); - } - - data.push_back(char(((reinterpret_cast(&val)[i%4])%95)+32)); - } - } else { - data.assign(size,'*'); - } - } + void fill_utf8(std::string& data,size_t size,bool random = true); + void fill_binary(std::string& data,size_t size,bool random = true); - void fill_binary(std::string& data,size_t size,bool random = true) { - if (random) { - int32_t val; - for (size_t i = 0; i < size; i++) { - if (i%4 == 0) { - val = rand(); - } - - data.push_back((reinterpret_cast(&val))[i%4]); - } - } else { - data.assign(size,'*'); - } - } + void on_timer(connection_ptr con,const boost::system::error_code& error); + void on_close(connection_ptr con) {} + void on_fail(connection_ptr con); + + const std::string& get_data() const; + const std::string& get_token() const; + const std::string& get_uri() const; + + // TODO: refactor these three extract methods into wscmd std::string extract_string(wscmd::cmd command,std::string key) { if (command.args[key] != "") { return command.args[key]; @@ -261,32 +111,6 @@ public: } throw case_exception("Invalid " + key + " parameter."); } - - void on_timer(connection_ptr con,const boost::system::error_code& error) { - if (error == boost::system::errc::operation_canceled) { - return; // timer was canceled because test finished - } - - // time out - mark(); - m_pass = TIME_OUT; - this->end(con); - } - - void on_close(connection_ptr con) {} - void on_fail(connection_ptr con) { - m_data = "{\"result\":\"connection_failed\"}"; - } - - const std::string& get_data() const { - return m_data; - } - const std::string& get_token() const { - return m_token; - } - const std::string& get_uri() const { - return m_uri; - } protected: enum status { FAIL = 0, diff --git a/examples/wsperf/generic.cpp b/examples/wsperf/generic.cpp index 65d401e974..9082caa460 100644 --- a/examples/wsperf/generic.cpp +++ b/examples/wsperf/generic.cpp @@ -24,3 +24,117 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + +#include "generic.hpp" + +using wsperf::message_test; + +// Construct a message_test from a wscmd command +/* Reads values from the wscmd object into member variables. The cmd object is + * passed to the parent constructor for extracting values common to all test + * cases. + * + * Any of the constructors may throw a `case_exception` if required parameters + * are not found or default values don't make sense. + * + * Values that message_test checks for: + * + * size=[interger]; + * Example: size=4096; + * Size of messages to send in bytes. Valid values 0 - max size_t + * + * count=[integer]; + * Example: count=1000; + * Number of test messages to send. Valid values 0-2^64 + * + * timeout=[integer]; + * Example: timeout=10000; + * How long to wait (in ms) for a response before failing the test. + * + * binary=[bool]; + * Example: binary=true; + * Whether or not to use binary websocket frames. (true=binary, false=utf8) + * + * sync=[bool]; + * Example: sync=true; + * Syncronize messages. When sync is on wsperf will wait for a response before + * sending the next message. When sync is off, messages will be sent as quickly + * as possible. + * + * correctness=[string]; + * Example: correctness=exact; + * Example: correctness=length; + * How to evaluate the correctness of responses. Exact checks each response for + * exact correctness. Length checks only that the response has the correct + * length. Length mode is faster but won't catch invalid implimentations. Length + * mode can be used to test situations where you deliberately return incorrect + * bytes in order to compare performance (ex: performance with/without masking) + */ +message_test::message_test(wscmd::cmd& cmd) : case_handler(cmd) { + m_message_count = extract_number(cmd,"size"); + m_message_size = extract_number(cmd,"count"); + m_timeout = extract_number(cmd,"timeout"); + + m_binary = extract_bool(cmd,"binary"); + m_sync = extract_bool(cmd,"sync"); + + if (cmd.args["correctness"] == "exact") { + m_mode = EXACT; + } else if (cmd.args["correctness"] == "length") { + m_mode = LENGTH; + } else { + throw case_exception("Invalid correctness parameter."); + } +} + +void message_test::on_open(connection_ptr con) { + m_msg = con->get_data_message(); + + m_data.reserve(static_cast(m_message_size)); + + if (!m_binary) { + fill_utf8(m_data,static_cast(m_message_size),true); + m_msg->reset(websocketpp::frame::opcode::TEXT); + } else { + fill_binary(m_data,static_cast(m_message_size),true); + m_msg->reset(websocketpp::frame::opcode::BINARY); + } + + m_msg->set_payload(m_data); + + start(con,m_timeout); + + if (m_sync) { + con->send(m_msg); + } else { + for (uint64_t i = 0; i < m_message_count; i++) { + con->send(m_msg); + } + } +} + +void message_test::on_message(connection_ptr con,websocketpp::message::data_ptr msg) { + if ((m_mode == LENGTH && msg->get_payload().size() == m_data.size()) || + (m_mode == EXACT && msg->get_payload() == m_data)) + { + m_acks++; + m_bytes += m_message_size; + mark(); + } else { + mark(); + m_timer->cancel(); + m_msg.reset(); + m_pass = FAIL; + + this->end(con); + } + + if (m_acks == m_message_count) { + m_pass = PASS; + m_timer->cancel(); + m_msg.reset(); + this->end(con); + } else if (m_sync && m_pass == RUNNING) { + con->send(m_msg); + } +} diff --git a/examples/wsperf/generic.hpp b/examples/wsperf/generic.hpp index 534f3e9773..a9dff48429 100644 --- a/examples/wsperf/generic.hpp +++ b/examples/wsperf/generic.hpp @@ -40,107 +40,11 @@ enum correctness_mode { class message_test : public case_handler { public: - message_test(uint64_t message_size, - uint64_t message_count, - int timeout, - bool binary, - bool sync, - correctness_mode mode) - : m_message_size(message_size), - m_message_count(message_count), - m_timeout(timeout), - m_binary(binary), - m_sync(sync), - m_mode(mode), - m_acks(0) - {} + /// Construct a message test from a wscmd command + explicit message_test(wscmd::cmd& cmd); - explicit message_test(wscmd::cmd& cmd) { - // message_test:uri=[string]; ws://localhost:9000 - // token=[string]; foo - // quantile_count=[integer]; 10 - // rtts=[bool]; true/false - // size=[interger]; 4096 - // count=[integer]; 1000 - // timeout=[integer]; 10000 - // binary=[bool]; true/false - // sync=[bool]; true/false - // correctness=[string]; exact/length - - // generic stuff - m_uri = extract_string(cmd,"uri"); - m_token = extract_string(cmd,"token"); - m_quantile_count = extract_number(cmd,"quantile_count"); - m_rtts = extract_bool(cmd,"rtts"); - - // specific to message_test - m_message_count = extract_number(cmd,"size"); - m_message_size = extract_number(cmd,"count"); - m_timeout = extract_number(cmd,"timeout"); - - m_binary = extract_bool(cmd,"binary"); - m_sync = extract_bool(cmd,"sync"); - - if (cmd.args["correctness"] == "exact") { - m_mode = EXACT; - } else if (cmd.args["correctness"] == "length") { - m_mode = LENGTH; - } else { - throw case_exception("Invalid correctness parameter."); - } - } - - void on_open(connection_ptr con) { - m_msg = con->get_data_message(); - - m_data.reserve(static_cast(m_message_size)); - - if (!m_binary) { - fill_utf8(m_data,static_cast(m_message_size),true); - m_msg->reset(websocketpp::frame::opcode::TEXT); - } else { - fill_binary(m_data,static_cast(m_message_size),true); - m_msg->reset(websocketpp::frame::opcode::BINARY); - } - - m_msg->set_payload(m_data); - - start(con,m_timeout); - - if (m_sync) { - con->send(m_msg); - } else { - for (uint64_t i = 0; i < m_message_count; i++) { - con->send(m_msg); - } - } - } - - void on_message(connection_ptr con,websocketpp::message::data_ptr msg) { - if ((m_mode == LENGTH && msg->get_payload().size() == m_data.size()) || - (m_mode == EXACT && msg->get_payload() == m_data)) - { - m_acks++; - m_bytes += m_message_size; - mark(); - } else { - mark(); - m_timer->cancel(); - m_msg.reset(); - m_pass = FAIL; - - this->end(con); - } - - if (m_acks == m_message_count) { - m_pass = PASS; - m_timer->cancel(); - m_msg.reset(); - this->end(con); - } else if (m_sync && m_pass == RUNNING) { - con->send(m_msg); - } - } + void on_open(connection_ptr con); + void on_message(connection_ptr con,websocketpp::message::data_ptr msg); private: // Simulation Parameters uint64_t m_message_size; diff --git a/examples/wsperf/request.cpp b/examples/wsperf/request.cpp index 65d401e974..2953235986 100644 --- a/examples/wsperf/request.cpp +++ b/examples/wsperf/request.cpp @@ -24,3 +24,75 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + +#include "request.hpp" + +using wsperf::request; + +void request::process() { + case_handler_ptr test; + std::string uri; + + wscmd::cmd command = wscmd::parse(req); + + try { + if (command.command == "message_test") { + test = case_handler_ptr(new message_test(command)); + token = test->get_token(); + uri = test->get_uri(); + } else { + writer->write(prepare_response("error","Invalid Command")); + return; + } + + writer->write(prepare_response("test_start","")); + + client e(test); + + e.alog().unset_level(websocketpp::log::alevel::ALL); + e.elog().unset_level(websocketpp::log::elevel::ALL); + + e.elog().set_level(websocketpp::log::elevel::RERROR); + e.elog().set_level(websocketpp::log::elevel::FATAL); + + e.connect(uri); + e.run(); + + writer->write(prepare_response_object("test_data",test->get_data())); + + writer->write(prepare_response("test_complete","")); + } catch (case_exception& e) { + writer->write(prepare_response("error",e.what())); + return; + } catch (websocketpp::exception& e) { + writer->write(prepare_response("error",e.what())); + return; + } catch (websocketpp::uri_exception& e) { + writer->write(prepare_response("error",e.what())); + return; + } +} + +std::string request::prepare_response(std::string type,std::string data) { + return "{\"type\":\"" + type + + "\",\"token\":\"" + token + "\",\"data\":\"" + data + "\"}"; +} + +std::string request::prepare_response_object(std::string type,std::string data){ + return "{\"type\":\"" + type + + "\",\"token\":\"" + token + "\",\"data\":" + data + "}"; +} + +void wsperf::process_requests(request_coordinator* coordinator) { + request r; + + while (1) { + coordinator->get_request(r); + + if (r.type == PERF_TEST) { + r.process(); + } else { + break; + } + } +} \ No newline at end of file diff --git a/examples/wsperf/request.hpp b/examples/wsperf/request.hpp index c0c8bc664a..1c257ae1a7 100644 --- a/examples/wsperf/request.hpp +++ b/examples/wsperf/request.hpp @@ -79,57 +79,11 @@ struct request { std::string token; // Parsed test token. Return in all results /// Run a test and return JSON result - void process() { - case_handler_ptr test; - std::string uri; - - wscmd::cmd command = wscmd::parse(req); - - try { - if (command.command == "message_test") { - test = case_handler_ptr(new message_test(command)); - token = test->get_token(); - uri = test->get_uri(); - } else { - writer->write(prepare_response("error","Invalid Command")); - return; - } - - writer->write(prepare_response("test_start","")); - - client e(test); - - e.alog().unset_level(websocketpp::log::alevel::ALL); - e.elog().unset_level(websocketpp::log::elevel::ALL); - - e.elog().set_level(websocketpp::log::elevel::RERROR); - e.elog().set_level(websocketpp::log::elevel::FATAL); - - e.connect(uri); - e.run(); - - writer->write(prepare_response_object("test_data",test->get_data())); - - writer->write(prepare_response("test_complete","")); - } catch (case_exception& e) { - writer->write(prepare_response("error",e.what())); - return; - } catch (websocketpp::exception& e) { - writer->write(prepare_response("error",e.what())); - return; - } catch (websocketpp::uri_exception& e) { - writer->write(prepare_response("error",e.what())); - return; - } - } + void process(); - std::string prepare_response(std::string type,std::string data) { - return "{\"type\":\"" + type + "\",\"token\":\"" + token + "\",\"data\":\"" + data + "\"}"; - } - - std::string prepare_response_object(std::string type,std::string data) { - return "{\"type\":\"" + type + "\",\"token\":\"" + token + "\",\"data\":" + data + "}"; - } + // Simple json generation + std::string prepare_response(std::string type,std::string data); + std::string prepare_response_object(std::string type,std::string data); }; // The coordinator is a simple wrapper around an STL queue. add_request inserts @@ -165,7 +119,14 @@ private: // coordinator. class concurrent_server_handler : public server::handler { public: - concurrent_server_handler(request_coordinator& c) : m_coordinator(c) {} + concurrent_server_handler(request_coordinator& c, + std::string ident, + std::string ua) + : m_coordinator(c), m_ident(ident), m_ua(ua) {} + + void on_open(connection_ptr con) { + con->send("{\"type\":\"test_welcome\",\"version\":\""+m_ua+"\",\"ident\":\""+m_ident+"\"}"); + } void on_message(connection_ptr con,message_ptr msg) { request r; @@ -180,6 +141,8 @@ public: } private: request_coordinator& m_coordinator; + std::string m_ident; + std::string m_ua; }; // The WebSocket++ handler in this case reads numbers from connections and packs @@ -187,7 +150,14 @@ private: // coordinator. class concurrent_client_handler : public client::handler { public: - concurrent_client_handler(request_coordinator& c) : m_coordinator(c) {} + concurrent_client_handler(request_coordinator& c, + std::string ident, + std::string ua) + : m_coordinator(c), m_ident(ident), m_ua(ua) {} + + void on_open(connection_ptr con) { + con->send("{\"type\":\"test_welcome\",\"version\":\""+m_ua+"\",\"ident\":\""+m_ident+"\"}"); + } void on_message(connection_ptr con,message_ptr msg) { request r; @@ -202,6 +172,8 @@ public: } private: request_coordinator& m_coordinator; + std::string m_ident; + std::string m_ua; }; // process_requests is the body function for a processing thread. It loops @@ -209,20 +181,6 @@ private: // request. void process_requests(request_coordinator* coordinator); -void process_requests(request_coordinator* coordinator) { - request r; - - while (1) { - coordinator->get_request(r); - - if (r.type == PERF_TEST) { - r.process(); - } else { - break; - } - } -} - } // namespace wsperf #endif // WSPERF_REQUEST_HPP diff --git a/examples/wsperf/autobahn.cpp b/examples/wsperf/wscmd.cpp similarity index 60% rename from examples/wsperf/autobahn.cpp rename to examples/wsperf/wscmd.cpp index 1156347606..3a26e95266 100644 --- a/examples/wsperf/autobahn.cpp +++ b/examples/wsperf/wscmd.cpp @@ -23,4 +23,48 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - */ \ No newline at end of file + */ + +#include "wscmd.hpp" + +#include +#include + +wscmd::cmd wscmd::parse(const std::string& m) { + cmd command; + std::string::size_type start; + std::string::size_type end; + + start = m.find(":",0); + + if (start != std::string::npos) { + command.command = m.substr(0,start); + + start++; // skip the colon + end = m.find(";",start); + + // find all semicolons + while (end != std::string::npos) { + std::string arg; + std::string val; + + std::string::size_type sep = m.find("=",start); + + if (sep != std::string::npos) { + arg = m.substr(start,sep-start); + val = m.substr(sep+1,end-sep-1); + } else { + arg = m.substr(start,end-start); + val = ""; + } + + command.args[arg] = val; + + start = end+1; + end = m.find(";",start); + } + } + + return command; +} + diff --git a/examples/wsperf/wscmd.hpp b/examples/wsperf/wscmd.hpp index 8bc3ae0b12..27e88be025 100644 --- a/examples/wsperf/wscmd.hpp +++ b/examples/wsperf/wscmd.hpp @@ -56,44 +56,6 @@ namespace wscmd { }; wscmd::cmd parse(const std::string& m); - - wscmd::cmd parse(const std::string& m) { - cmd command; - std::string::size_type start; - std::string::size_type end; - - start = m.find(":",0); - - if (start != std::string::npos) { - command.command = m.substr(0,start); - - start++; // skip the colon - end = m.find(";",start); - - // find all semicolons - while (end != std::string::npos) { - std::string arg; - std::string val; - - std::string::size_type sep = m.find("=",start); - - if (sep != std::string::npos) { - arg = m.substr(start,sep-start); - val = m.substr(sep+1,end-sep-1); - } else { - arg = m.substr(start,end-start); - val = ""; - } - - command.args[arg] = val; - - start = end+1; - end = m.find(";",start); - } - } - - return command; - } } // namespace wscmd #endif // WSCMD_HPP diff --git a/examples/wsperf/wsperf.cpp b/examples/wsperf/wsperf.cpp index 838b1f1d47..021e29f6ff 100644 --- a/examples/wsperf/wsperf.cpp +++ b/examples/wsperf/wsperf.cpp @@ -50,9 +50,13 @@ static const std::string user_agent = "wsperf/0.2.0dev WebSocket++/0.2.0dev"; using websocketpp::client; namespace po = boost::program_options; +int start_server(po::variables_map& vm); +int start_client(po::variables_map& vm); + int start_server(po::variables_map& vm) { unsigned short port = vm["port"].as(); unsigned int num_threads = vm["num_threads"].as(); + std::string ident = vm["ident"].as(); std::list< boost::shared_ptr > threads; @@ -63,7 +67,7 @@ int start_server(po::variables_map& vm) { std::cout << "bad thread number" << std::endl; return 1; } else { - h = server::handler::ptr(new wsperf::concurrent_server_handler(rc)); + h = server::handler::ptr(new wsperf::concurrent_server_handler(rc,ident,user_agent)); } server endpoint(h); @@ -92,6 +96,7 @@ int start_client(po::variables_map& vm) { std::string uri = vm["uri"].as(); unsigned int num_threads = vm["num_threads"].as(); + std::string ident = vm["ident"].as(); // Start wsperf std::list< boost::shared_ptr > threads; @@ -104,16 +109,14 @@ int start_client(po::variables_map& vm) { std::cout << "bad thread number" << std::endl; return 1; } else { - h = client::handler::ptr(new wsperf::concurrent_client_handler(rc)); + h = client::handler::ptr(new wsperf::concurrent_client_handler(rc,ident,user_agent)); } client endpoint(h); endpoint.alog().unset_level(websocketpp::log::alevel::ALL); endpoint.elog().unset_level(websocketpp::log::elevel::ALL); - - endpoint.alog().set_level(websocketpp::log::alevel::CONNECT); - + endpoint.elog().set_level(websocketpp::log::elevel::RERROR); endpoint.elog().set_level(websocketpp::log::elevel::FATAL); @@ -147,13 +150,6 @@ int start_client(po::variables_map& vm) { return 0; } -std::string env_mapper(std::string input) { - if (input == "WSPERF_CONFIG") { - return "config"; - } - return ""; -} - int main(int argc, char* argv[]) { try { std::string config_file; @@ -174,14 +170,9 @@ int main(int argc, char* argv[]) { ("port,p", po::value()->default_value(9050), "Port to listen on in server mode") ("uri,u", po::value(), "URI to connect to in client mode") ("num_threads", po::value()->default_value(2), "Number of worker threads to use") + ("ident,i", po::value()->default_value("Unspecified"), "Implimentation identification string reported by this agent.") ; - /*po::options_description environment("Environment"); - environment.add_options() - ("config", po::value(&config_file)->default_value("~/.wsperf"), - "Configuration file to use.") - ;*/ - po::options_description cmdline_options; cmdline_options.add(generic).add(config); @@ -194,7 +185,6 @@ int main(int argc, char* argv[]) { po::variables_map vm; po::store(po::parse_command_line(argc, argv, cmdline_options), vm); - //po::store(po::parse_environment(environment,env_mapper), vm); po::notify(vm); std::ifstream ifs(config_file.c_str()); diff --git a/websocketpp.xcodeproj/project.pbxproj b/websocketpp.xcodeproj/project.pbxproj index 9ae1d7fe98..55db529ef1 100644 --- a/websocketpp.xcodeproj/project.pbxproj +++ b/websocketpp.xcodeproj/project.pbxproj @@ -100,7 +100,6 @@ B6DF1CE21435F1860029A1B1 /* libboost_system.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CE11435F1860029A1B1 /* libboost_system.dylib */; }; B6E56D80150644A3007E1707 /* request.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6E56D7E150644A3007E1707 /* request.cpp */; }; B6E7E77E1505536500394909 /* wsperf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6E56D6814FEFC54007E1707 /* wsperf.cpp */; }; - B6E7E77F1505536500394909 /* autobahn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6E56D6B14FF0183007E1707 /* autobahn.cpp */; }; B6E7E7801505536500394909 /* case.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6E56D6914FEFE95007E1707 /* case.cpp */; }; B6E7E7811505536500394909 /* generic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6E56D6D1503DF2C007E1707 /* generic.cpp */; }; B6E7E7821505537200394909 /* libwebsocketpp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1C721434A8280029A1B1 /* libwebsocketpp.dylib */; }; @@ -274,6 +273,7 @@ B62A5A7014774F08005F9EB0 /* md5.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = md5.hpp; path = src/md5/md5.hpp; sourceTree = ""; }; B62A5A71147759EA005F9EB0 /* common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = common.hpp; path = src/common.hpp; sourceTree = ""; }; B63E51A814C9A5790006BF9A /* shared_const_buffer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = shared_const_buffer.hpp; path = src/shared_const_buffer.hpp; sourceTree = ""; }; + B649759D150BA96100BE17D1 /* wscmd.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = wscmd.cpp; path = examples/wsperf/wscmd.cpp; sourceTree = ""; }; B64E12D214BDE132006F20F0 /* logging.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = logging.cpp; sourceTree = ""; }; B64E12D314BDE132006F20F0 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; B64E12D414BDE132006F20F0 /* parsing.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = parsing.cpp; sourceTree = ""; }; @@ -365,8 +365,6 @@ B6E56D6814FEFC54007E1707 /* wsperf.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = wsperf.cpp; path = examples/wsperf/wsperf.cpp; sourceTree = ""; }; B6E56D6914FEFE95007E1707 /* case.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = case.cpp; path = examples/wsperf/case.cpp; sourceTree = ""; }; B6E56D6A14FEFE95007E1707 /* case.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = case.hpp; path = examples/wsperf/case.hpp; sourceTree = ""; }; - B6E56D6B14FF0183007E1707 /* autobahn.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = autobahn.cpp; path = examples/wsperf/autobahn.cpp; sourceTree = ""; }; - B6E56D6C14FF0184007E1707 /* autobahn.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = autobahn.hpp; path = examples/wsperf/autobahn.hpp; sourceTree = ""; }; B6E56D6D1503DF2C007E1707 /* generic.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = generic.cpp; path = examples/wsperf/generic.cpp; sourceTree = ""; }; B6E56D6E1503DF2C007E1707 /* generic.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = generic.hpp; path = examples/wsperf/generic.hpp; sourceTree = ""; }; B6E56D6F150457B8007E1707 /* wscmd.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = wscmd.hpp; path = examples/wsperf/wscmd.hpp; sourceTree = ""; }; @@ -910,12 +908,11 @@ B6E56D6814FEFC54007E1707 /* wsperf.cpp */, B6E56D7E150644A3007E1707 /* request.cpp */, B6E56D7F150644A3007E1707 /* request.hpp */, - B6E56D6B14FF0183007E1707 /* autobahn.cpp */, - B6E56D6C14FF0184007E1707 /* autobahn.hpp */, B6E56D6914FEFE95007E1707 /* case.cpp */, B6E56D6A14FEFE95007E1707 /* case.hpp */, B6E56D6D1503DF2C007E1707 /* generic.cpp */, B6E56D6E1503DF2C007E1707 /* generic.hpp */, + B649759D150BA96100BE17D1 /* wscmd.cpp */, B6E56D6F150457B8007E1707 /* wscmd.hpp */, ); name = wsperf; @@ -1358,7 +1355,6 @@ buildActionMask = 2147483647; files = ( B6E7E77E1505536500394909 /* wsperf.cpp in Sources */, - B6E7E77F1505536500394909 /* autobahn.cpp in Sources */, B6E7E7801505536500394909 /* case.cpp in Sources */, B6E7E7811505536500394909 /* generic.cpp in Sources */, B6E56D80150644A3007E1707 /* request.cpp in Sources */,