[DEV] HTTP start to be ok and websocket start to be implemented ...

This commit is contained in:
Edouard DUPIN 2016-06-17 22:45:19 +02:00
parent 97acf584f8
commit 83c9cf69b0
8 changed files with 747 additions and 200 deletions

View File

@ -11,6 +11,13 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
static std::string escapeChar(const std::string& _value) {
return _value;
}
static std::string unEscapeChar(const std::string& _value) {
return _value;
}
static std::map<enet::HTTPAnswerCode, std::string> protocolName = { static std::map<enet::HTTPAnswerCode, std::string> protocolName = {
{enet::HTTPAnswerCode::c100_continue, "Continue"}, {enet::HTTPAnswerCode::c100_continue, "Continue"},
{enet::HTTPAnswerCode::c101_switchingProtocols, "Switching Protocols"}, {enet::HTTPAnswerCode::c101_switchingProtocols, "Switching Protocols"},
@ -69,12 +76,13 @@ enet::Http::Http(enet::Tcp _connection, bool _isServer) :
m_connection(std::move(_connection)), m_connection(std::move(_connection)),
m_headerIsSend(false), m_headerIsSend(false),
m_thread(nullptr), m_thread(nullptr),
m_threadRunning(false), m_threadRunning(false) {
m_keepAlive(false) { //setSendHeaderProperties("User-Agent", "e-net (ewol network interface)");
setSendHeaderProperties("User-Agent", "e-net (ewol network interface)"); /*
if (m_keepAlive == true) { if (m_keepAlive == true) {
setSendHeaderProperties("Connection", "Keep-Alive"); setSendHeaderProperties("Connection", "Keep-Alive");
} }
*/
} }
enet::Http::~Http() { enet::Http::~Http() {
@ -97,7 +105,7 @@ void enet::Http::threadCallback() {
if (len > 0) { if (len > 0) {
ENET_INFO("Call client with datas ..."); ENET_INFO("Call client with datas ...");
if (m_observer != nullptr) { 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]"); 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) { void enet::Http::writeAnswerHeader(enum enet::HTTPAnswerCode _value) {
std::string out; std::string out;
out = "HTTP/1.1 "; out = "HTTP/1.1 ";
@ -177,6 +167,172 @@ void enet::Http::writeAnswerHeader(enum enet::HTTPAnswerCode _value) {
ENET_WARNING("Write header :" << out); ENET_WARNING("Write header :" << out);
write(out, false); write(out, false);
} }
*/
namespace etk {
template <>
bool from_string<enum enet::HTTPAnswerCode>(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<enum enet::HTTPAnswerCode>(const enum enet::HTTPAnswerCode& _value) {
return etk::to_string(int32_t(_value));
}
template <>
bool from_string<enum enet::HTTPReqType>(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<enum enet::HTTPReqType>(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>(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<enum enet::HTTPProtocol>(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() { void enet::Http::getHeader() {
ENET_VERBOSE("Read HTTP Header [START]"); ENET_VERBOSE("Read HTTP Header [START]");
@ -219,41 +375,113 @@ void enet::Http::getHeader() {
it.resize(it.size()-1); it.resize(it.size()-1);
} }
} }
headerEnded = false; //parse first element:
m_header.m_map.clear(); std::vector<std::string> listLineOne = etk::split(list[0], ' ');
for (auto element : list) { if (listLineOne.size() < 2) {
if (headerEnded == false) { ENET_ERROR("can not parse answear : " << listLineOne);
headerEnded = true; // answer bad request and close connection ...
m_header.setReq(element);
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<listLineOne.size(); ++iii) {
if (comment.size() != 0) {
comment += " ";
}
comment += listLineOne[iii];
}
m_answerHeader.setHelp(comment);
} else {
// can not have anser ==> 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<list.size(); ++iii) {
size_t found = list[iii].find(":");
if (found == std::string::npos) {
// nothing
continue;
}
std::string key = unEscapeChar(std::string(list[iii], 0, found));
std::string value = unEscapeChar(std::string(list[iii], found+2));
ENET_VERBOSE("header : key='" << key << "' value='" << value << "'");
if (m_isServer == false) {
m_answerHeader.setKey(key,value);
} else { } else {
size_t found = element.find(":"); m_requestHeader.setKey(key,value);
if (found == std::string::npos) { }
// nothing if ( key == "Connection"
continue; && value == "close") {
} ENET_DEBUG("connection closed by remote :");
ENET_VERBOSE("header : key='" << std::string(element, 0, found) << "' value='" << std::string(element, found+2) << "'"); m_connection.unlink();
m_header.m_map.insert(make_pair(unEscapeChar(std::string(element, 0, found)), unEscapeChar(std::string(element, found+2))));
} }
} }
for (auto &it : m_header.m_map) { if (m_isServer == false) {
if (it.first == "Connection") { if (m_observerAnswer != nullptr) {
if (it.second == "close") { m_observerAnswer(m_answerHeader);
ENET_DEBUG("connection closed by remote :"); }
m_connection.unlink(); } else {
} else { if (m_observerRequest != nullptr) {
ENET_TODO("manage connection type : '" << it.second); m_observerRequest(m_requestHeader);
}
} }
}
m_header.display();
if (m_observerRequest != nullptr) {
m_observerRequest(*this, m_header);
} }
} }
/*
bool enet::Http::get(const std::string& _address) { bool enet::Http::get(const std::string& _address) {
m_receiveData.clear();
m_header.m_map.clear(); m_header.m_map.clear();
std::string req = "GET http://" + m_connection.getName(); std::string req = "GET http://" + m_connection.getName();
if (_address != "") { if (_address != "") {
@ -280,15 +508,7 @@ bool enet::Http::get(const std::string& _address) {
return false; return false;
} }
std::string enet::Http::escapeChar(const std::string& _value) {
return _value;
}
std::string enet::Http::unEscapeChar(const std::string& _value) {
return _value;
}
bool enet::Http::post(const std::string& _address, const std::map<std::string, std::string>& _values) { bool enet::Http::post(const std::string& _address, const std::map<std::string, std::string>& _values) {
m_receiveData.clear();
m_header.m_map.clear(); m_header.m_map.clear();
// First create body : // First create body :
std::string body; std::string body;
@ -302,7 +522,6 @@ bool enet::Http::post(const std::string& _address, const std::map<std::string, s
} }
bool enet::Http::post(const std::string& _address, const std::string& _contentType, const std::string& _data) { bool enet::Http::post(const std::string& _address, const std::string& _contentType, const std::string& _data) {
m_receiveData.clear();
m_header.m_map.clear(); m_header.m_map.clear();
std::string req = "POST http://" + m_connection.getName(); std::string req = "POST http://" + m_connection.getName();
if (_address != "") { if (_address != "") {
@ -329,68 +548,154 @@ bool enet::Http::post(const std::string& _address, const std::string& _contentTy
//return receiveData(); //return receiveData();
return false; return false;
} }
*/
std::string enet::Http::dataString() {
std::string data;
for (auto element : m_receiveData) {
if (element == '\0') {
return data;
}
data += element;
}
return data;
}
int32_t enet::Http::write(const void* _data, int32_t _len) { int32_t enet::Http::write(const void* _data, int32_t _len) {
return m_connection.write(_data, _len); return m_connection.write(_data, _len);
} }
void enet::HttpHeader::setReq(const std::string& _req) {
// parse base answear: void enet::HttpHeader::setKey(const std::string& _key, const std::string& _value) {
std::vector<std::string> list = etk::split(_req, ' '); auto it = m_map.find(_key);
if (list.size() < 2) { if (it == m_map.end()) {
ENET_ERROR("can not parse answear : " << list); m_map.insert(make_pair(_key, _value));
return; } else {
it->second = _value;
} }
m_req = list[0]; }
m_what = list[1];
if ( m_req == "GET" void enet::HttpHeader::rmKey(const std::string& _key) {
|| m_req == "POST") { auto it = m_map.find(_key);
// HTTP CALL if (it != m_map.end()) {
m_map.erase(it);
} else if (etk::start_with(m_req,"HTTP/")==true) { }
// HTTP answer }
int32_t ret = etk::string_to_int32_t(m_what);
switch (ret/100) { std::string enet::HttpHeader::getKey(const std::string& _key) const {
case 1: auto it = m_map.find(_key);
// information message if (it != m_map.end()) {
break; return it->second;
case 2: }
// OK return "";
break; }
case 3:
// Redirect std::string enet::HttpHeader::generateKeys() const {
ENET_WARNING("Rediret request"); std::string out;
break; for (auto &it : m_map) {
case 4: if ( it.first != ""
// client Error && it.second != "") {
ENET_WARNING("Client error"); out += escapeChar(it.first) + " : " + escapeChar(it.second) + "\r\n";
break; }
case 5: }
// server error return out;
ENET_WARNING("Server error"); }
break;
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::HttpClient::HttpClient(enet::Tcp _connection) :
ENET_INFO("header :"); 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) { 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::" <<etk::to_string(_obj);
return _os;
}
std::ostream& enet::operator <<(std::ostream& _os, enum enet::HTTPAnswerCode _obj) {
_os << "enet::HTTPAnswerCode::" << etk::to_string(_obj);
return _os;
}
std::ostream& enet::operator <<(std::ostream& _os, enum enet::HTTPReqType _obj) {
_os << "enet::HTTPReqType::" << etk::to_string(_obj);
return _os;
}

View File

@ -13,6 +13,7 @@
namespace enet { namespace enet {
enum class HTTPAnswerCode { enum class HTTPAnswerCode {
c000_unknow = 0,
//1xx: Information //1xx: Information
c100_continue = 100, //!< The server has received the request headers, and the client should proceed to send the request body c100_continue = 100, //!< The server has received the request headers, and the client should proceed to send the request body
c101_switchingProtocols, //!< The requester has asked the server to switch protocols c101_switchingProtocols, //!< The requester has asked the server to switch protocols
@ -62,40 +63,97 @@ namespace enet {
c505_httpVersionNotSupported, //!< The server does not support the HTTP protocol version used in the request c505_httpVersionNotSupported, //!< The server does not support the HTTP protocol version used in the request
c511_networkAuthenticationRequired, //!< The client needs to authenticate to gain network access c511_networkAuthenticationRequired, //!< The client needs to authenticate to gain network access
}; };
std::ostream& operator <<(std::ostream& _os, enum enet::HTTPAnswerCode _obj);
enum class HTTPProtocol { enum class HTTPProtocol {
http_0_1,
http_0_2,
http_0_3,
http_0_4,
http_0_5,
http_0_6,
http_0_7,
http_0_8,
http_0_9,
http_0_10,
http_1_0, http_1_0,
http_1_1, http_1_1,
http_1_2,
http_1_3,
http_1_4,
http_1_5,
http_1_6,
http_1_7,
http_1_8,
http_1_9,
http_1_10,
http_2_0,
http_2_1,
http_2_2,
http_2_3,
http_2_4,
http_2_5,
http_2_6,
http_2_7,
http_2_8,
http_2_9,
http_2_10,
http_3_0,
http_3_1,
http_3_2,
http_3_3,
http_3_4,
http_3_5,
http_3_6,
http_3_7,
http_3_8,
http_3_9,
http_3_10,
}; };
std::ostream& operator <<(std::ostream& _os, enum enet::HTTPProtocol _obj);
class HttpHeader { class HttpHeader {
private: protected:
// key, val // key, val
std::map<std::string, std::string> m_map; std::map<std::string, std::string> m_map;
enum HTTPProtocol m_protocol; enum HTTPProtocol m_protocol;
public: 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); void rmKey(const std::string& _key);
std::string getKey(const std::string& _key); std::string getKey(const std::string& _key) const;
enum HTTPProtocol getProtocol() { protected:
std::string generateKeys() const;
public:
enum HTTPProtocol getProtocol() const {
return m_protocol; return m_protocol;
} }
void setProtocol(enum HTTPProtocol _protocol) { void setProtocol(enum HTTPProtocol _protocol) {
m_protocol = _protocol; m_protocol = _protocol;
} }
HttpHeader();
virtual ~HttpHeader() = default; virtual ~HttpHeader() = default;
virtual std::string generate() const = 0;
}; };
class HttpAnswer : public HttpHeader { class HttpAnswer : public HttpHeader {
private: private:
enet::HTTPAnswerCode m_what; enet::HTTPAnswerCode m_what;
int64_t m_messageSize; // parameter std::string m_helpMessage;
public: public:
HttpAnswer(); HttpAnswer(enum HTTPAnswerCode _code = enet::HTTPAnswerCode::c400_badRequest, const std::string& _help="");
HttpAnswer(const std::string& _value);
get(const std::string& _uri);
setSize(int64_t _messageSize=-1);
void display() const; 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 { enum class HTTPReqType {
GET, GET,
@ -104,19 +162,28 @@ namespace enet {
PUT, PUT,
DELETE, DELETE,
}; };
class HttpRequest : public HttpHeader{ std::ostream& operator <<(std::ostream& _os, enum enet::HTTPReqType _obj);
class HttpRequest : public HttpHeader {
private: private:
// key, val // key, val
std::map<std::string, std::string> m_parameters;
enum HTTPReqType m_req; enum HTTPReqType m_req;
std::string m_uri; std::string m_uri;
enum HTTPProtocol m_protocol;
bool m_keepAlive;
public: public:
HttpRequest(enum HTTPReqType _type, HttpRequest(enum enet::HTTPReqType _type=enet::HTTPReqType::GET);
void display() const; 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 { class Http {
public: public:
@ -128,26 +195,32 @@ namespace enet {
bool getServerState() { bool getServerState() {
return m_isServer; 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; enet::Tcp m_connection;
bool m_headerIsSend; bool m_headerIsSend;
std::thread* m_thread; std::thread* m_thread;
bool m_threadRunning; bool m_threadRunning;
std::vector<uint8_t> m_temporaryBuffer; std::vector<uint8_t> m_temporaryBuffer;
private: private:
bool m_keepAlive;
void threadCallback(); void threadCallback();
public:
void setKeepAlive(bool _keepAlive) {
m_keepAlive = true;
}
bool getKeepAlive() {
return m_keepAlive;
}
private: private:
HttpHeader m_header;
std::vector<uint8_t> m_receiveData;
void getHeader(); void getHeader();
public: public:
void start(); void start();
@ -155,18 +228,8 @@ namespace enet {
bool isAlive() { bool isAlive() {
return m_connection.getConnectionStatus() == enet::Tcp::status::link; 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<std::string, std::string>& _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: public:
using Observer = std::function<void(enet::Http& _interface, std::vector<uint8_t>&)>; //!< Define an Observer: function pointer using Observer = std::function<void(std::vector<uint8_t>&)>; //!< Define an Observer: function pointer
Observer m_observer; Observer m_observer;
/** /**
* @brief Connect an function member on the signal with the shared_ptr object. * @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. * @param[in] _args Argument optinnal the user want to add.
*/ */
template<class CLASS_TYPE> template<class CLASS_TYPE>
void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(enet::Http& _interface, std::vector<uint8_t>&)) { void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(std::vector<uint8_t>&)) {
m_observer = [=](enet::Http& _interface, std::vector<uint8_t>& _value){ m_observer = [=](std::vector<uint8_t>& _value){
(*_class.*_func)(_interface,_value); (*_class.*_func)(_value);
}; };
} }
void connect(Observer _func) { void connect(Observer _func) {
m_observer = _func; m_observer = _func;
} }
public: public:
using ObserverRequest = std::function<void(enet::Http& _interface, const enet::HttpHeader&)>; //!< Define an Observer: function pointer using ObserverRequest = std::function<void(const enet::HttpRequest&)>; //!< Define an Observer: function pointer
protected:
ObserverRequest m_observerRequest; ObserverRequest m_observerRequest;
/** public:
* @brief Connect an function member on the signal with the shared_ptr object. using ObserverAnswer = std::function<void(const enet::HttpAnswer&)>; //!< Define an Observer: function pointer
* @param[in] _class shared_ptr Object on whe we need to call ==> the object is get in keeped in weak_ptr. protected:
* @param[in] _func Function to call. ObserverAnswer m_observerAnswer;
* @param[in] _args Argument optinnal the user want to add. public:
*/
template<class CLASS_TYPE>
void connectHeader(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(enet::Http& _interface, const enet::HttpHeader&)) {
m_observerRequest = [=](enet::Http& _interface, std::vector<uint8_t>& _value){
(*_class.*_func)(_value);
};
}
void connectHeader(ObserverRequest _func) {
m_observerRequest = _func;
}
/** /**
* @brief Write a chunk of data on the socket * @brief Write a chunk of data on the socket
* @param[in] _data pointer on the data might be write * @param[in] _data pointer on the data might be write
@ -244,5 +298,59 @@ namespace enet {
return ret/sizeof(T); 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<std::string, std::string>& _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<class CLASS_TYPE>
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<class CLASS_TYPE>
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;
}
};
} }

View File

@ -13,26 +13,79 @@
enet::WebSocket::WebSocket(enet::Tcp _connection, bool _isServer) : enet::WebSocket::WebSocket(enet::Tcp _connection, bool _isServer) :
m_interface(std::move(_connection), _isServer) { m_interface(),
m_observer(nullptr) {
if (_isServer == true) {
ememory::SharedPtr<enet::HttpServer> interface = std::make_shared<enet::HttpServer>(std::move(_connection));
interface->connectHeader(this, &enet::WebSocket::onReceiveRequest);
m_interface = interface;
} else {
ememory::SharedPtr<enet::HttpClient> interface = std::make_shared<enet::HttpClient>(std::move(_connection));
interface->connectHeader(this, &enet::WebSocket::onReceiveAnswer);
m_interface = interface;
}
m_interface->connect(this, &enet::WebSocket::onReceiveData);
} }
enet::WebSocket::~WebSocket() { enet::WebSocket::~WebSocket() {
if (m_interface == nullptr) {
return;
}
stop(true);
} }
void enet::WebSocket::start(const std::string& _uri) { 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(); m_interface->start();
if (m_interface.isServer() == false) { if (m_interface->isServer() == false) {
m_interface.get( enet::HttpRequest req(enet::HTTPReqType::GET);
req.setUri(_uri);
std::dynamic_pointer_cast<enet::HttpClient>(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<uint8_t>& _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;
}

View File

@ -5,18 +5,88 @@
*/ */
#pragma once #pragma once
#include <enet/Tcp.h> #include <enet/Http.h>
#include <ememory/memory.h>
#include <vector> #include <vector>
#include <map> #include <map>
namespace enet { namespace enet {
class WebSocket{ class WebSocket {
private: private:
enet::Http m_interface; ememory::SharedPtr<enet::Http> m_interface;
public: public:
WebSocket(enet::Tcp _connection, bool _isServer=false); WebSocket(enet::Tcp _connection, bool _isServer=false);
virtual ~WebSocket(); virtual ~WebSocket();
void start(const std::string& _uri); void start(const std::string& _uri="");
void stop(bool _inThread=false); void stop(bool _inThread=false);
bool isAlive() {
return m_interface->isAlive();
}
void onReceiveData(std::vector<uint8_t>& _data);
void onReceiveRequest(const enet::HttpRequest& _data);
void onReceiveAnswer(const enet::HttpAnswer& _data);
public:
using Observer = std::function<void(std::vector<uint8_t>&)>; //!< 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<class CLASS_TYPE>
void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(std::vector<uint8_t>&)) {
m_observer = [=](std::vector<uint8_t>& _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 <class T>
int32_t write(const std::vector<T>& _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);
}
}; };
} }

View File

@ -13,6 +13,7 @@ namespace enet {
#define ENET_BASE(info,data) ELOG_BASE(enet::getLogId(),info,data) #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_CRITICAL(data) ENET_BASE(1, data)
#define ENET_ERROR(data) ENET_BASE(2, data) #define ENET_ERROR(data) ENET_BASE(2, data)
#define ENET_WARNING(data) ENET_BASE(3, data) #define ENET_WARNING(data) ENET_BASE(3, data)

View File

@ -26,7 +26,7 @@ def get_version():
def create(target, module_name): def create(target, module_name):
my_module = module.Module(__file__, module_name, get_type()) 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([ my_module.add_src_file([
'enet/debug.cpp' 'enet/debug.cpp'
]) ])
@ -40,6 +40,7 @@ def create(target, module_name):
'enet/TcpClient.cpp', 'enet/TcpClient.cpp',
'enet/Http.cpp', 'enet/Http.cpp',
'enet/Ftp.cpp', 'enet/Ftp.cpp',
'enet/WebSocket.cpp',
]) ])
my_module.add_header_file([ my_module.add_header_file([
'enet/debug.h', 'enet/debug.h',
@ -49,6 +50,7 @@ def create(target, module_name):
'enet/TcpClient.h', 'enet/TcpClient.h',
'enet/Http.h', 'enet/Http.h',
'enet/Ftp.h', 'enet/Ftp.h',
'enet/WebSocket.h',
]) ])
return my_module return my_module

View File

@ -14,7 +14,7 @@
#include <unistd.h> #include <unistd.h>
namespace appl { namespace appl {
void onReceiveData(enet::Http& _interface, std::vector<uint8_t>& _data) { void onReceiveData(std::vector<uint8_t>& _data) {
TEST_INFO("Receive Datas : " << _data.size() << " bytes"); TEST_INFO("Receive Datas : " << _data.size() << " bytes");
TEST_INFO("data:" << (char*)&_data[0] << ""); TEST_INFO("data:" << (char*)&_data[0] << "");
} }
@ -41,15 +41,15 @@ int main(int _argc, const char *_argv[]) {
// TODO : Check if connection is valid ... // TODO : Check if connection is valid ...
// Create a HTTP connection in Client mode // Create a HTTP connection in Client mode
enet::Http connection(std::move(tcpConnection), false); enet::HttpClient connection(std::move(tcpConnection));
connection.setKeepAlive(true);
// Set callbacks: // Set callbacks:
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(); connection.start();
connection.get("plop.txt"); enet::HttpRequest req(enet::HTTPReqType::GET);
req.setUri("plop.txt");
connection.setHeader(req);
while (connection.isAlive() == true) { while (connection.isAlive() == true) {
usleep(100000); usleep(100000);

View File

@ -13,23 +13,27 @@
#include <unistd.h> #include <unistd.h>
#include <etk/stdTools.h> #include <etk/stdTools.h>
namespace appl { namespace appl {
void onReceiveData(enet::Http& _interface, std::vector<uint8_t>& _data) { void onReceiveData(enet::HttpServer* _interface, std::vector<uint8_t>& _data) {
TEST_INFO("Receive Datas : " << _data.size() << " bytes"); 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:"); TEST_INFO("Receive Header data:");
_data.display(); _data.display();
if (_data.m_req == "GET") { if (_data.getType() == enet::HTTPReqType::GET) {
if (_data.m_what == "http://127.0.0.1:12345/plop.txt") { if (_data.getUri() == "http://127.0.0.1:12345/plop.txt") {
_interface.writeAnswerHeader(enet::HTTPAnswerCode::c200_ok); enet::HttpAnswer answer(enet::HTTPAnswerCode::c200_ok);
std::string data = "<html><head></head></body>coucou</body></html>"; std::string data = "<html><head></head></body>coucou</body></html>";
_interface.write(data); answer.setKey("Content-Length", etk::to_string(data.size()));
_interface.stop(); _interface->setHeader(answer);
_interface->write(data);
_interface->stop();
return; return;
} }
} }
_interface.writeAnswerHeader(enet::HTTPAnswerCode::c200_ok); enet::HttpAnswer answer(enet::HTTPAnswerCode::c404_notFound);
_interface.stop(); 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 ... // TODO : Check if connection is valid ...
// Create a HTTP connection in Server mode // Create a HTTP connection in Server mode
enet::Http connection(std::move(tcpConnection), true); enet::HttpServer connection(std::move(tcpConnection));
connection.setKeepAlive(true); enet::HttpServer* tmp = &connection;
// Set callbacks: // Set callbacks:
connection.connect(appl::onReceiveData); connection.connect([=](std::vector<uint8_t>& _value){
connection.connectHeader(appl::onReceiveHeader); appl::onReceiveData(tmp, _value);
});
connection.connectHeader([=](const enet::HttpRequest& _value){
appl::onReceiveHeader(tmp, _value);
});
// start http connection (the actual state is just TCP start ...) // start http connection (the actual state is just TCP start ...)
connection.start(); connection.start();