diff --git a/src/hybi_00_processor.hpp b/src/hybi_00_processor.hpp new file mode 100644 index 0000000000..7313df50cc --- /dev/null +++ b/src/hybi_00_processor.hpp @@ -0,0 +1,49 @@ +/* + * 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 WEBSOCKET_HYBI_00_PROCESSOR_HPP +#define WEBSOCKET_HYBI_00_PROCESSOR_HPP + +#include "interfaces/protocol.hpp" + +namespace websocketpp { +namespace protocol { + +class hybi_00_processor : public processor { +public: + void validate_handshake(const http::parser::request& headers) const { + + } + + void handshake_response(const http::parser::request& request,http::parser::response& response) { + + } +}; + +} +} +#endif // WEBSOCKET_HYBI_00_PROCESSOR_HPP diff --git a/src/interfaces/protocol.hpp b/src/interfaces/protocol.hpp index acae309869..d61e6a8297 100644 --- a/src/interfaces/protocol.hpp +++ b/src/interfaces/protocol.hpp @@ -30,10 +30,13 @@ #include +#include "../http/parser.hpp" + namespace websocketpp { namespace protocol { class processor { +public: // validate client handshake // validate server handshake @@ -43,7 +46,7 @@ class processor { // validate handshake request virtual void validate_handshake(const http::parser::request& headers) const = 0; - virtual void handshake_response(const http::parser::request& headers,http::parser::response& headers) = 0; + virtual void handshake_response(const http::parser::request& request,http::parser::response& response) = 0; // Given a list of HTTP headers determin if the values are a reasonable // response to our handshake request. If so diff --git a/src/websocket_server.hpp b/src/websocket_server.hpp index 4eb5cabaa5..7d6e7d6085 100644 --- a/src/websocket_server.hpp +++ b/src/websocket_server.hpp @@ -39,6 +39,7 @@ namespace po = boost::program_options; #include "websocketpp.hpp" #include "interfaces/session.hpp" +#include "interfaces/protocol.hpp" #include "websocket_session.hpp" #include "websocket_connection_handler.hpp" @@ -52,6 +53,7 @@ namespace po = boost::program_options; using boost::asio ::ip::tcp; using websocketpp::session::server_handler_ptr; +using websocketpp::protocol::processor_ptr; namespace websocketpp { namespace server { @@ -113,7 +115,7 @@ public: std::size_t bytes_transferred) { if (e) { log_error("Error reading HTTP request",e); - drop_tcp(); + terminate_connection(false); return; } @@ -141,10 +143,10 @@ public: int m_version = -1; if (boost::ifind_first(m_request.header("Upgrade","websocket"))) { - if (handshake.header("Sec-WebSocket-Version") == "") { + if (m_request.header("Sec-WebSocket-Version") == "") { m_version = 0; } else { - m_version = atoi(h.c_str()); + m_version = atoi(m_request.header("Sec-WebSocket-Version").c_str()); if (m_version == 0) { throw(handshake_error("Unable to determine connection version",http::status_code::BAD_REQUEST)); } @@ -171,7 +173,7 @@ public: } m_request.set_header("Sec-WebSocket-Key3",std::string(foo)); - m_processor = protocol::processor_ptr(new protocol::hybi_00_processor()); + m_processor = processor_ptr(new protocol::hybi_00_processor()); } else if (m_version == 7 || m_version == 8 || m_version == 13) { // create hybi 17 processor m_processor = protocol::processor_ptr(new protocol::hybi_17_processor()); @@ -242,31 +244,32 @@ public: } void handle_write_response(const boost::system::error_code& error) { + // stop the handshake timer + m_timer.cancel(); + if (error) { - log_error("Error writing handshake response",error); - drop_tcp(); + log_error("Network error writing handshake response",error); + terminate_connection(false); + m_handler->on_fail(boost::static_pointer_cast(session_type::shared_from_this())); return; } log_open_result(); - if (m_response.status_code() != http::status_code::SWITCHING_PROTOCOLS) { + // log error if this was + if (m_version != -1 && m_response.status_code() != http::status_code::SWITCHING_PROTOCOLS) { m_server->elog().at(log::elevel::ERROR) << "Handshake ended with HTTP error: " << m_response.status_code() << " " << m_response.status_msg() << log::endl; - drop_tcp(); - // TODO: tell client that connection failed? - // use on_fail? + terminate_connection(true); + m_handler->on_fail(boost::static_pointer_cast(session_type::shared_from_this())); return; } m_state = state::OPEN; - // stop the handshake timer - m_timer.cancel(); - m_handler->on_open(boost::static_pointer_cast(session_type::shared_from_this())); // TODO: start read message loop. @@ -281,8 +284,7 @@ public: } else { // got unexpected EOF // TODO: log error - // TODO: drop tcp - // TODO: set state to CLOSED + terminate_connection(false); } } else if (error == boost::asio::error::operation_aborted) { if (m_state == session::state::CLOSED) { @@ -296,21 +298,18 @@ public: // all connections on this io_service) // TODO: log error - // TODO: drop tcp - // TODO: set state to CLOSED + terminate_connection(true); } } else { // Other unexpected error // TODO: log error - // TODO: drop tcp - // TODO: set state to CLOSED + terminate_connection(false); } } // check if state changed while we were waiting for a read. if (m_state == session::state::CLOSED) { - // TODO: on_close return; } @@ -358,14 +357,12 @@ public: // connection exit the process loop. Otherwise re-check if we have // any bytes left to process. if (m_state == session::state::CLOSED) { - // TODO: on_close break; } } // check if state changed while processing frames if (m_state == session::state::CLOSED) { - // notify end user and don't refresh the ASIO loop return; } @@ -376,7 +373,11 @@ public: // - tcp connection is closed // - session state is CLOSED // - session end flags are set - void terminate_connection() { + void terminate_connection(bool failed_by_me) { + if (m_state == session::state::CLOSED) { + // shouldn't be here + } + // cancel the close timeout m_timer.cancel(); @@ -395,9 +396,26 @@ public: } } + m_failed_by_me = failed_by_me; - + session::state::value old_state = m_state; m_state = session::state::CLOSED; + + // If we called terminate from the connecting state call on_fail + if (old_state == session::state::CONNECTING) { + m_handler->on_fail(boost::static_pointer_cast(session_type::shared_from_this())); + } else if (old_state == session::state::OPEN || + old_state == session::state::CLOSING) { + m_handler->on_close(boost::static_pointer_cast(session_type::shared_from_this())); + } else { + // if we were already closed something is wrong + } + } + + // this is called when an async asio call encounters an error + void log_error(std::string msg,const boost::system::error_code& e) { + m_server->elog().at(log::elevel::ERROR) + << msg << "(" << e << ")" << log::endl; } void fail_on_expire(const boost::system::error_code& error) { @@ -405,13 +423,13 @@ public: if (error != boost::asio::error::operation_aborted) { m_server->elog().at(log::elevel::DEVEL) << "fail_on_expire timer ended in unknown error" << log::endl; - //drop_tcp(true); + terminate_connection(false); } return; } m_server->elog().at(log::elevel::DEVEL) << "fail_on_expire timer expired" << log::endl; - drop_tcp(true); + terminate_connection(true); } private: @@ -423,7 +441,7 @@ private: boost::asio::streambuf m_buf; server_handler_ptr m_handler; - protocol::processor_ptr m_processor; + processor_ptr m_processor; http::parser::request m_request; http::parser::response m_response; diff --git a/websocketpp.xcodeproj/project.pbxproj b/websocketpp.xcodeproj/project.pbxproj index 95e65ad750..e325ae69b1 100644 --- a/websocketpp.xcodeproj/project.pbxproj +++ b/websocketpp.xcodeproj/project.pbxproj @@ -145,6 +145,7 @@ B62A5A3214695185005F9EB0 /* frame_parser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = frame_parser.hpp; path = src/interfaces/frame_parser.hpp; sourceTree = ""; }; B62A5A3314695185005F9EB0 /* session.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = session.hpp; path = src/interfaces/session.hpp; sourceTree = ""; }; B62A5A34146963FE005F9EB0 /* protocol.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = protocol.hpp; path = src/interfaces/protocol.hpp; sourceTree = ""; }; + B62A5A35146BFC7D005F9EB0 /* hybi_00_processor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = hybi_00_processor.hpp; path = src/hybi_00_processor.hpp; sourceTree = ""; }; B6828875143745DA002BA48B /* chat_client_handler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat_client_handler.cpp; path = examples/chat_client/chat_client_handler.cpp; sourceTree = ""; }; B6828876143745DA002BA48B /* chat_client_handler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = chat_client_handler.hpp; path = examples/chat_client/chat_client_handler.hpp; sourceTree = ""; }; B6828877143745DA002BA48B /* chat_client.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat_client.cpp; path = examples/chat_client/chat_client.cpp; sourceTree = ""; }; @@ -326,6 +327,7 @@ B6DF1C7F1434ABB70029A1B1 /* src */ = { isa = PBXGroup; children = ( + B62A5A35146BFC7D005F9EB0 /* hybi_00_processor.hpp */, B62A5A2F1469512A005F9EB0 /* interfaces */, B6FE8D1414686A6D00B32547 /* md5 */, B61387B51462B34400ED9B19 /* logger */,