[DEV] rework ENET

This commit is contained in:
Edouard DUPIN 2016-06-16 22:46:43 +02:00
parent f0c425cd29
commit 97acf584f8
9 changed files with 692 additions and 207 deletions

View File

@ -9,37 +9,146 @@
#include <map> #include <map>
#include <etk/stdTools.h> #include <etk/stdTools.h>
#include <string.h> #include <string.h>
#if 0 #include <unistd.h>
static std::map<enet::HTTPAnswerCode, std::string> protocolName = {
{enet::HTTPAnswerCode::c100_continue, "Continue"},
{enet::HTTPAnswerCode::c101_switchingProtocols, "Switching Protocols"},
{enet::HTTPAnswerCode::c103_checkpoint, "Checkpoint"},
{enet::HTTPAnswerCode::c200_ok, "OK"},
{enet::HTTPAnswerCode::c201_created, "Created"},
{enet::HTTPAnswerCode::c202_accepted, "Accepted"},
{enet::HTTPAnswerCode::c203_nonAuthoritativeInformation, "Non-Authoritative Information"},
{enet::HTTPAnswerCode::c204_noContent, "No Content"},
{enet::HTTPAnswerCode::c205_resetContent, "Reset Content"},
{enet::HTTPAnswerCode::c206_partialContent, "Partial Content"},
{enet::HTTPAnswerCode::c300_multipleChoices, "Multiple Choices"},
{enet::HTTPAnswerCode::c301_movedPermanently, "Moved Permanently"},
{enet::HTTPAnswerCode::c302_found, "Found"},
{enet::HTTPAnswerCode::c303_seeOther, "See Other"},
{enet::HTTPAnswerCode::c304_notModified, "Not Modified"},
{enet::HTTPAnswerCode::c306_switchProxy, "Switch Proxy"},
{enet::HTTPAnswerCode::c307_temporaryRedirect, "Temporary Redirect"},
{enet::HTTPAnswerCode::c308_resumeIncomplete, "Resume Incomplete"},
{enet::HTTPAnswerCode::c400_badRequest, "Bad Request"},
{enet::HTTPAnswerCode::c401_unauthorized, "Unauthorized"},
{enet::HTTPAnswerCode::c402_paymentRequired, "Payment Required"},
{enet::HTTPAnswerCode::c403_forbidden, "Forbidden"},
{enet::HTTPAnswerCode::c404_notFound, "Not Found"},
{enet::HTTPAnswerCode::c405_methodNotAllowed, "Method Not Allowed"},
{enet::HTTPAnswerCode::c406_notAcceptable, "Not Acceptable"},
{enet::HTTPAnswerCode::c407_proxyAuthenticationRequired, "Proxy Authentication Required"},
{enet::HTTPAnswerCode::c408_requestTimeout, "Request Timeout"},
{enet::HTTPAnswerCode::c409_conflict, "Conflict"},
{enet::HTTPAnswerCode::c410_gone, "Gone"},
{enet::HTTPAnswerCode::c411_lengthRequired, "Length Required"},
{enet::HTTPAnswerCode::c412_preconditionFailed, "Precondition Failed"},
{enet::HTTPAnswerCode::c413_requestEntityTooLarge, "Request Entity Too Large"},
{enet::HTTPAnswerCode::c414_requestURITooLong, "Request-URI Too Long"},
{enet::HTTPAnswerCode::c415_unsupportedMediaType, "Unsupported Media Type"},
{enet::HTTPAnswerCode::c416_requestedRangeNotSatisfiable, "Requested Range Not Satisfiable"},
{enet::HTTPAnswerCode::c417_expectationFailed, "Expectation Failed"},
{enet::HTTPAnswerCode::c500_internalServerError, "Internal Server Error"},
{enet::HTTPAnswerCode::c501_notImplemented, "Not Implemented"},
{enet::HTTPAnswerCode::c502_badGateway, "Bad Gateway"},
{enet::HTTPAnswerCode::c503_serviceUnavailable, "Service Unavailable"},
{enet::HTTPAnswerCode::c504_gatewayTimeout, "Gateway Timeout"},
{enet::HTTPAnswerCode::c505_httpVersionNotSupported, "HTTP Version Not Supported"},
{enet::HTTPAnswerCode::c511_networkAuthenticationRequired, "Network Authentication Required"}
};
static std::map<int32_t, std::string> getErrorList() { static std::map<int32_t, std::string> getErrorList() {
static std::map<int32_t, std::string> g_list; static std::map<int32_t, std::string> g_list;
return g_list; return g_list;
} }
enet::Http::Http() : enet::Http::Http(enet::Tcp _connection, bool _isServer) :
m_isServer(_isServer),
m_connection(std::move(_connection)),
m_headerIsSend(false),
m_thread(nullptr),
m_threadRunning(false),
m_keepAlive(false) { m_keepAlive(false) {
m_connection.setPort(80); setSendHeaderProperties("User-Agent", "e-net (ewol network interface)");
m_connection.setServer(false); if (m_keepAlive == true) {
setSendHeaderProperties("Connection", "Keep-Alive");
}
} }
enet::Http::~Http() { enet::Http::~Http() {
reset(); stop();
} }
bool enet::Http::connect() { void enet::Http::threadCallback() {
ENET_DEBUG("Start of thread HTTP");
ethread::setName("TcpString-input");
// get datas:
while ( m_threadRunning == true
&& m_connection.getConnectionStatus() == enet::Tcp::status::link) {
// READ section data:
if (m_headerIsSend == false) {
getHeader();
m_headerIsSend = true;
}
m_temporaryBuffer.resize(67000);
int32_t len = m_connection.read(&m_temporaryBuffer[0], m_temporaryBuffer.size());
if (len > 0) {
ENET_INFO("Call client with datas ...");
if (m_observer != nullptr) {
m_observer(*this, m_temporaryBuffer);
}
}
}
m_threadRunning = false;
ENET_DEBUG("End of thread HTTP");
}
void enet::Http::start() {
ENET_DEBUG("connect [START]");
m_threadRunning = true;
m_thread = new std::thread([&](void *){ this->threadCallback();}, nullptr);
if (m_thread == nullptr) {
m_threadRunning = false;
ENET_ERROR("creating callback thread!");
return;
}
while ( m_threadRunning == true
&& m_connection.getConnectionStatus() != enet::Tcp::status::link) {
usleep(50000);
}
//ethread::setPriority(*m_receiveThread, -6);
ENET_DEBUG("connect [STOP]");
}
void enet::Http::stop(bool _inThreadStop){
ENET_DEBUG("disconnect [START]");
m_threadRunning = false;
/*
if (m_connection.getConnectionStatus() == enet::Tcp::status::link) { if (m_connection.getConnectionStatus() == enet::Tcp::status::link) {
return true; uint32_t size = 0xFFFFFFFF;
m_connection.write(&size, 4);
} }
if (m_connection.link() == false) { */
ENET_ERROR("can not link to the socket..."); if (m_connection.getConnectionStatus() != enet::Tcp::status::unlink) {
return false; m_connection.unlink();
} }
return true; if (_inThreadStop == false) {
if (m_thread != nullptr) {
m_thread->join();
delete m_thread;
m_thread = nullptr;
}
}
ENET_DEBUG("disconnect [STOP]");
} }
void enet::Http::setSendHeaderProperties(const std::string& _key, const std::string& _val) { void enet::Http::setSendHeaderProperties(const std::string& _key, const std::string& _val) {
auto it = m_sendHeader.find(_key); auto it = m_header.m_map.find(_key);
if (it == m_sendHeader.end()) { if (it == m_header.m_map.end()) {
m_sendHeader.insert(make_pair(_key, _val)); m_header.m_map.insert(make_pair(_key, _val));
} else { } else {
it->second = _val; it->second = _val;
} }
@ -54,96 +163,68 @@ std::string enet::Http::getReceiveHeaderProperties(const std::string& _key) {
ENET_TODO("get header key=" << _key); ENET_TODO("get header key=" << _key);
return ""; return "";
} }
void enet::Http::writeAnswerHeader(enum enet::HTTPAnswerCode _value) {
bool enet::Http::reset() { std::string out;
if (m_connection.getConnectionStatus() != enet::Tcp::status::link) { out = "HTTP/1.1 ";
m_connection.unlink(); out += etk::to_string(int32_t(_value));
auto it = protocolName.find(_value);
if (it == protocolName.end() ) {
out += " ???";
} else {
out += " " + it->second;
} }
m_receiveData.clear(); out += "\r\n\r\n";
m_sendHeader.clear(); ENET_WARNING("Write header :" << out);
m_receiveHeader.clear(); write(out, false);
setSendHeaderProperties("User-Agent", "e-net (ewol network interface)");
if (m_keepAlive == true) {
setSendHeaderProperties("Connection", "Keep-Alive");
}
return true;
} }
bool enet::Http::setServer(const std::string& _hostName) { void enet::Http::getHeader() {
// if change server ==> restart connection ... ENET_VERBOSE("Read HTTP Header [START]");
if (_hostName == m_connection.getHostName()) {
return true;
}
reset();
m_connection.setHostNane(_hostName);
return true;
}
bool enet::Http::setPort(uint16_t _port) {
// if change server ==> restart connection ...
if (_port == m_connection.getPort()) {
return true;
}
reset();
m_connection.setPort(_port);
return true;
}
bool enet::Http::receiveData() {
std::string header;
// Get data
char data[1025];
int32_t len = 1;
bool headerEnded = false; bool headerEnded = false;
while ( m_connection.getConnectionStatus() == enet::Tcp::status::link std::string header;
&& len > 0) { while (m_connection.getConnectionStatus() == enet::Tcp::status::link) {
len = m_connection.read(data, 1024); char type;
// TODO : Parse header ... int32_t len = m_connection.read(&type, 1);
if (len == 0) {
if (headerEnded == false) { usleep(1);
char previous = '\0'; continue;
if (header.size()>0) { }
previous = header[header.size()-1]; header += type;
} if ( header.size() > 4
for (int32_t iii=0; iii<len; ++iii) { && header[header.size()-1] == '\n'
if (headerEnded == false) { && header[header.size()-2] == '\r'
if (data[iii] != '\r') { && header[header.size()-3] == '\n'
header += data[iii]; && header[header.size()-4] == '\r') {
if (data[iii] == '\n') { // Normal end case ...
//ENET_VERBOSE("parse: '\\n'"); break;
if (previous == '\n') { } else if ( header.size() > 2
//ENET_VERBOSE("End header"); && header[header.size()-1] == '\n'
// Find end of header && header[header.size()-2] == '\n') {
headerEnded = true; // linux end case
} break;
previous = data[iii]; } else if ( header.size() > 2
} else { && header[header.size()-1] == '\r'
previous = data[iii]; && header[header.size()-2] == '\r') {
//ENET_VERBOSE("parse: '" << data[iii] << "'"); // Mac end case
} break;
}
} else {
m_receiveData.push_back(data[iii]);
}
}
} else {
for (int32_t iii=0; iii<len; ++iii) {
m_receiveData.push_back(data[iii]);
}
} }
} }
if (m_connection.getConnectionStatus() != enet::Tcp::status::link) { ENET_VERBOSE("Read HTTP Header [STOP] : '" << header << "'");
ENET_WARNING("server disconnected"); m_headerIsSend = true;
return false;
}
// parse header : // parse header :
std::vector<std::string> list = etk::split(header, '\n'); std::vector<std::string> list = etk::split(header, '\n');
for (auto &it : list) {
if ( it.size()>0
&& it[it.size()-1] == '\r') {
it.resize(it.size()-1);
}
}
headerEnded = false; headerEnded = false;
m_receiveHeader.clear(); m_header.m_map.clear();
for (auto element : list) { for (auto element : list) {
if (headerEnded == false) { if (headerEnded == false) {
header = element;
headerEnded = true; headerEnded = true;
m_header.setReq(element);
} else { } else {
size_t found = element.find(":"); size_t found = element.find(":");
if (found == std::string::npos) { if (found == std::string::npos) {
@ -151,10 +232,10 @@ bool enet::Http::receiveData() {
continue; continue;
} }
ENET_VERBOSE("header : key='" << std::string(element, 0, found) << "' value='" << std::string(element, found+2) << "'"); ENET_VERBOSE("header : key='" << std::string(element, 0, found) << "' value='" << std::string(element, found+2) << "'");
m_receiveHeader.insert(make_pair(unEscapeChar(std::string(element, 0, found)), unEscapeChar(std::string(element, found+2)))); m_header.m_map.insert(make_pair(unEscapeChar(std::string(element, 0, found)), unEscapeChar(std::string(element, found+2))));
} }
} }
for (auto &it : m_receiveHeader) { for (auto &it : m_header.m_map) {
if (it.first == "Connection") { if (it.first == "Connection") {
if (it.second == "close") { if (it.second == "close") {
ENET_DEBUG("connection closed by remote :"); ENET_DEBUG("connection closed by remote :");
@ -164,67 +245,29 @@ bool enet::Http::receiveData() {
} }
} }
} }
/* m_header.display();
ENET_INFO("header : '" << header << "'"); if (m_observerRequest != nullptr) {
for (auto &it : m_receiveHeader) { m_observerRequest(*this, m_header);
ENET_INFO("header : key='" << it.first << "' value='" << it.second << "'");
} }
*/
// parse base answear:
list = etk::split(header, ' ');
if (list.size() < 2) {
ENET_ERROR("can not parse answear : " << list);
return false;
}
int32_t ret = etk::string_to_int32_t(list[1]);
switch (ret/100) {
case 1:
// information message
return true;
break;
case 2:
// OK
return true;
break;
case 3:
// Redirect
ENET_WARNING("Rediret request");
return false;
break;
case 4:
// client Error
ENET_WARNING("Client error");
return false;
break;
case 5:
// server error
ENET_WARNING("Server error");
return false;
break;
}
return true;
} }
bool enet::Http::get(const std::string& _address) { bool enet::Http::get(const std::string& _address) {
m_receiveData.clear(); m_receiveData.clear();
m_receiveHeader.clear(); m_header.m_map.clear();
if (connect() == false) { std::string req = "GET http://" + m_connection.getName();
return false;
}
std::string req = "GET http://" + m_connection.getHostName();
if (_address != "") { if (_address != "") {
req += "/"; req += "/";
req += _address; req += _address;
} }
req += " HTTP/1.0\n"; req += " HTTP/1.1\r\n";
setSendHeaderProperties("Content-Length", "0"); setSendHeaderProperties("Content-Length", "0");
// add header properties : // add header properties :
for (auto &it : m_sendHeader) { for (auto &it : m_header.m_map) {
req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\n"; req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\r\n";
} }
// end of header // end of header
req += "\n"; req += "\r\n";
// no body: // no body:
int32_t len = m_connection.write(req, false); int32_t len = m_connection.write(req, false);
@ -233,7 +276,8 @@ bool enet::Http::get(const std::string& _address) {
ENET_ERROR("An error occured when sending data " << len << "!=" << req.size()); ENET_ERROR("An error occured when sending data " << len << "!=" << req.size());
return false; return false;
} }
return receiveData(); //return receiveData();
return false;
} }
std::string enet::Http::escapeChar(const std::string& _value) { std::string enet::Http::escapeChar(const std::string& _value) {
@ -245,10 +289,7 @@ std::string enet::Http::unEscapeChar(const std::string& _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_receiveData.clear();
m_receiveHeader.clear(); m_header.m_map.clear();
if (connect() == false) {
return false;
}
// First create body : // First create body :
std::string body; std::string body;
for (auto &it : _values) { for (auto &it : _values) {
@ -262,11 +303,8 @@ 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_receiveData.clear();
m_receiveHeader.clear(); m_header.m_map.clear();
if (connect() == false) { std::string req = "POST http://" + m_connection.getName();
return false;
}
std::string req = "POST http://" + m_connection.getHostName();
if (_address != "") { if (_address != "") {
req += "/"; req += "/";
req += _address; req += _address;
@ -275,8 +313,8 @@ bool enet::Http::post(const std::string& _address, const std::string& _contentTy
setSendHeaderProperties("Content-Type", _contentType); setSendHeaderProperties("Content-Type", _contentType);
setSendHeaderProperties("Content-Length", etk::to_string(_data.size())); setSendHeaderProperties("Content-Length", etk::to_string(_data.size()));
// add header properties : // add header properties :
for (auto &it : m_sendHeader) { for (auto &it : m_header.m_map) {
req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\n"; req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\r\n";
} }
// end of header // end of header
req += "\n"; req += "\n";
@ -288,7 +326,8 @@ bool enet::Http::post(const std::string& _address, const std::string& _contentTy
ENET_ERROR("An error occured when sending data " << len << "!=" << req.size()); ENET_ERROR("An error occured when sending data " << len << "!=" << req.size());
return false; return false;
} }
return receiveData(); //return receiveData();
return false;
} }
@ -302,5 +341,56 @@ std::string enet::Http::dataString() {
} }
return data; return data;
} }
#endif
int32_t enet::Http::write(const void* _data, int32_t _len) {
return m_connection.write(_data, _len);
}
void enet::HttpHeader::setReq(const std::string& _req) {
// parse base answear:
std::vector<std::string> list = etk::split(_req, ' ');
if (list.size() < 2) {
ENET_ERROR("can not parse answear : " << list);
return;
}
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::display() const {
ENET_INFO("header :");
for (auto &it : m_map) {
ENET_INFO(" key='" << it.first << "' value='" << it.second << "'");
}
}

View File

@ -8,17 +8,135 @@
#include <enet/Tcp.h> #include <enet/Tcp.h>
#include <vector> #include <vector>
#include <map> #include <map>
#include <thread>
#include <ethread/tools.h>
#if 0
namespace enet { namespace enet {
enum class HTTPAnswerCode {
//1xx: Information
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
c103_checkpoint, //!< Used in the resumable requests proposal to resume aborted PUT or POST requests
//2xx: Successful
c200_ok = 200, //!< The request is OK (this is the standard response for successful HTTP requests)
c201_created, //!< The request has been fulfilled, and a new resource is created
c202_accepted, //!< The request has been accepted for processing, but the processing has not been completed
c203_nonAuthoritativeInformation, //!< The request has been successfully processed, but is returning information that may be from another source
c204_noContent, //!< The request has been successfully processed, but is not returning any content
c205_resetContent, //!< The request has been successfully processed, but is not returning any content, and requires that the requester reset the document view
c206_partialContent, //!< The server is delivering only part of the resource due to a range header sent by the client
//3xx: Redirection
c300_multipleChoices = 300, //!< A link list. The user can select a link and go to that location. Maximum five addresses
c301_movedPermanently, //!< The requested page has moved to a new URL
c302_found, //!< The requested page has moved temporarily to a new URL
c303_seeOther, //!< The requested page can be found under a different URL
c304_notModified, //!< Indicates the requested page has not been modified since last requested
c306_switchProxy, //!< No longer used
c307_temporaryRedirect, //!< The requested page has moved temporarily to a new URL
c308_resumeIncomplete, //!< Used in the resumable requests proposal to resume aborted PUT or POST requests
//4xx: Client Error,
c400_badRequest = 400, //!< The request cannot be fulfilled due to bad syntax
c401_unauthorized, //!< The request was a legal request, but the server is refusing to respond to it. For use when authentication is possible but has failed or not yet been provided
c402_paymentRequired, //!< Reserved for future use
c403_forbidden, //!< The request was a legal request, but the server is refusing to respond to it
c404_notFound, //!< The requested page could not be found but may be available again in the future
c405_methodNotAllowed, //!< A request was made of a page using a request method not supported by that page
c406_notAcceptable, //!< The server can only generate a response that is not accepted by the client
c407_proxyAuthenticationRequired, //!< The client must first authenticate itself with the proxy
c408_requestTimeout, //!< The server timed out waiting for the request
c409_conflict, //!< The request could not be completed because of a conflict in the request
c410_gone, //!< The requested page is no longer available
c411_lengthRequired, //!< The "Content-Length" is not defined. The server will not accept the request without it
c412_preconditionFailed, //!< The precondition given in the request evaluated to false by the server
c413_requestEntityTooLarge, //!< The server will not accept the request, because the request entity is too large
c414_requestURITooLong, //!< The server will not accept the request, because the URL is too long. Occurs when you convert a POST request to a GET request with a long query information
c415_unsupportedMediaType, //!< The server will not accept the request, because the media type is not supported
c416_requestedRangeNotSatisfiable, //!< The client has asked for a portion of the file, but the server cannot supply that portion
c417_expectationFailed, //!< The server cannot meet the requirements of the Expect request-header field
//5xx: Server Error
c500_internalServerError = 500, //!< A generic error message, given when no more specific message is suitable
c501_notImplemented, //!< The server either does not recognize the request method, or it lacks the ability to fulfill the request
c502_badGateway, //!< The server was acting as a gateway or proxy and received an invalid response from the upstream server
c503_serviceUnavailable, //!< The server is currently unavailable (overloaded or down)
c504_gatewayTimeout, //!< The server was acting as a gateway or proxy and did not receive a timely response from the upstream server
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
};
enum class HTTPProtocol {
http_1_0,
http_1_1,
};
class HttpHeader {
private:
// key, val
std::map<std::string, std::string> m_map;
enum HTTPProtocol m_protocol;
public:
void addKey(const std::string& _key, const std::string& _value);
void rmKey(const std::string& _key);
std::string getKey(const std::string& _key);
enum HTTPProtocol getProtocol() {
return m_protocol;
}
void setProtocol(enum HTTPProtocol _protocol) {
m_protocol = _protocol;
}
virtual ~HttpHeader() = default;
};
class HttpAnswer : public HttpHeader {
private:
enet::HTTPAnswerCode m_what;
int64_t m_messageSize; // parameter
public:
HttpAnswer();
HttpAnswer(const std::string& _value);
get(const std::string& _uri);
setSize(int64_t _messageSize=-1);
void display() const;
std::string generate();
};
enum class HTTPReqType {
GET,
HEAD,
POST,
PUT,
DELETE,
};
class HttpRequest : public HttpHeader{
private:
// key, val
std::map<std::string, std::string> m_parameters;
enum HTTPReqType m_req;
std::string m_uri;
enum HTTPProtocol m_protocol;
bool m_keepAlive;
public:
HttpRequest(enum HTTPReqType _type,
void display() const;
std::string generate();
};
class Http { class Http {
public: public:
Http(); Http(enet::Tcp _connection, bool _isServer=false);
virtual ~Http(); virtual ~Http();
private:
bool m_isServer;
public:
bool getServerState() {
return m_isServer;
}
private: private:
enet::Tcp m_connection; enet::Tcp m_connection;
bool m_headerIsSend;
std::thread* m_thread;
bool m_threadRunning;
std::vector<uint8_t> m_temporaryBuffer;
private: private:
bool m_keepAlive; bool m_keepAlive;
void threadCallback();
public: public:
void setKeepAlive(bool _keepAlive) { void setKeepAlive(bool _keepAlive) {
m_keepAlive = true; m_keepAlive = true;
@ -27,32 +145,104 @@ namespace enet {
return m_keepAlive; return m_keepAlive;
} }
private: private:
// key, val
std::map<std::string, std::string> m_sendHeader; HttpHeader m_header;
std::map<std::string, std::string> m_receiveHeader;
std::vector<uint8_t> m_receiveData; std::vector<uint8_t> m_receiveData;
bool connect(); void getHeader();
bool reset();
public: public:
void start();
void stop(bool _inThread=false);
bool isAlive() {
return m_connection.getConnectionStatus() == enet::Tcp::status::link;
}
void setSendHeaderProperties(const std::string& _key, const std::string& _val); void setSendHeaderProperties(const std::string& _key, const std::string& _val);
std::string getSendHeaderProperties(const std::string& _key); std::string getSendHeaderProperties(const std::string& _key);
std::string getReceiveHeaderProperties(const std::string& _key); std::string getReceiveHeaderProperties(const std::string& _key);
bool setServer(const std::string& _hostName);
bool setPort(uint16_t _port);
bool get(const std::string& _address); 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::map<std::string, std::string>& _values);
bool post(const std::string& _address, const std::string& _contentType, const std::string& _data); bool post(const std::string& _address, const std::string& _contentType, const std::string& _data);
int32_t dataSize() {
return m_receiveData.size();
}
const std::vector<uint8_t>& data() {
return m_receiveData;
}
std::string dataString(); std::string dataString();
void writeAnswerHeader(enum enet::HTTPAnswerCode _value);
std::string escapeChar(const std::string& _value); std::string escapeChar(const std::string& _value);
std::string unEscapeChar(const std::string& _value); std::string unEscapeChar(const std::string& _value);
bool receiveData(); public:
using Observer = std::function<void(enet::Http& _interface, std::vector<uint8_t>&)>; //!< Define an Observer: function pointer
Observer m_observer;
/**
* @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)(enet::Http& _interface, std::vector<uint8_t>&)) {
m_observer = [=](enet::Http& _interface, std::vector<uint8_t>& _value){
(*_class.*_func)(_interface,_value);
};
}
void connect(Observer _func) {
m_observer = _func;
}
public:
using ObserverRequest = std::function<void(enet::Http& _interface, const enet::HttpHeader&)>; //!< Define an Observer: function pointer
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<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
* @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);
}
}; };
} }
#endif

View File

@ -81,7 +81,6 @@ bool enet::Tcp::unlink() {
int32_t enet::Tcp::read(void* _data, int32_t _maxLen) { int32_t enet::Tcp::read(void* _data, int32_t _maxLen) {
ENET_VERBOSE("read [START]");
if (m_status != status::link) { if (m_status != status::link) {
ENET_ERROR("Can not read on unlink connection"); ENET_ERROR("Can not read on unlink connection");
return -1; return -1;
@ -91,7 +90,6 @@ int32_t enet::Tcp::read(void* _data, int32_t _maxLen) {
// Initialize the timeout to 3 minutes. If no activity after 3 minutes this program will end. timeout value is based on milliseconds. // Initialize the timeout to 3 minutes. If no activity after 3 minutes this program will end. timeout value is based on milliseconds.
int timeout = (3 * 60 * 1000); int timeout = (3 * 60 * 1000);
// Call poll() and wait 3 minutes for it to complete. // Call poll() and wait 3 minutes for it to complete.
ENET_VERBOSE("Waiting on poll()...");
int rc = poll(m_fds, nfds, timeout); int rc = poll(m_fds, nfds, timeout);
// Check to see if the poll call failed. // Check to see if the poll call failed.
if (rc < 0) { if (rc < 0) {
@ -116,20 +114,18 @@ int32_t enet::Tcp::read(void* _data, int32_t _maxLen) {
} }
// Check to see if the connection has been closed by the client // Check to see if the connection has been closed by the client
if (rc == 0) { if (rc == 0) {
ENET_ERROR(" Connection closed"); ENET_INFO(" Connection closed");
closeConn = true; closeConn = true;
} }
if (closeConn == false) { if (closeConn == false) {
// Data was received // Data was received
size = rc; size = rc;
ENET_VERBOSE(" " << size << " bytes received");
} else { } else {
// If the close_conn flag was turned on, we need to clean up this active connection. // If the close_conn flag was turned on, we need to clean up this active connection.
// This clean up process includes removing the descriptor. // This clean up process includes removing the descriptor.
ENET_ERROR(" Set status at remote close ..."); ENET_DEBUG(" Set status at remote close ...");
m_status = status::linkRemoteClose; m_status = status::linkRemoteClose;
} }
ENET_VERBOSE("read [STOP]");
return size; return size;
} }

View File

@ -12,9 +12,7 @@ namespace enet {
class Tcp { class Tcp {
private: private:
int32_t m_socketId; //!< socket linux interface generic int32_t m_socketId; //!< socket linux interface generic
#if 1 struct pollfd m_fds[1];
struct pollfd m_fds[1];
#endif
public: public:
Tcp(); Tcp();
Tcp(int32_t _idSocket, const std::string& _name); Tcp(int32_t _idSocket, const std::string& _name);

38
enet/WebSocket.cpp Normal file
View File

@ -0,0 +1,38 @@
/** @file
* @author Edouard DUPIN
* @copyright 2014, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <enet/debug.h>
#include <enet/WebSocket.h>
#include <map>
#include <etk/stdTools.h>
#include <string.h>
enet::WebSocket::WebSocket(enet::Tcp _connection, bool _isServer) :
m_interface(std::move(_connection), _isServer) {
}
enet::WebSocket::~WebSocket() {
}
void enet::WebSocket::start(const std::string& _uri) {
if (m_interface.isServer() == true) {
}
m_interface.start();
if (m_interface.isServer() == false) {
m_interface.get(
}
}
void enet::WebSocket::stop(bool _inThread=false) {
}

22
enet/WebSocket.h Normal file
View File

@ -0,0 +1,22 @@
/** @file
* @author Edouard DUPIN
* @copyright 2014, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#pragma once
#include <enet/Tcp.h>
#include <vector>
#include <map>
namespace enet {
class WebSocket{
private:
enet::Http m_interface;
public:
WebSocket(enet::Tcp _connection, bool _isServer=false);
virtual ~WebSocket();
void start(const std::string& _uri);
void stop(bool _inThread=false);
};
}

View File

@ -0,0 +1,41 @@
#!/usr/bin/python
import lutin.module as module
import lutin.tools as tools
def get_type():
return "BINARY"
def get_sub_type():
return "TEST"
def get_desc():
return "e-net TEST test software for enet"
def get_licence():
return "APACHE-2"
def get_compagny_type():
return "com"
def get_compagny_name():
return "atria-soft"
def get_maintainer():
return ["Mr DUPIN Edouard <yui.heero@gmail.com>"]
def create(target, module_name):
my_module = module.Module(__file__, module_name, get_type())
my_module.add_export_path(tools.get_current_path(__file__))
my_module.add_module_depend(['enet', 'gtest', 'test-debug'])
my_module.add_src_file([
'test/main-server-http.cpp'
])
return my_module

View File

@ -6,10 +6,19 @@
#include <test-debug/debug.h> #include <test-debug/debug.h>
#include <enet/Tcp.h> #include <enet/Tcp.h>
#include <enet/TcpClient.h>
#include <enet/Http.h> #include <enet/Http.h>
#include <etk/etk.h> #include <etk/etk.h>
#include <etk/stdTools.h> #include <etk/stdTools.h>
#include <unistd.h>
namespace appl {
void onReceiveData(enet::Http& _interface, std::vector<uint8_t>& _data) {
TEST_INFO("Receive Datas : " << _data.size() << " bytes");
TEST_INFO("data:" << (char*)&_data[0] << "");
}
}
int main(int _argc, const char *_argv[]) { int main(int _argc, const char *_argv[]) {
etk::init(_argc, _argv); etk::init(_argc, _argv);
@ -27,35 +36,25 @@ int main(int _argc, const char *_argv[]) {
TEST_INFO("== Test HTTP client =="); TEST_INFO("== Test HTTP client ==");
TEST_INFO("=================================="); TEST_INFO("==================================");
#ifndef __TARGET_OS__Windows #ifndef __TARGET_OS__Windows
// client mode ... // connect on TCP server:
enet::Http connection; enet::Tcp tcpConnection = std::move(enet::connectTcpClient("127.0.0.1", 12345));
connection.setServer("127.0.0.1"); // TODO : Check if connection is valid ...
// Create a HTTP connection in Client mode
enet::Http connection(std::move(tcpConnection), false);
connection.setKeepAlive(true); connection.setKeepAlive(true);
TEST_INFO("----------------------------"); // Set callbacks:
TEST_INFO("GET data : "); connection.connect(appl::onReceiveData);
if (connection.get("") == false) {
TEST_ERROR("can not GET data...");
return -1;
}
TEST_INFO("data : " << connection.dataString());
TEST_INFO("----------------------------"); // start http connection (the actual state is just TCP start ...)
TEST_INFO("POST data : "); connection.start();
std::map<std::string, std::string> values; connection.get("plop.txt");
values.insert(std::make_pair<std::string, std::string>("plop", "valuePlop"));
if (connection.post("", values) == false) {
TEST_ERROR("can not POST data...");
return -1;
}
TEST_INFO("data : " << connection.dataString());
TEST_INFO("----------------------------");
TEST_INFO("POST xml : "); while (connection.isAlive() == true) {
if (connection.post("", /*"application/xml"*/ "text/xml; charset=utf-8", "<plop><string>value1</string></plop>") == false) { usleep(100000);
TEST_ERROR("can not POST XML data...");
return -1;
} }
TEST_INFO("data : " << connection.dataString());
#else #else
TEST_CRITICAL("not implemented"); TEST_CRITICAL("not implemented");
#endif #endif

111
test/main-server-http.cpp Normal file
View File

@ -0,0 +1,111 @@
/** @file
* @author Edouard DUPIN
* @copyright 2014, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <test-debug/debug.h>
#include <enet/Tcp.h>
#include <enet/Http.h>
#include <enet/TcpServer.h>
#include <etk/etk.h>
#include <unistd.h>
#include <etk/stdTools.h>
namespace appl {
void onReceiveData(enet::Http& _interface, std::vector<uint8_t>& _data) {
TEST_INFO("Receive Datas : " << _data.size() << " bytes");
}
void onReceiveHeader(enet::Http& _interface, const enet::HttpHeader& _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);
std::string data = "<html><head></head></body>coucou</body></html>";
_interface.write(data);
_interface.stop();
return;
}
}
_interface.writeAnswerHeader(enet::HTTPAnswerCode::c200_ok);
_interface.stop();
}
}
int main(int _argc, const char *_argv[]) {
etk::init(_argc, _argv);
for (int32_t iii=0; iii<_argc ; ++iii) {
std::string data = _argv[iii];
if ( data == "-h"
|| data == "--help") {
TEST_PRINT(etk::getApplicationName() << " - help : ");
TEST_PRINT(" " << _argv[0] << " [options]");
TEST_PRINT(" No options ...");
return -1;
}
}
TEST_INFO("==================================");
TEST_INFO("== Test HTTP server ==");
TEST_INFO("==================================");
#ifndef __TARGET_OS__Windows
//Wait on TCP connection:
enet::TcpServer interface;
// Configure server interface:
interface.setHostNane("127.0.0.1");
interface.setPort(12345);
// Start listening ...
interface.link();
// Wait a new connection ..
enet::Tcp tcpConnection = std::move(interface.waitNext());
// Free Connected port
interface.unlink();
// TODO : Check if connection is valid ...
// Create a HTTP connection in Server mode
enet::Http connection(std::move(tcpConnection), true);
connection.setKeepAlive(true);
// Set callbacks:
connection.connect(appl::onReceiveData);
connection.connectHeader(appl::onReceiveHeader);
// start http connection (the actual state is just TCP start ...)
connection.start();
while (connection.isAlive() == true) {
usleep(100000);
}
/*
TEST_INFO("----------------------------");
TEST_INFO("GET data : ");
if (connection.get("") == false) {
TEST_ERROR("can not GET data...");
return -1;
}
TEST_INFO("data : " << connection.dataString());
TEST_INFO("----------------------------");
TEST_INFO("POST data : ");
std::map<std::string, std::string> values;
values.insert(std::make_pair<std::string, std::string>("plop", "valuePlop"));
if (connection.post("", values) == false) {
TEST_ERROR("can not POST data...");
return -1;
}
TEST_INFO("data : " << connection.dataString());
TEST_INFO("----------------------------");
TEST_INFO("POST xml : ");
if (connection.post("", "text/xml; charset=utf-8", "<plop><string>value1</string></plop>") == false) {
TEST_ERROR("can not POST XML data...");
return -1;
}
TEST_INFO("data : " << connection.dataString());
*/
#else
TEST_CRITICAL("not implemented");
#endif
return 0;
}