diff --git a/test/roles/client.cpp b/test/roles/client.cpp index f32cd0a3f7..d9495aea18 100644 --- a/test/roles/client.cpp +++ b/test/roles/client.cpp @@ -30,9 +30,13 @@ #include +#include + #include #include +#include + struct stub_config : public websocketpp::config::core { typedef core::concurrency_type concurrency_type; @@ -46,7 +50,8 @@ struct stub_config : public websocketpp::config::core { typedef core::alog_type alog_type; typedef core::elog_type elog_type; - typedef core::rng_type rng_type; + //typedef core::rng_type rng_type; + typedef websocketpp::random::random_device::int_generator rng_type; typedef core::transport_type transport_type; @@ -86,3 +91,52 @@ BOOST_AUTO_TEST_CASE( get_connection ) { BOOST_CHECK_EQUAL( con->get_secure() , false ); BOOST_CHECK_EQUAL( con->get_resource() , "/" ); } + +BOOST_AUTO_TEST_CASE( connect_con ) { + client c; + websocketpp::lib::error_code ec; + std::stringstream out; + std::string o; + + c.register_ostream(&out); + + connection_ptr con = c.get_connection("ws://localhost/", ec); + c.connect(con); + + o = out.str(); + websocketpp::http::parser::request r; + r.consume(o.data(),o.size()); + + BOOST_CHECK( r.ready() ); + BOOST_CHECK_EQUAL( r.get_method(), "GET"); + BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1"); + BOOST_CHECK_EQUAL( r.get_uri(), "/"); + + BOOST_CHECK_EQUAL( r.get_header("Host"), "localhost"); + BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Version"), "13"); + BOOST_CHECK_EQUAL( r.get_header("Connection"), "Upgrade"); + BOOST_CHECK_EQUAL( r.get_header("Upgrade"), "websocket"); + + // Key is randomly generated & User-Agent will change so just check that + // they are not empty. + BOOST_CHECK_NE( r.get_header("Sec-WebSocket-Key"), ""); + BOOST_CHECK_NE( r.get_header("User-Agent"), "" ); + + // connection should have written out an opening handshake request and be in + // the read response internal state + + std::cout << "output: " << out.str() << std::endl; +} + + +// test cases +// - adding headers +// - adding Upgrade header +// - adding Connection header +// - adding Sec-WebSocket-Version, Sec-WebSocket-Key, or Host header +// - other Sec* headers? +// - User Agent header? + + +// Origin support +// Subprotocol requests \ No newline at end of file diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 0c9a24ea28..a35f4a6904 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -157,6 +157,7 @@ public: typedef typename con_msg_manager_type::ptr con_msg_manager_ptr; /// Type of RNG + typedef typename config::rng_type rng_type; typedef processor::processor processor_type; typedef lib::shared_ptr processor_ptr; @@ -672,6 +673,7 @@ public: size_t bytes_transferred); void handle_send_http_response(const lib::error_code& ec); + void handle_send_http_request(const lib::error_code& ec); /// Get array of WebSocket protocol versions that this connection supports. @@ -757,6 +759,9 @@ private: /// Completes m_response, serializes it, and sends it out on the wire. void send_http_response(); + /// Sends an opening WebSocket connect request + void send_http_request(); + /// Alternate path for send_http_response in error conditions void send_http_response_error(); @@ -893,7 +898,11 @@ private: size_t m_buf_cursor; termination_handler m_termination_handler; con_msg_manager_ptr m_msg_manager; - + + // TODO: this is not memory efficient. this value is not used after the + // handshake. + std::string m_handshake_buffer; + /// Pointer to the processor object for this connection /** * The processor provides functionality that is specific to the WebSocket diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 3706369f4c..8a5276451a 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -465,7 +465,10 @@ void connection::handle_transport_init(const lib::error_code& ec) { if (m_is_server) { this->read(1); } else { - // call prepare HTTP request + // We are a client. Set the processor to the version specified in the + // config file and send a handshake request. + m_processor = get_processor(config::client_version); + this->send_http_request(); } // TODO: Begin websocket handshake @@ -929,25 +932,23 @@ void connection::send_http_response() { // Set some common headers m_response.replace_header("Server",m_user_agent); - - std::string raw; - + // have the processor generate the raw bytes for the wire (if it exists) if (m_processor) { - raw = m_processor->get_raw(m_response); + m_handshake_buffer = m_processor->get_raw(m_response); } else { // a processor wont exist for raw HTTP responses. - raw = m_response.raw(); + m_handshake_buffer = m_response.raw(); } if (m_alog.static_test(log::alevel::devel)) { - m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+raw); + m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer); } // write raw bytes transport_con_type::async_write( - raw.c_str(), - raw.size(), + m_handshake_buffer.data(), + m_handshake_buffer.size(), lib::bind( &type::handle_send_http_response, type::shared_from_this(), @@ -1009,6 +1010,111 @@ void connection::handle_send_http_response( this->handle_read_frame(lib::error_code(), m_buf_cursor); } +template +void connection::send_http_request() { + m_alog.write(log::alevel::devel,"connection send_http_request"); + + // TODO: origin header + // TODO: subprotocol requests + + // Generate client key + // set client key header + + // Set User-Agent header + + + + // Have the protocol processor fill in the appropriate fields based on the + // selected client version + if (m_processor) { + lib::error_code ec; + ec = m_processor->handshake_request(m_request,m_uri); + + if (ec) { + m_elog.write(log::elevel::fatal, + "Internal library error: processor error: "+ec.message()); + return; + } + } else { + m_elog.write(log::elevel::fatal, + "Internal library error: missing processor"); + return; + } + + // Unless the user has overridden the user agent, send generic WS++ + if (m_request.get_header("User-Agent") == "") { + m_request.replace_header("User-Agent",m_user_agent); + } + + m_handshake_buffer = m_request.raw(); + + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel, + "Raw Handshake response:\n"+m_handshake_buffer); + } + + transport_con_type::async_write( + m_handshake_buffer.data(), + m_handshake_buffer.size(), + lib::bind( + &type::handle_send_http_request, + type::shared_from_this(), + lib::placeholders::_1 + ) + ); +} + +template +void connection::handle_send_http_request(const lib::error_code& ec) { + m_alog.write(log::alevel::devel,"handle_send_http_request"); + + /*this->atomic_state_check( + istate::PROCESS_HTTP_REQUEST, + "handle_send_http_response must be called from PROCESS_HTTP_REQUEST state" + ); + + if (ec) { + m_elog.write(log::elevel::rerror, + "error in handle_send_http_response: "+ec.message()); + this->terminate(); + return; + } + + this->log_open_result(); + + if (m_response.get_status_code() != http::status_code::SWITCHING_PROTOCOLS) + { + if (m_processor) { + // if this was not a websocket connection, we have written + // the expected response and the connection can be closed. + } else { + // this was a websocket connection that ended in an error + std::stringstream s; + s << "Handshake ended with HTTP error: " + << m_response.get_status_code(); + m_elog.write(log::elevel::rerror,s.str()); + } + this->terminate(); + return; + } + + // TODO: cancel handshake timer + + this->atomic_state_change( + istate::PROCESS_HTTP_REQUEST, + istate::PROCESS_CONNECTION, + session::state::CONNECTING, + session::state::OPEN, + "handle_send_http_response must be called from PROCESS_HTTP_REQUEST state" + ); + + if (m_open_handler) { + m_open_handler(m_connection_hdl); + } + + this->handle_read_frame(lib::error_code(), m_buf_cursor);*/ +} + template void connection::terminate() { try {