[DEV] set websocket support the reconnection to an other server after http rediredt event

This commit is contained in:
2017-10-14 10:49:26 +02:00
parent 6ec43d60ed
commit c653ed5b4b
2 changed files with 98 additions and 42 deletions

View File

@@ -43,6 +43,14 @@ enet::WebSocket::WebSocket(enet::Tcp _connection, bool _isServer) :
setInterface(etk::move(_connection), _isServer); setInterface(etk::move(_connection), _isServer);
} }
const etk::String& enet::WebSocket::getRemoteAddress() const {
if (m_interface == nullptr) {
static const etk::String tmpOut;
return tmpOut;
}
return m_interface->getRemoteAddress();
}
void enet::WebSocket::setInterface(enet::Tcp _connection, bool _isServer) { void enet::WebSocket::setInterface(enet::Tcp _connection, bool _isServer) {
_connection.setTCPNoDelay(true); _connection.setTCPNoDelay(true);
if (_isServer == true) { if (_isServer == true) {
@@ -92,49 +100,61 @@ void enet::WebSocket::start(const etk::String& _uri, const etk::Vector<etk::Stri
ENET_ERROR("Nullptr interface ..."); ENET_ERROR("Nullptr interface ...");
return; return;
} }
m_interface->start(); if (m_interface->isServer() == true) {
if (m_interface->isServer() == false) { m_interface->start();
enet::HttpRequest req(enet::HTTPReqType::HTTP_GET); } else {
req.setProtocol(enet::HTTPProtocol::http_1_1); do {
req.setUri(_uri); m_redirectInProgress = false;
req.setKey("Upgrade", "websocket"); m_interface->start();
req.setKey("Connection", "Upgrade"); enet::HttpRequest req(enet::HTTPReqType::HTTP_GET);
m_checkKey = generateKey(); req.setProtocol(enet::HTTPProtocol::http_1_1);
req.setKey("Sec-WebSocket-Key", m_checkKey); // this is an example key ... req.setUri(_uri);
m_checkKey = generateCheckKey(m_checkKey); req.setKey("Upgrade", "websocket");
req.setKey("Sec-WebSocket-Version", "13"); req.setKey("Connection", "Upgrade");
req.setKey("Pragma", "no-cache"); m_checkKey = generateKey();
req.setKey("Cache-Control", "no-cache"); req.setKey("Sec-WebSocket-Key", m_checkKey); // this is an example key ...
etk::String protocolList; m_checkKey = generateCheckKey(m_checkKey);
for (auto &it : _listProtocols) { req.setKey("Sec-WebSocket-Version", "13");
if (it == "") { req.setKey("Pragma", "no-cache");
continue; req.setKey("Cache-Control", "no-cache");
etk::String protocolList;
for (auto &it : _listProtocols) {
if (it == "") {
continue;
}
if (protocolList != "") {
protocolList += ", ";
}
protocolList += it;
} }
if (protocolList != "") { if (protocolList != "") {
protocolList += ", "; req.setKey("Sec-WebSocket-Protocol", protocolList);
} }
protocolList += it; ememory::SharedPtr<enet::HttpClient> interface = ememory::dynamicPointerCast<enet::HttpClient>(m_interface);
} if (interface != nullptr) {
if (protocolList != "") { interface->setHeader(req);
req.setKey("Sec-WebSocket-Protocol", protocolList); int32_t timeout = 500000; // 5 second
} while ( timeout>=0
ememory::SharedPtr<enet::HttpClient> interface = ememory::dynamicPointerCast<enet::HttpClient>(m_interface); && m_redirectInProgress == false) {
if (interface != nullptr) { if ( m_connectionValidate == true
interface->setHeader(req); || m_interface->isAlive() == false) {
int32_t timeout = 500000; // 5 second break;
while (timeout>=0) { }
if ( m_connectionValidate == true ethread::sleepMilliSeconds(10);
|| m_interface->isAlive() == false) { timeout--;
break; }
if (m_redirectInProgress == true) {
ENET_WARNING("Request a redirection (wait 500ms)");
ethread::sleepMilliSeconds(500);
ENET_WARNING("Request a redirection (wait-end)");
} else {
if ( m_connectionValidate == false
|| m_interface->isAlive() == false) {
ENET_ERROR("Connection refused by SERVER ...");
}
} }
ethread::sleepMilliSeconds((10));
timeout--;
} }
if ( m_connectionValidate == false } while (m_redirectInProgress == true);
|| m_interface->isAlive() == false) {
ENET_ERROR("Connection refused by SERVER ...");
}
}
} }
} }
@@ -370,9 +390,22 @@ void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) {
listProtocol[iii] = removeStartAndStopSpace(listProtocol[iii]); listProtocol[iii] = removeStartAndStopSpace(listProtocol[iii]);
} }
} }
if (m_observerUriCheck != nullptr) { if (m_observerUriCheck != nullptr) {
if (m_observerUriCheck(_data.getUri(), listProtocol) == false) { etk::String ret = m_observerUriCheck(_data.getUri(), listProtocol);
if (ret == "OK") {
// Nothing to do
} else if (ret.startWith("REDIRECT:") == true) {
ENET_INFO("Request redirection of HTTP/WebSocket connection to : '" << ret.extract(9, ret.size()) << "'");
enet::HttpAnswer answer(enet::HTTPAnswerCode::c307_temporaryRedirect);
answer.setProtocol(enet::HTTPProtocol::http_1_1);
answer.setKey("Location", ret.extract(9, ret.size()));
interface->setHeader(answer);
interface->stop(true);
return;
} else {
if (ret != "CLOSE") {
ENET_ERROR("UNKNOW return type of URI request: '" << ret << "'");
}
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");
@@ -399,6 +432,14 @@ void enet::WebSocket::onReceiveAnswer(const enet::HttpAnswer& _data) {
return; return;
} }
_data.display(); _data.display();
if (_data.getErrorCode() == enet::HTTPAnswerCode::c307_temporaryRedirect) {
ENET_ERROR("Request connection redirection to '" << _data.getKey("Location") << "'");
// We are a client mode, we need to recreate a TCP connection on the new remote interface
// This is the generic way to accept a redirection
m_redirectInProgress = true;
m_interface->redirectTo(_data.getKey("Location"), true);
return;
}
if (_data.getErrorCode() != enet::HTTPAnswerCode::c101_switchingProtocols) { if (_data.getErrorCode() != enet::HTTPAnswerCode::c101_switchingProtocols) {
ENET_ERROR("change protocol has not been accepted ... " << _data.getErrorCode() << " with message : " << _data.getHelp()); ENET_ERROR("change protocol has not been accepted ... " << _data.getErrorCode() << " with message : " << _data.getHelp());
m_interface->stop(true); m_interface->stop(true);

View File

@@ -22,6 +22,7 @@ namespace enet {
echrono::Steady m_lastReceive; echrono::Steady m_lastReceive;
echrono::Steady m_lastSend; echrono::Steady m_lastSend;
ethread::Mutex m_mutex; ethread::Mutex m_mutex;
bool m_redirectInProgress = false;
public: public:
const echrono::Steady& getLastTimeReceive() { const echrono::Steady& getLastTimeReceive() {
return m_lastReceive; return m_lastReceive;
@@ -51,6 +52,12 @@ namespace enet {
void setProtocol(const etk::String& _protocol) { void setProtocol(const etk::String& _protocol) {
m_protocol = _protocol; m_protocol = _protocol;
} }
public:
/**
* @brief Get the address of the connection source IP:port or empty string
* @return string with the remote address name.
*/
const etk::String& getRemoteAddress() const;
public: public:
using Observer = etk::Function<void(etk::Vector<uint8_t>&, bool)>; //!< Define an Observer: function pointer using Observer = etk::Function<void(etk::Vector<uint8_t>&, bool)>; //!< Define an Observer: function pointer
protected: protected:
@@ -73,7 +80,15 @@ namespace enet {
} }
// Only server: // Only server:
public: public:
using ObserverUriCheck = etk::Function<bool(const etk::String&, const etk::Vector<etk::String>&)>; //!< Define an Observer: function pointer /**
* @brief Define an Observer: function pointer
* @param[in] _uri Current HTTP URI
* @param[in] _protocols List of protocol requested
* @return "OK" Connection accepted ==> send header
* @return "CLOSE" Close the current connection: 404
* @return "REDIRECT:IP:port" Redirect at the specific IP and port
*/
using ObserverUriCheck = etk::Function<etk::String(const etk::String&, const etk::Vector<etk::String>&)>;
protected: protected:
ObserverUriCheck m_observerUriCheck; ObserverUriCheck m_observerUriCheck;
public: public:
@@ -84,7 +99,7 @@ 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 etk::String&, const etk::Vector<etk::String>&)) { void connectUri(CLASS_TYPE* _class, etk::String (CLASS_TYPE::*_func)(const etk::String&, const etk::Vector<etk::String>&)) {
m_observerUriCheck = [=](const etk::String& _value, const etk::Vector<etk::String>& _protocols){ m_observerUriCheck = [=](const etk::String& _value, const etk::Vector<etk::String>& _protocols){
return (*_class.*_func)(_value, _protocols); return (*_class.*_func)(_value, _protocols);
}; };