From 83c9cf69b054b44162b9cb6fdaac08df3f6bf9c3 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Fri, 17 Jun 2016 22:45:19 +0200 Subject: [PATCH] [DEV] HTTP start to be ok and websocket start to be implemented ... --- enet/Http.cpp | 519 ++++++++++++++++++++++++++++++-------- enet/Http.h | 228 ++++++++++++----- enet/WebSocket.cpp | 73 +++++- enet/WebSocket.h | 78 +++++- enet/debug.h | 1 + lutin_enet.py | 4 +- test/main-client-http.cpp | 10 +- test/main-server-http.cpp | 34 ++- 8 files changed, 747 insertions(+), 200 deletions(-) diff --git a/enet/Http.cpp b/enet/Http.cpp index d77c9d2..aee6705 100644 --- a/enet/Http.cpp +++ b/enet/Http.cpp @@ -11,6 +11,13 @@ #include #include +static std::string escapeChar(const std::string& _value) { + return _value; +} +static std::string unEscapeChar(const std::string& _value) { + return _value; +} + static std::map protocolName = { {enet::HTTPAnswerCode::c100_continue, "Continue"}, {enet::HTTPAnswerCode::c101_switchingProtocols, "Switching Protocols"}, @@ -69,12 +76,13 @@ enet::Http::Http(enet::Tcp _connection, bool _isServer) : m_connection(std::move(_connection)), m_headerIsSend(false), m_thread(nullptr), - m_threadRunning(false), - m_keepAlive(false) { - setSendHeaderProperties("User-Agent", "e-net (ewol network interface)"); + m_threadRunning(false) { + //setSendHeaderProperties("User-Agent", "e-net (ewol network interface)"); + /* if (m_keepAlive == true) { setSendHeaderProperties("Connection", "Keep-Alive"); } + */ } enet::Http::~Http() { @@ -97,7 +105,7 @@ void enet::Http::threadCallback() { if (len > 0) { ENET_INFO("Call client with datas ..."); if (m_observer != nullptr) { - m_observer(*this, m_temporaryBuffer); + m_observer(m_temporaryBuffer); } } } @@ -144,25 +152,7 @@ void enet::Http::stop(bool _inThreadStop){ } ENET_DEBUG("disconnect [STOP]"); } - -void enet::Http::setSendHeaderProperties(const std::string& _key, const std::string& _val) { - auto it = m_header.m_map.find(_key); - if (it == m_header.m_map.end()) { - m_header.m_map.insert(make_pair(_key, _val)); - } else { - it->second = _val; - } -} - -std::string enet::Http::getSendHeaderProperties(const std::string& _key) { - ENET_TODO("get header key=" << _key); - return ""; -} - -std::string enet::Http::getReceiveHeaderProperties(const std::string& _key) { - ENET_TODO("get header key=" << _key); - return ""; -} +/* void enet::Http::writeAnswerHeader(enum enet::HTTPAnswerCode _value) { std::string out; out = "HTTP/1.1 "; @@ -177,6 +167,172 @@ void enet::Http::writeAnswerHeader(enum enet::HTTPAnswerCode _value) { ENET_WARNING("Write header :" << out); write(out, false); } +*/ +namespace etk { + template <> + bool from_string(enum enet::HTTPAnswerCode& _variableRet, const std::string& _value) { + _variableRet = enet::HTTPAnswerCode::c000_unknow; + for (auto &it : protocolName) { + if (etk::to_string(int32_t(it.first)) == _value) { + _variableRet = it.first; + return true; + } + } + return false; + } + template <> + std::string to_string(const enum enet::HTTPAnswerCode& _value) { + return etk::to_string(int32_t(_value)); + } + template <> + bool from_string(enum enet::HTTPReqType& _variableRet, const std::string& _value) { + _variableRet = enet::HTTPReqType::GET; + if (_value == "GET") { + _variableRet = enet::HTTPReqType::GET; + return true; + } else if (_value == "HEAD") { + _variableRet = enet::HTTPReqType::HEAD; + return true; + } else if (_value == "POST") { + _variableRet = enet::HTTPReqType::POST; + return true; + } else if (_value == "PUT") { + _variableRet = enet::HTTPReqType::PUT; + return true; + } else if (_value == "DELETE") { + _variableRet = enet::HTTPReqType::DELETE; + return true; + } + return false; + } + template <> + std::string to_string(const enum enet::HTTPReqType& _value) { + switch (_value) { + case enet::HTTPReqType::GET: return "GET"; + case enet::HTTPReqType::HEAD: return "HEAD"; + case enet::HTTPReqType::POST: return "POST"; + case enet::HTTPReqType::PUT: return "PUT"; + case enet::HTTPReqType::DELETE: return "DELETE"; + } + return "UNKNOW"; + } + template <> + bool from_string(enum enet::HTTPProtocol& _variableRet, const std::string& _value) { + _variableRet = enet::HTTPProtocol::http_0_1; + if (_value == "HTTP/0.1") { _variableRet = enet::HTTPProtocol::http_0_1; return true; } + if (_value == "HTTP/0.2") { _variableRet = enet::HTTPProtocol::http_0_2; return true; } + if (_value == "HTTP/0.3") { _variableRet = enet::HTTPProtocol::http_0_3; return true; } + if (_value == "HTTP/0.4") { _variableRet = enet::HTTPProtocol::http_0_4; return true; } + if (_value == "HTTP/0.5") { _variableRet = enet::HTTPProtocol::http_0_5; return true; } + if (_value == "HTTP/0.6") { _variableRet = enet::HTTPProtocol::http_0_6; return true; } + if (_value == "HTTP/0.7") { _variableRet = enet::HTTPProtocol::http_0_7; return true; } + if (_value == "HTTP/0.8") { _variableRet = enet::HTTPProtocol::http_0_8; return true; } + if (_value == "HTTP/0.9") { _variableRet = enet::HTTPProtocol::http_0_9; return true; } + if (_value == "HTTP/0.10") { _variableRet = enet::HTTPProtocol::http_0_10; return true; } + if (_value == "HTTP/1.0") { _variableRet = enet::HTTPProtocol::http_1_0; return true; } + if (_value == "HTTP/1.1") { _variableRet = enet::HTTPProtocol::http_1_1; return true; } + if (_value == "HTTP/1.2") { _variableRet = enet::HTTPProtocol::http_1_2; return true; } + if (_value == "HTTP/1.3") { _variableRet = enet::HTTPProtocol::http_1_3; return true; } + if (_value == "HTTP/1.4") { _variableRet = enet::HTTPProtocol::http_1_4; return true; } + if (_value == "HTTP/1.5") { _variableRet = enet::HTTPProtocol::http_1_5; return true; } + if (_value == "HTTP/1.6") { _variableRet = enet::HTTPProtocol::http_1_6; return true; } + if (_value == "HTTP/1.7") { _variableRet = enet::HTTPProtocol::http_1_7; return true; } + if (_value == "HTTP/1.8") { _variableRet = enet::HTTPProtocol::http_1_8; return true; } + if (_value == "HTTP/1.9") { _variableRet = enet::HTTPProtocol::http_1_9; return true; } + if (_value == "HTTP/1.10") { _variableRet = enet::HTTPProtocol::http_1_10; return true; } + if (_value == "HTTP/2.0") { _variableRet = enet::HTTPProtocol::http_2_0; return true; } + if (_value == "HTTP/2.1") { _variableRet = enet::HTTPProtocol::http_2_1; return true; } + if (_value == "HTTP/2.2") { _variableRet = enet::HTTPProtocol::http_2_2; return true; } + if (_value == "HTTP/2.3") { _variableRet = enet::HTTPProtocol::http_2_3; return true; } + if (_value == "HTTP/2.4") { _variableRet = enet::HTTPProtocol::http_2_4; return true; } + if (_value == "HTTP/2.5") { _variableRet = enet::HTTPProtocol::http_2_5; return true; } + if (_value == "HTTP/2.6") { _variableRet = enet::HTTPProtocol::http_2_6; return true; } + if (_value == "HTTP/2.7") { _variableRet = enet::HTTPProtocol::http_2_7; return true; } + if (_value == "HTTP/2.8") { _variableRet = enet::HTTPProtocol::http_2_8; return true; } + if (_value == "HTTP/2.9") { _variableRet = enet::HTTPProtocol::http_2_9; return true; } + if (_value == "HTTP/2.10") { _variableRet = enet::HTTPProtocol::http_2_10; return true; } + if (_value == "HTTP/3.0") { _variableRet = enet::HTTPProtocol::http_3_0; return true; } + if (_value == "HTTP/3.1") { _variableRet = enet::HTTPProtocol::http_3_1; return true; } + if (_value == "HTTP/3.2") { _variableRet = enet::HTTPProtocol::http_3_2; return true; } + if (_value == "HTTP/3.3") { _variableRet = enet::HTTPProtocol::http_3_3; return true; } + if (_value == "HTTP/3.4") { _variableRet = enet::HTTPProtocol::http_3_4; return true; } + if (_value == "HTTP/3.5") { _variableRet = enet::HTTPProtocol::http_3_5; return true; } + if (_value == "HTTP/3.6") { _variableRet = enet::HTTPProtocol::http_3_6; return true; } + if (_value == "HTTP/3.7") { _variableRet = enet::HTTPProtocol::http_3_7; return true; } + if (_value == "HTTP/3.8") { _variableRet = enet::HTTPProtocol::http_3_8; return true; } + if (_value == "HTTP/3.9") { _variableRet = enet::HTTPProtocol::http_3_9; return true; } + if (_value == "HTTP/3.10") { _variableRet = enet::HTTPProtocol::http_3_10; return true; } + return false; + } + template <> + std::string to_string(const enum enet::HTTPProtocol& _value) { + switch (_value) { + case enet::HTTPProtocol::http_0_1: return "HTTP/0.1"; + case enet::HTTPProtocol::http_0_2: return "HTTP/0.2"; + case enet::HTTPProtocol::http_0_3: return "HTTP/0.3"; + case enet::HTTPProtocol::http_0_4: return "HTTP/0.4"; + case enet::HTTPProtocol::http_0_5: return "HTTP/0.5"; + case enet::HTTPProtocol::http_0_6: return "HTTP/0.6"; + case enet::HTTPProtocol::http_0_7: return "HTTP/0.7"; + case enet::HTTPProtocol::http_0_8: return "HTTP/0.8"; + case enet::HTTPProtocol::http_0_9: return "HTTP/0.9"; + case enet::HTTPProtocol::http_0_10: return "HTTP/0.10"; + case enet::HTTPProtocol::http_1_0: return "HTTP/1.0"; + case enet::HTTPProtocol::http_1_1: return "HTTP/1.1"; + case enet::HTTPProtocol::http_1_2: return "HTTP/1.2"; + case enet::HTTPProtocol::http_1_3: return "HTTP/1.3"; + case enet::HTTPProtocol::http_1_4: return "HTTP/1.4"; + case enet::HTTPProtocol::http_1_5: return "HTTP/1.5"; + case enet::HTTPProtocol::http_1_6: return "HTTP/1.6"; + case enet::HTTPProtocol::http_1_7: return "HTTP/1.7"; + case enet::HTTPProtocol::http_1_8: return "HTTP/1.8"; + case enet::HTTPProtocol::http_1_9: return "HTTP/1.9"; + case enet::HTTPProtocol::http_1_10: return "HTTP/1.10"; + case enet::HTTPProtocol::http_2_0: return "HTTP/2.0"; + case enet::HTTPProtocol::http_2_1: return "HTTP/2.1"; + case enet::HTTPProtocol::http_2_2: return "HTTP/2.2"; + case enet::HTTPProtocol::http_2_3: return "HTTP/2.3"; + case enet::HTTPProtocol::http_2_4: return "HTTP/2.4"; + case enet::HTTPProtocol::http_2_5: return "HTTP/2.5"; + case enet::HTTPProtocol::http_2_6: return "HTTP/2.6"; + case enet::HTTPProtocol::http_2_7: return "HTTP/2.7"; + case enet::HTTPProtocol::http_2_8: return "HTTP/2.8"; + case enet::HTTPProtocol::http_2_9: return "HTTP/2.9"; + case enet::HTTPProtocol::http_2_10: return "HTTP/2.10"; + case enet::HTTPProtocol::http_3_0: return "HTTP/3.0"; + case enet::HTTPProtocol::http_3_1: return "HTTP/3.1"; + case enet::HTTPProtocol::http_3_2: return "HTTP/3.2"; + case enet::HTTPProtocol::http_3_3: return "HTTP/3.3"; + case enet::HTTPProtocol::http_3_4: return "HTTP/3.4"; + case enet::HTTPProtocol::http_3_5: return "HTTP/3.5"; + case enet::HTTPProtocol::http_3_6: return "HTTP/3.6"; + case enet::HTTPProtocol::http_3_7: return "HTTP/3.7"; + case enet::HTTPProtocol::http_3_8: return "HTTP/3.8"; + case enet::HTTPProtocol::http_3_9: return "HTTP/3.9"; + case enet::HTTPProtocol::http_3_10: return "HTTP/3.10"; + } + return "HTTP/0.1"; + } +} + + +void enet::Http::setRequestHeader(const enet::HttpRequest& _req) { + m_requestHeader = _req; + if (m_requestHeader.getKey("User-Agent") == "") { + m_requestHeader.setKey("User-Agent", "e-net (ewol network interface)"); + } + std::string value = m_requestHeader.generate(); + write(value, false); +} + +void enet::Http::setAnswerHeader(const enet::HttpAnswer& _req) { + m_answerHeader = _req; + if (m_requestHeader.getKey("User-Agent") == "") { + m_requestHeader.setKey("User-Agent", "e-net (ewol network interface)"); + } + std::string value = m_answerHeader.generate(); + write(value, false); +} void enet::Http::getHeader() { ENET_VERBOSE("Read HTTP Header [START]"); @@ -219,41 +375,113 @@ void enet::Http::getHeader() { it.resize(it.size()-1); } } - headerEnded = false; - m_header.m_map.clear(); - for (auto element : list) { - if (headerEnded == false) { - headerEnded = true; - m_header.setReq(element); + //parse first element: + std::vector listLineOne = etk::split(list[0], ' '); + if (listLineOne.size() < 2) { + ENET_ERROR("can not parse answear : " << listLineOne); + // answer bad request and close connection ... + + return; + } + if ( listLineOne.size() >= 3 + && ( listLineOne[0] == "GET" + || listLineOne[0] == "POST" + || listLineOne[0] == "HEAD" + || listLineOne[0] == "DELETE" + || listLineOne[0] == "PUT" ) ) { + // HTTP CALL + if (m_isServer == false) { + // can not have call in client mode + ENET_ERROR("can not parse call in client mode ..." << listLineOne); + m_answerHeader.setErrorCode(enet::HTTPAnswerCode::c400_badRequest); + m_answerHeader.setHelp("Call a client with a request from server ..."); + setAnswerHeader(m_answerHeader); + stop(true); + return; + } + // get type call: + enum enet::HTTPReqType valueType; + etk::from_string(valueType, listLineOne[0]); + m_requestHeader.setType(valueType); + // get URI: + m_requestHeader.setUri(listLineOne[1]); + // Get http version: + enum enet::HTTPProtocol valueProtocol; + etk::from_string(valueProtocol, listLineOne[2]); + m_requestHeader.setProtocol(valueProtocol); + } else if ( listLineOne.size() >= 3 + && etk::start_with(listLineOne[0],"HTTP/") == true) { + // HTTP answer + if (m_isServer == true) { + // can not have anser ==> need to be a get ot something like this ... + ENET_ERROR("can not parse answer in server mode ..." << listLineOne); + m_answerHeader.setErrorCode(enet::HTTPAnswerCode::c400_badRequest); + m_answerHeader.setHelp("Call a client with a request from server ..."); + setAnswerHeader(m_answerHeader); + stop(true); + return; + } + // Get http version: + enum enet::HTTPProtocol valueProtocol; + etk::from_string(valueProtocol, listLineOne[0]); + m_answerHeader.setProtocol(valueProtocol); + + enum HTTPAnswerCode valueErrorCode; + etk::from_string(valueErrorCode, listLineOne[1]); + m_answerHeader.setErrorCode(valueErrorCode); + + // get comment: + std::string comment; + for (size_t iii=2; iii need to be a get ot something like this ... + ENET_ERROR("Un understand message ..." << listLineOne); + m_answerHeader.setErrorCode(enet::HTTPAnswerCode::c400_badRequest); + m_answerHeader.setHelp("Un understand message ..."); + setAnswerHeader(m_answerHeader); + stop(true); + return; + } + for (size_t iii=1; iii& _values) { - m_receiveData.clear(); m_header.m_map.clear(); // First create body : std::string body; @@ -302,7 +522,6 @@ bool enet::Http::post(const std::string& _address, const std::map list = etk::split(_req, ' '); - if (list.size() < 2) { - ENET_ERROR("can not parse answear : " << list); - return; + +void enet::HttpHeader::setKey(const std::string& _key, const std::string& _value) { + auto it = m_map.find(_key); + if (it == m_map.end()) { + m_map.insert(make_pair(_key, _value)); + } else { + it->second = _value; } - m_req = list[0]; - m_what = list[1]; - if ( m_req == "GET" - || m_req == "POST") { - // HTTP CALL - - } else if (etk::start_with(m_req,"HTTP/")==true) { - // HTTP answer - int32_t ret = etk::string_to_int32_t(m_what); - switch (ret/100) { - case 1: - // information message - break; - case 2: - // OK - break; - case 3: - // Redirect - ENET_WARNING("Rediret request"); - break; - case 4: - // client Error - ENET_WARNING("Client error"); - break; - case 5: - // server error - ENET_WARNING("Server error"); - break; +} + +void enet::HttpHeader::rmKey(const std::string& _key) { + auto it = m_map.find(_key); + if (it != m_map.end()) { + m_map.erase(it); + } +} + +std::string enet::HttpHeader::getKey(const std::string& _key) const { + auto it = m_map.find(_key); + if (it != m_map.end()) { + return it->second; + } + return ""; +} + +std::string enet::HttpHeader::generateKeys() const { + std::string out; + for (auto &it : m_map) { + if ( it.first != "" + && it.second != "") { + out += escapeChar(it.first) + " : " + escapeChar(it.second) + "\r\n"; + } + } + return out; +} + +enet::HttpHeader::HttpHeader(): + m_protocol(enet::HTTPProtocol::http_1_0) { + +} + + +// ----------------------------------------------------------------------------------------- + + +enet::HttpAnswer::HttpAnswer(enum HTTPAnswerCode _code, const std::string& _help): + m_what(_code), + m_helpMessage(_help) { + +} + +void enet::HttpAnswer::display() const { + ENET_PRINT("display header 'Answer' "); + ENET_PRINT(" protocol=" << m_protocol); + ENET_PRINT(" Code=" << int32_t(m_what)); + ENET_PRINT(" message=" << m_helpMessage); + ENET_PRINT(" Options:"); + for (auto &it : m_map) { + if ( it.first != "" + && it.second != "") { + ENET_PRINT(" '" + it.first + "' = '" + it.second + "'"); } } } +std::string enet::HttpAnswer::generate() const { + std::string out; + out += etk::to_string(m_protocol); + out += " "; + out += etk::to_string(int32_t(m_what)); + out += " "; + if (m_helpMessage != "") { + out += escapeChar(m_helpMessage); + } else { + auto it = protocolName.find(m_what); + if (it != protocolName.end()) { + out += escapeChar(it->second); + } else { + out += "???"; + } + } + out += "\r\n"; + out += generateKeys(); + out += "\r\n\r\n"; + return out; +} +enet::HttpServer::HttpServer(enet::Tcp _connection) : + enet::Http(std::move(_connection), true) { + +} -void enet::HttpHeader::display() const { - ENET_INFO("header :"); +enet::HttpClient::HttpClient(enet::Tcp _connection) : + enet::Http(std::move(_connection), false) { + +} +// ----------------------------------------------------------------------------------------- + +enet::HttpRequest::HttpRequest(enum enet::HTTPReqType _type): + m_req(_type), + m_uri() { + +} + +void enet::HttpRequest::display() const { + ENET_PRINT("display header 'Request' "); + ENET_PRINT(" type=" << m_req); + ENET_PRINT(" protocol=" << m_protocol); + ENET_PRINT(" uri=" << m_uri); + ENET_PRINT(" Options:"); for (auto &it : m_map) { - ENET_INFO(" key='" << it.first << "' value='" << it.second << "'"); + if ( it.first != "" + && it.second != "") { + ENET_PRINT(" '" + it.first + "' = '" + it.second + "'"); + } } } +std::string enet::HttpRequest::generate() const { + std::string out; + out += etk::to_string(m_req); + out += " "; + out += m_uri; + out += " "; + out += etk::to_string(m_protocol); + out += "\r\n"; + out += generateKeys(); + out += "\r\n\r\n"; + return out; +} + +std::ostream& enet::operator <<(std::ostream& _os, enum enet::HTTPProtocol _obj) { + _os << "enet::HTTPProtocol::" < m_map; enum HTTPProtocol m_protocol; public: - void addKey(const std::string& _key, const std::string& _value); + void setKey(const std::string& _key, const std::string& _value); void rmKey(const std::string& _key); - std::string getKey(const std::string& _key); - enum HTTPProtocol getProtocol() { + std::string getKey(const std::string& _key) const; + protected: + std::string generateKeys() const; + public: + enum HTTPProtocol getProtocol() const { return m_protocol; } void setProtocol(enum HTTPProtocol _protocol) { m_protocol = _protocol; } + HttpHeader(); virtual ~HttpHeader() = default; + virtual std::string generate() const = 0; }; + class HttpAnswer : public HttpHeader { private: - enet::HTTPAnswerCode m_what; - int64_t m_messageSize; // parameter + std::string m_helpMessage; public: - HttpAnswer(); - HttpAnswer(const std::string& _value); - get(const std::string& _uri); - setSize(int64_t _messageSize=-1); + HttpAnswer(enum HTTPAnswerCode _code = enet::HTTPAnswerCode::c400_badRequest, const std::string& _help=""); void display() const; - std::string generate(); + std::string generate() const; + void setErrorCode(enum HTTPAnswerCode _value) { + m_what = _value; + } + enum HTTPAnswerCode getErrorCode() { + return m_what; + } + void setHelp(const std::string& _value) { + m_helpMessage = _value; + } + const std::string& getHelp() { + return m_helpMessage; + } }; enum class HTTPReqType { GET, @@ -104,19 +162,28 @@ namespace enet { PUT, DELETE, }; - class HttpRequest : public HttpHeader{ + std::ostream& operator <<(std::ostream& _os, enum enet::HTTPReqType _obj); + class HttpRequest : public HttpHeader { private: // key, val - std::map m_parameters; enum HTTPReqType m_req; std::string m_uri; - enum HTTPProtocol m_protocol; - bool m_keepAlive; public: - HttpRequest(enum HTTPReqType _type, + HttpRequest(enum enet::HTTPReqType _type=enet::HTTPReqType::GET); void display() const; - std::string generate(); - + std::string generate() const; + void setType(enum enet::HTTPReqType _value) { + m_req = _value; + } + enum enet::HTTPReqType getType() const{ + return m_req; + } + void setUri(const std::string& _value) { + m_uri = _value; + } + const std::string& getUri() const { + return m_uri; + } }; class Http { public: @@ -128,26 +195,32 @@ namespace enet { bool getServerState() { return m_isServer; } - private: + bool isServer() { + return m_isServer; + } + protected: + enet::HttpRequest m_requestHeader; + void setRequestHeader(const enet::HttpRequest& _req); + public: + const enet::HttpRequest& getRequestHeader() { + return m_requestHeader; + } + protected: + enet::HttpAnswer m_answerHeader; + void setAnswerHeader(const enet::HttpAnswer& _req); + public: + const enet::HttpAnswer& getAnswerHeader() { + return m_answerHeader; + } + protected: enet::Tcp m_connection; bool m_headerIsSend; std::thread* m_thread; bool m_threadRunning; std::vector m_temporaryBuffer; private: - bool m_keepAlive; void threadCallback(); - public: - void setKeepAlive(bool _keepAlive) { - m_keepAlive = true; - } - bool getKeepAlive() { - return m_keepAlive; - } private: - - HttpHeader m_header; - std::vector m_receiveData; void getHeader(); public: void start(); @@ -155,18 +228,8 @@ namespace enet { bool isAlive() { return m_connection.getConnectionStatus() == enet::Tcp::status::link; } - void setSendHeaderProperties(const std::string& _key, const std::string& _val); - std::string getSendHeaderProperties(const std::string& _key); - std::string getReceiveHeaderProperties(const std::string& _key); - bool get(const std::string& _address); - bool post(const std::string& _address, const std::map& _values); - bool post(const std::string& _address, const std::string& _contentType, const std::string& _data); - std::string dataString(); - void writeAnswerHeader(enum enet::HTTPAnswerCode _value); - std::string escapeChar(const std::string& _value); - std::string unEscapeChar(const std::string& _value); public: - using Observer = std::function&)>; //!< Define an Observer: function pointer + using Observer = std::function&)>; //!< Define an Observer: function pointer Observer m_observer; /** * @brief Connect an function member on the signal with the shared_ptr object. @@ -175,32 +238,23 @@ namespace enet { * @param[in] _args Argument optinnal the user want to add. */ template - void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(enet::Http& _interface, std::vector&)) { - m_observer = [=](enet::Http& _interface, std::vector& _value){ - (*_class.*_func)(_interface,_value); + void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(std::vector&)) { + m_observer = [=](std::vector& _value){ + (*_class.*_func)(_value); }; } void connect(Observer _func) { m_observer = _func; } public: - using ObserverRequest = std::function; //!< Define an Observer: function pointer + using ObserverRequest = std::function; //!< Define an Observer: function pointer + protected: ObserverRequest m_observerRequest; - /** - * @brief Connect an function member on the signal with the shared_ptr object. - * @param[in] _class shared_ptr Object on whe we need to call ==> the object is get in keeped in weak_ptr. - * @param[in] _func Function to call. - * @param[in] _args Argument optinnal the user want to add. - */ - template - void connectHeader(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(enet::Http& _interface, const enet::HttpHeader&)) { - m_observerRequest = [=](enet::Http& _interface, std::vector& _value){ - (*_class.*_func)(_value); - }; - } - void connectHeader(ObserverRequest _func) { - m_observerRequest = _func; - } + public: + using ObserverAnswer = std::function; //!< Define an Observer: function pointer + protected: + ObserverAnswer m_observerAnswer; + public: /** * @brief Write a chunk of data on the socket * @param[in] _data pointer on the data might be write @@ -244,5 +298,59 @@ namespace enet { return ret/sizeof(T); } }; + + class HttpClient : public Http { + public: + HttpClient(enet::Tcp _connection); + public: + void setHeader(const enet::HttpRequest& _header) { + setRequestHeader(_header); + } + public: + //bool get(const std::string& _address); + //bool post(const std::string& _address, const std::map& _values); + //bool post(const std::string& _address, const std::string& _contentType, const std::string& _data); + public: + /** + * @brief Connect an function member on the signal with the shared_ptr object. + * @param[in] _class shared_ptr Object on whe we need to call ==> the object is get in keeped in weak_ptr. + * @param[in] _func Function to call. + * @param[in] _args Argument optinnal the user want to add. + */ + template + void connectHeader(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(const enet::HttpAnswer&)) { + m_observerAnswer = [=](const enet::HttpAnswer& _value){ + (*_class.*_func)(_value); + }; + } + void connectHeader(Http::ObserverAnswer _func) { + m_observerAnswer = _func; + } + }; + + class HttpServer : public Http { + public: + HttpServer(enet::Tcp _connection); + public: + void setHeader(const enet::HttpAnswer& _header) { + setAnswerHeader(_header); + } + public: + /** + * @brief Connect an function member on the signal with the shared_ptr object. + * @param[in] _class shared_ptr Object on whe we need to call ==> the object is get in keeped in weak_ptr. + * @param[in] _func Function to call. + * @param[in] _args Argument optinnal the user want to add. + */ + template + void connectHeader(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(const enet::HttpRequest&)) { + m_observerRequest = [=](const enet::HttpRequest& _value){ + (*_class.*_func)(_value); + }; + } + void connectHeader(Http::ObserverRequest _func) { + m_observerRequest = _func; + } + }; } diff --git a/enet/WebSocket.cpp b/enet/WebSocket.cpp index 9801d27..49eb71e 100644 --- a/enet/WebSocket.cpp +++ b/enet/WebSocket.cpp @@ -13,26 +13,79 @@ enet::WebSocket::WebSocket(enet::Tcp _connection, bool _isServer) : - m_interface(std::move(_connection), _isServer) { - + m_interface(), + m_observer(nullptr) { + if (_isServer == true) { + ememory::SharedPtr interface = std::make_shared(std::move(_connection)); + interface->connectHeader(this, &enet::WebSocket::onReceiveRequest); + m_interface = interface; + } else { + ememory::SharedPtr interface = std::make_shared(std::move(_connection)); + interface->connectHeader(this, &enet::WebSocket::onReceiveAnswer); + m_interface = interface; + } + m_interface->connect(this, &enet::WebSocket::onReceiveData); } enet::WebSocket::~WebSocket() { - + if (m_interface == nullptr) { + return; + } + stop(true); } void enet::WebSocket::start(const std::string& _uri) { - if (m_interface.isServer() == true) { - + if (m_interface == nullptr) { + ENET_ERROR("Nullptr interface ..."); + return; } - m_interface.start(); - if (m_interface.isServer() == false) { - m_interface.get( + m_interface->start(); + if (m_interface->isServer() == false) { + enet::HttpRequest req(enet::HTTPReqType::GET); + req.setUri(_uri); + std::dynamic_pointer_cast(m_interface)->setHeader(req); } } -void enet::WebSocket::stop(bool _inThread=false) { - +void enet::WebSocket::stop(bool _inThread) { + if (m_interface == nullptr) { + ENET_ERROR("Nullptr interface ..."); + return; + } + m_interface->stop(_inThread); + m_interface.reset(); } +void enet::WebSocket::onReceiveData(std::vector& _data) { + if (m_interface == nullptr) { + ENET_ERROR("Nullptr interface ..."); + return; + } + ENET_ERROR("manage receive data event ..."); +} + +void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) { + if (m_interface == nullptr) { + ENET_ERROR("Nullptr interface ..."); + return; + } + _data.display(); +} + +void enet::WebSocket::onReceiveAnswer(const enet::HttpAnswer& _data) { + if (m_interface == nullptr) { + ENET_ERROR("Nullptr interface ..."); + return; + } + _data.display(); +} + +int32_t enet::WebSocket::write(const void* _data, int32_t _len) { + if (m_interface == nullptr) { + ENET_ERROR("Nullptr interface ..."); + return -1; + } + // TODO : ... + return -1; +} \ No newline at end of file diff --git a/enet/WebSocket.h b/enet/WebSocket.h index f4c6f54..3b050dc 100644 --- a/enet/WebSocket.h +++ b/enet/WebSocket.h @@ -5,18 +5,88 @@ */ #pragma once -#include +#include +#include #include #include namespace enet { - class WebSocket{ + class WebSocket { private: - enet::Http m_interface; + ememory::SharedPtr m_interface; public: WebSocket(enet::Tcp _connection, bool _isServer=false); virtual ~WebSocket(); - void start(const std::string& _uri); + void start(const std::string& _uri=""); void stop(bool _inThread=false); + bool isAlive() { + return m_interface->isAlive(); + } + void onReceiveData(std::vector& _data); + void onReceiveRequest(const enet::HttpRequest& _data); + void onReceiveAnswer(const enet::HttpAnswer& _data); + public: + using Observer = std::function&)>; //!< Define an Observer: function pointer + private: + Observer m_observer; + public: + /** + * @brief Connect an function member on the signal with the shared_ptr object. + * @param[in] _class shared_ptr Object on whe we need to call ==> the object is get in keeped in weak_ptr. + * @param[in] _func Function to call. + * @param[in] _args Argument optinnal the user want to add. + */ + template + void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(std::vector&)) { + m_observer = [=](std::vector& _value){ + (*_class.*_func)(_value); + }; + } + void connect(Observer _func) { + m_observer = _func; + } + public: + /** + * @brief Write a chunk of data on the socket + * @param[in] _data pointer on the data might be write + * @param[in] _len Size that must be written socket + * @return >0 byte size on the socket write + * @return -1 an error occured. + */ + int32_t write(const void* _data, int32_t _len); + /** + * @brief Write a chunk of data on the socket + * @param[in] _data String to rite on the soccket + * @param[in] _writeBackSlashZero if false, the \0 is not write + * @return >0 byte size on the socket write + * @return -1 an error occured. + */ + int32_t write(const std::string& _data, bool _writeBackSlashZero = true) { + if (_data.size() == 0) { + return 0; + } + if (_writeBackSlashZero == true) { + return write(_data.c_str(), _data.size()+1); + } + return write(_data.c_str(), _data.size()); + } + /** + * @brief Write a chunk of data on the socket + * @param[in] _data String to rite on the soccket + * @param[in] _writeBackSlashZero if false, the \0 is not write + * @return >0 T element write on the socket + * @return -1 an error occured. + */ + template + int32_t write(const std::vector& _data) { + if (_data.size() == 0) { + return 0; + } + size_t ret = write(&_data[0], _data.size()*sizeof(T)); + if (ret <=0) { + return ret; + } + return ret/sizeof(T); + } }; } diff --git a/enet/debug.h b/enet/debug.h index 80a0ad7..28c362e 100644 --- a/enet/debug.h +++ b/enet/debug.h @@ -13,6 +13,7 @@ namespace enet { #define ENET_BASE(info,data) ELOG_BASE(enet::getLogId(),info,data) +#define ENET_PRINT(data) ENET_BASE(-1, data) #define ENET_CRITICAL(data) ENET_BASE(1, data) #define ENET_ERROR(data) ENET_BASE(2, data) #define ENET_WARNING(data) ENET_BASE(3, data) diff --git a/lutin_enet.py b/lutin_enet.py index a397555..7ddd76e 100644 --- a/lutin_enet.py +++ b/lutin_enet.py @@ -26,7 +26,7 @@ def get_version(): def create(target, module_name): my_module = module.Module(__file__, module_name, get_type()) - my_module.add_module_depend(['etk']) + my_module.add_module_depend(['etk', 'ememory']) my_module.add_src_file([ 'enet/debug.cpp' ]) @@ -40,6 +40,7 @@ def create(target, module_name): 'enet/TcpClient.cpp', 'enet/Http.cpp', 'enet/Ftp.cpp', + 'enet/WebSocket.cpp', ]) my_module.add_header_file([ 'enet/debug.h', @@ -49,6 +50,7 @@ def create(target, module_name): 'enet/TcpClient.h', 'enet/Http.h', 'enet/Ftp.h', + 'enet/WebSocket.h', ]) return my_module diff --git a/test/main-client-http.cpp b/test/main-client-http.cpp index 3ba6c42..2efe30e 100644 --- a/test/main-client-http.cpp +++ b/test/main-client-http.cpp @@ -14,7 +14,7 @@ #include namespace appl { - void onReceiveData(enet::Http& _interface, std::vector& _data) { + void onReceiveData(std::vector& _data) { TEST_INFO("Receive Datas : " << _data.size() << " bytes"); TEST_INFO("data:" << (char*)&_data[0] << ""); } @@ -41,15 +41,15 @@ int main(int _argc, const char *_argv[]) { // TODO : Check if connection is valid ... // Create a HTTP connection in Client mode - enet::Http connection(std::move(tcpConnection), false); - connection.setKeepAlive(true); + enet::HttpClient connection(std::move(tcpConnection)); // Set callbacks: connection.connect(appl::onReceiveData); // start http connection (the actual state is just TCP start ...) connection.start(); - connection.get("plop.txt"); - + enet::HttpRequest req(enet::HTTPReqType::GET); + req.setUri("plop.txt"); + connection.setHeader(req); while (connection.isAlive() == true) { usleep(100000); diff --git a/test/main-server-http.cpp b/test/main-server-http.cpp index 5cf7dcf..756e697 100644 --- a/test/main-server-http.cpp +++ b/test/main-server-http.cpp @@ -13,23 +13,27 @@ #include #include namespace appl { - void onReceiveData(enet::Http& _interface, std::vector& _data) { + void onReceiveData(enet::HttpServer* _interface, std::vector& _data) { TEST_INFO("Receive Datas : " << _data.size() << " bytes"); } - void onReceiveHeader(enet::Http& _interface, const enet::HttpHeader& _data) { + void onReceiveHeader(enet::HttpServer* _interface, const enet::HttpRequest& _data) { TEST_INFO("Receive Header data:"); _data.display(); - if (_data.m_req == "GET") { - if (_data.m_what == "http://127.0.0.1:12345/plop.txt") { - _interface.writeAnswerHeader(enet::HTTPAnswerCode::c200_ok); + if (_data.getType() == enet::HTTPReqType::GET) { + if (_data.getUri() == "http://127.0.0.1:12345/plop.txt") { + enet::HttpAnswer answer(enet::HTTPAnswerCode::c200_ok); std::string data = "coucou"; - _interface.write(data); - _interface.stop(); + answer.setKey("Content-Length", etk::to_string(data.size())); + _interface->setHeader(answer); + _interface->write(data); + _interface->stop(); return; } } - _interface.writeAnswerHeader(enet::HTTPAnswerCode::c200_ok); - _interface.stop(); + enet::HttpAnswer answer(enet::HTTPAnswerCode::c404_notFound); + answer.setKey("Connection", "close"); + _interface->setHeader(answer); + _interface->stop(); } } @@ -63,11 +67,15 @@ int main(int _argc, const char *_argv[]) { // TODO : Check if connection is valid ... // Create a HTTP connection in Server mode - enet::Http connection(std::move(tcpConnection), true); - connection.setKeepAlive(true); + enet::HttpServer connection(std::move(tcpConnection)); + enet::HttpServer* tmp = &connection; // Set callbacks: - connection.connect(appl::onReceiveData); - connection.connectHeader(appl::onReceiveHeader); + connection.connect([=](std::vector& _value){ + appl::onReceiveData(tmp, _value); + }); + connection.connectHeader([=](const enet::HttpRequest& _value){ + appl::onReceiveHeader(tmp, _value); + }); // start http connection (the actual state is just TCP start ...) connection.start();