[DEV] clean interface and add protocol management

This commit is contained in:
Edouard DUPIN 2016-06-21 21:28:58 +02:00
parent 07323386b7
commit d16c4b130d
4 changed files with 76 additions and 26 deletions

View File

@ -87,7 +87,7 @@ static std::string generateCheckKey(const std::string& _key) {
return algue::base64::encode(keyData); return algue::base64::encode(keyData);
} }
void enet::WebSocket::start(const std::string& _uri) { void enet::WebSocket::start(const std::string& _uri, const std::vector<std::string>& _listProtocols) {
if (m_interface == nullptr) { if (m_interface == nullptr) {
ENET_ERROR("Nullptr interface ..."); ENET_ERROR("Nullptr interface ...");
return; return;
@ -105,6 +105,19 @@ void enet::WebSocket::start(const std::string& _uri) {
req.setKey("Sec-WebSocket-Version", "13"); req.setKey("Sec-WebSocket-Version", "13");
req.setKey("Pragma", "no-cache"); req.setKey("Pragma", "no-cache");
req.setKey("Cache-Control", "no-cache"); req.setKey("Cache-Control", "no-cache");
std::string protocolList;
for (auto &it : _listProtocols) {
if (it == "") {
continue;
}
if (protocolList != "") {
protocolList += ", ";
}
protocolList += it;
}
if (protocolList != "") {
req.setKey("Sec-WebSocket-Protocol", protocolList);
}
ememory::SharedPtr<enet::HttpClient> interface = std::dynamic_pointer_cast<enet::HttpClient>(m_interface); ememory::SharedPtr<enet::HttpClient> interface = std::dynamic_pointer_cast<enet::HttpClient>(m_interface);
if (interface!=nullptr) { if (interface!=nullptr) {
interface->setHeader(req); interface->setHeader(req);
@ -118,11 +131,10 @@ void enet::WebSocket::stop(bool _inThread) {
return; return;
} }
m_interface->stop(_inThread); m_interface->stop(_inThread);
m_interface.reset(); // deadlock ... m_interface.reset();
} }
void enet::WebSocket::onReceiveData(enet::Tcp& _connection) { void enet::WebSocket::onReceiveData(enet::Tcp& _connection) {
ENET_VERBOSE("Read Binary [START]");
uint8_t opcode = 0; uint8_t opcode = 0;
int32_t len = _connection.read(&opcode, sizeof(uint8_t)); int32_t len = _connection.read(&opcode, sizeof(uint8_t));
if (len <= 0) { if (len <= 0) {
@ -139,7 +151,6 @@ void enet::WebSocket::onReceiveData(enet::Tcp& _connection) {
return; return;
} }
m_lastReceive = std::chrono::steady_clock::now(); m_lastReceive = std::chrono::steady_clock::now();
ENET_VERBOSE("Read opcode : " << uint32_t(opcode));
if ((opcode & 0x80) == 0) { if ((opcode & 0x80) == 0) {
ENET_ERROR("Multiple frames ... NOT managed ..."); ENET_ERROR("Multiple frames ... NOT managed ...");
m_interface->stop(true); m_interface->stop(true);
@ -147,7 +158,6 @@ void enet::WebSocket::onReceiveData(enet::Tcp& _connection) {
} }
int8_t size1 = 0; int8_t size1 = 0;
len = _connection.read(&size1, sizeof(uint8_t)); len = _connection.read(&size1, sizeof(uint8_t));
ENET_VERBOSE("Read payload : " << uint32_t(size1));
if (len <= 0) { if (len <= 0) {
if (len < 0) { if (len < 0) {
if (_connection.getConnectionStatus() == enet::Tcp::status::link) { if (_connection.getConnectionStatus() == enet::Tcp::status::link) {
@ -244,13 +254,13 @@ void enet::WebSocket::onReceiveData(enet::Tcp& _connection) {
} }
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_PING) { if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_PING) {
// Close the conection by remote: // Close the conection by remote:
ENET_INFO("Receive a ping (send a pong)"); ENET_DEBUG("Receive a ping (send a pong)");
controlPong(); controlPong();
return; return;
} }
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_PONG) { if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_PONG) {
// Close the conection by remote: // Close the conection by remote:
ENET_INFO("Receive a pong"); ENET_DEBUG("Receive a pong");
return; return;
} }
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_TEXT) { if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_TEXT) {
@ -263,14 +273,31 @@ void enet::WebSocket::onReceiveData(enet::Tcp& _connection) {
} }
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_BINARY) { if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_BINARY) {
// Close the conection by remote: // Close the conection by remote:
ENET_DEBUG("Receive a binary data " << m_buffer.size() << " Bytes");
if (m_observer != nullptr) { if (m_observer != nullptr) {
m_observer(m_buffer, false); m_observer(m_buffer, false);
} }
return; return;
} }
ENET_ERROR("ReadRaw [STOP] (no opcode manage ... " << int32_t(opcode & 0x0F)); ENET_ERROR("ReadRaw [STOP] (no opcode manage ... " << int32_t(opcode & 0x0F));
}
static std::string removeStartAndStopSpace(const std::string& _value) {
std::string out;
out.reserve(_value.size());
bool findSpace = false;
for (auto &it : _value) {
if (it != ' ') {
if ( findSpace == true
&& out.size() != 0) {
out += ' ';
}
out += it;
findSpace = false;
} else {
findSpace = true;
}
}
return out;
} }
void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) { void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) {
@ -312,8 +339,17 @@ void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) {
interface->stop(true); interface->stop(true);
return; return;
} }
// parse all protocols:
std::vector<std::string> listProtocol;
if (_data.getKey("Sec-WebSocket-Protocol") != "") {
listProtocol = etk::split(_data.getKey("Sec-WebSocket-Protocol"),',');
for (size_t iii=0; iii<listProtocol.size(); ++iii) {
listProtocol[iii] = removeStartAndStopSpace(listProtocol[iii]);
}
}
if (m_observerUriCheck != nullptr) { if (m_observerUriCheck != nullptr) {
if (m_observerUriCheck(_data.getUri()) == false) { if (m_observerUriCheck(_data.getUri(), listProtocol) == false) {
enet::HttpAnswer answer(enet::HTTPAnswerCode::c404_notFound); enet::HttpAnswer answer(enet::HTTPAnswerCode::c404_notFound);
answer.setProtocol(enet::HTTPProtocol::http_1_1); answer.setProtocol(enet::HTTPProtocol::http_1_1);
answer.setKey("Connection", "close"); answer.setKey("Connection", "close");
@ -328,6 +364,9 @@ void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) {
answer.setKey("Connection", "Upgrade"); answer.setKey("Connection", "Upgrade");
std::string answerKey = generateCheckKey(_data.getKey("Sec-WebSocket-Key")); std::string answerKey = generateCheckKey(_data.getKey("Sec-WebSocket-Key"));
answer.setKey("Sec-WebSocket-Accept", answerKey); answer.setKey("Sec-WebSocket-Accept", answerKey);
if (m_protocol != "") {
answer.setKey("Sec-WebSocket-Protocol", m_protocol);
}
interface->setHeader(answer); interface->setHeader(answer);
} }
@ -358,7 +397,8 @@ void enet::WebSocket::onReceiveAnswer(const enet::HttpAnswer& _data) {
m_interface->stop(true); m_interface->stop(true);
return; return;
} }
setProtocol(_data.getKey("Sec-WebSocket-Protocol"));
// TODO : Create a methode to check the current protocol ...
} }
bool enet::WebSocket::writeHeader(int32_t _len, bool _isString, bool _mask) { bool enet::WebSocket::writeHeader(int32_t _len, bool _isString, bool _mask) {
@ -378,24 +418,18 @@ bool enet::WebSocket::writeHeader(int32_t _len, bool _isString, bool _mask) {
} }
m_lastSend = std::chrono::steady_clock::now(); m_lastSend = std::chrono::steady_clock::now();
m_interface->write(&header, sizeof(uint8_t)); m_interface->write(&header, sizeof(uint8_t));
ENET_VERBOSE("write opcode : " << int32_t(header));
if (_len < 126) { if (_len < 126) {
uint8_t size = _len | mask; uint8_t size = _len | mask;
ENET_VERBOSE("write payload : " << int32_t(size));
m_interface->write(&size, sizeof(uint8_t)); m_interface->write(&size, sizeof(uint8_t));
} else if (_len < 65338) { } else if (_len < 65338) {
uint8_t payload = 126 | mask; uint8_t payload = 126 | mask;
ENET_VERBOSE("write payload : " << int32_t(payload));
m_interface->write(&payload, sizeof(uint8_t)); m_interface->write(&payload, sizeof(uint8_t));
uint16_t size = _len; uint16_t size = _len;
ENET_VERBOSE("write size : " << int32_t(size));
m_interface->write(&size, sizeof(uint16_t)); m_interface->write(&size, sizeof(uint16_t));
} else { } else {
uint8_t payload = 127 | mask; uint8_t payload = 127 | mask;
ENET_VERBOSE("write payload : " << int32_t(payload));
m_interface->write(&payload, sizeof(uint8_t)); m_interface->write(&payload, sizeof(uint8_t));
uint64_t size = _len; uint64_t size = _len;
ENET_VERBOSE("write size : " << size);
m_interface->write(&size, sizeof(uint64_t)); m_interface->write(&size, sizeof(uint64_t));
} }
if (mask != 0 ) { if (mask != 0 ) {

View File

@ -30,7 +30,7 @@ namespace enet {
WebSocket(enet::Tcp _connection, bool _isServer=false); WebSocket(enet::Tcp _connection, bool _isServer=false);
void setInterface(enet::Tcp _connection, bool _isServer=false); void setInterface(enet::Tcp _connection, bool _isServer=false);
virtual ~WebSocket(); virtual ~WebSocket();
void start(const std::string& _uri=""); void start(const std::string& _uri="", const std::vector<std::string>& _listProtocols=std::vector<std::string>());
void stop(bool _inThread=false); void stop(bool _inThread=false);
bool isAlive() const { bool isAlive() const {
if (m_interface == nullptr) { if (m_interface == nullptr) {
@ -41,6 +41,12 @@ namespace enet {
void onReceiveData(enet::Tcp& _data); void onReceiveData(enet::Tcp& _data);
void onReceiveRequest(const enet::HttpRequest& _data); void onReceiveRequest(const enet::HttpRequest& _data);
void onReceiveAnswer(const enet::HttpAnswer& _data); void onReceiveAnswer(const enet::HttpAnswer& _data);
protected:
std::string m_protocol;
public:
void setProtocol(const std::string& _protocol) {
m_protocol = _protocol;
}
public: public:
using Observer = std::function<void(std::vector<uint8_t>&, bool)>; //!< Define an Observer: function pointer using Observer = std::function<void(std::vector<uint8_t>&, bool)>; //!< Define an Observer: function pointer
protected: protected:
@ -63,7 +69,7 @@ namespace enet {
} }
// Only server: // Only server:
public: public:
using ObserverUriCheck = std::function<bool(const std::string&)>; //!< Define an Observer: function pointer using ObserverUriCheck = std::function<bool(const std::string&, const std::vector<std::string>&)>; //!< Define an Observer: function pointer
protected: protected:
ObserverUriCheck m_observerUriCheck; ObserverUriCheck m_observerUriCheck;
public: public:
@ -74,9 +80,9 @@ namespace enet {
* @param[in] _args Argument optinnal the user want to add. * @param[in] _args Argument optinnal the user want to add.
*/ */
template<class CLASS_TYPE> template<class CLASS_TYPE>
void connectUri(CLASS_TYPE* _class, bool (CLASS_TYPE::*_func)(const std::string&)) { void connectUri(CLASS_TYPE* _class, bool (CLASS_TYPE::*_func)(const std::string&, const std::vector<std::string>&)) {
m_observerUriCheck = [=](const std::string& _value){ m_observerUriCheck = [=](const std::string& _value, const std::vector<std::string>& _protocols){
return (*_class.*_func)(_value); return (*_class.*_func)(_value, _protocols);
}; };
} }
void connectUri(ObserverUriCheck _func) { void connectUri(ObserverUriCheck _func) {

View File

@ -53,7 +53,11 @@ int main(int _argc, const char *_argv[]) {
connection.connect(appl::onReceiveData); connection.connect(appl::onReceiveData);
// start http connection (the actual state is just TCP start ...) // start http connection (the actual state is just TCP start ...)
connection.start("/plop.txt"); std::vector<std::string> protocols;
protocols.push_back("test1526/1.0");
protocols.push_back("test1526/1.5");
protocols.push_back("Hello");
connection.start("/plop.txt", protocols);
// send some data to play ... // send some data to play ...
connection.write("coucou comment ca vas ???"); connection.write("coucou comment ca vas ???");

View File

@ -25,8 +25,14 @@ namespace appl {
TEST_INFO("binary data: ... "); TEST_INFO("binary data: ... ");
} }
} }
bool onReceiveUri(enet::WebSocket* _interface, const std::string& _uri) { bool onReceiveUri(enet::WebSocket* _interface, const std::string& _uri, const std::vector<std::string>& _protocols) {
TEST_INFO("Receive Header uri: " << _uri); TEST_INFO("Receive Header uri: " << _uri);
for (auto &it : _protocols) {
if (it == "test1526/1.5") {
_interface->setProtocol(it);
break;
}
}
if (_uri == "/plop.txt") { if (_uri == "/plop.txt") {
return true; return true;
} }
@ -70,8 +76,8 @@ int main(int _argc, const char *_argv[]) {
connection.connect([=](std::vector<uint8_t>& _value, bool _isString){ connection.connect([=](std::vector<uint8_t>& _value, bool _isString){
appl::onReceiveData(tmp, _value, _isString); appl::onReceiveData(tmp, _value, _isString);
}); });
connection.connectUri([=](const std::string& _value){ connection.connectUri([=](const std::string& _value, const std::vector<std::string>& _protocols){
return appl::onReceiveUri(tmp, _value); return appl::onReceiveUri(tmp, _value, _protocols);
}); });
// start http connection (the actual state is just TCP start ...) // start http connection (the actual state is just TCP start ...)