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

This commit is contained in:
Edouard DUPIN 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,8 +100,12 @@ void enet::WebSocket::start(const etk::String& _uri, const etk::Vector<etk::Stri
ENET_ERROR("Nullptr interface ..."); ENET_ERROR("Nullptr interface ...");
return; return;
} }
if (m_interface->isServer() == true) {
m_interface->start();
} else {
do {
m_redirectInProgress = false;
m_interface->start(); m_interface->start();
if (m_interface->isServer() == false) {
enet::HttpRequest req(enet::HTTPReqType::HTTP_GET); enet::HttpRequest req(enet::HTTPReqType::HTTP_GET);
req.setProtocol(enet::HTTPProtocol::http_1_1); req.setProtocol(enet::HTTPProtocol::http_1_1);
req.setUri(_uri); req.setUri(_uri);
@ -122,20 +134,28 @@ void enet::WebSocket::start(const etk::String& _uri, const etk::Vector<etk::Stri
if (interface != nullptr) { if (interface != nullptr) {
interface->setHeader(req); interface->setHeader(req);
int32_t timeout = 500000; // 5 second int32_t timeout = 500000; // 5 second
while (timeout>=0) { while ( timeout>=0
&& m_redirectInProgress == false) {
if ( m_connectionValidate == true if ( m_connectionValidate == true
|| m_interface->isAlive() == false) { || m_interface->isAlive() == false) {
break; break;
} }
ethread::sleepMilliSeconds((10)); ethread::sleepMilliSeconds(10);
timeout--; timeout--;
} }
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 if ( m_connectionValidate == false
|| m_interface->isAlive() == false) { || m_interface->isAlive() == false) {
ENET_ERROR("Connection refused by SERVER ..."); ENET_ERROR("Connection refused by SERVER ...");
} }
} }
} }
} while (m_redirectInProgress == true);
}
} }
void enet::WebSocket::stop(bool _inThread) { void enet::WebSocket::stop(bool _inThread) {
@ -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);
}; };