[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 <etk/stdTools.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> 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_connection.setPort(80);
m_connection.setServer(false);
setSendHeaderProperties("User-Agent", "e-net (ewol network interface)");
if (m_keepAlive == true) {
setSendHeaderProperties("Connection", "Keep-Alive");
}
}
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) {
return true;
uint32_t size = 0xFFFFFFFF;
m_connection.write(&size, 4);
}
if (m_connection.link() == false) {
ENET_ERROR("can not link to the socket...");
return false;
*/
if (m_connection.getConnectionStatus() != enet::Tcp::status::unlink) {
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) {
auto it = m_sendHeader.find(_key);
if (it == m_sendHeader.end()) {
m_sendHeader.insert(make_pair(_key, _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;
}
@ -54,96 +163,68 @@ std::string enet::Http::getReceiveHeaderProperties(const std::string& _key) {
ENET_TODO("get header key=" << _key);
return "";
}
bool enet::Http::reset() {
if (m_connection.getConnectionStatus() != enet::Tcp::status::link) {
m_connection.unlink();
void enet::Http::writeAnswerHeader(enum enet::HTTPAnswerCode _value) {
std::string out;
out = "HTTP/1.1 ";
out += etk::to_string(int32_t(_value));
auto it = protocolName.find(_value);
if (it == protocolName.end() ) {
out += " ???";
} else {
out += " " + it->second;
}
m_receiveData.clear();
m_sendHeader.clear();
m_receiveHeader.clear();
setSendHeaderProperties("User-Agent", "e-net (ewol network interface)");
if (m_keepAlive == true) {
setSendHeaderProperties("Connection", "Keep-Alive");
}
return true;
out += "\r\n\r\n";
ENET_WARNING("Write header :" << out);
write(out, false);
}
bool enet::Http::setServer(const std::string& _hostName) {
// if change server ==> restart connection ...
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;
void enet::Http::getHeader() {
ENET_VERBOSE("Read HTTP Header [START]");
bool headerEnded = false;
while ( m_connection.getConnectionStatus() == enet::Tcp::status::link
&& len > 0) {
len = m_connection.read(data, 1024);
// TODO : Parse header ...
if (headerEnded == false) {
char previous = '\0';
if (header.size()>0) {
previous = header[header.size()-1];
}
for (int32_t iii=0; iii<len; ++iii) {
if (headerEnded == false) {
if (data[iii] != '\r') {
header += data[iii];
if (data[iii] == '\n') {
//ENET_VERBOSE("parse: '\\n'");
if (previous == '\n') {
//ENET_VERBOSE("End header");
// Find end of header
headerEnded = true;
}
previous = data[iii];
} else {
previous = data[iii];
//ENET_VERBOSE("parse: '" << data[iii] << "'");
}
}
} else {
m_receiveData.push_back(data[iii]);
}
}
} else {
for (int32_t iii=0; iii<len; ++iii) {
m_receiveData.push_back(data[iii]);
}
std::string header;
while (m_connection.getConnectionStatus() == enet::Tcp::status::link) {
char type;
int32_t len = m_connection.read(&type, 1);
if (len == 0) {
usleep(1);
continue;
}
header += type;
if ( header.size() > 4
&& header[header.size()-1] == '\n'
&& header[header.size()-2] == '\r'
&& header[header.size()-3] == '\n'
&& header[header.size()-4] == '\r') {
// Normal end case ...
break;
} else if ( header.size() > 2
&& header[header.size()-1] == '\n'
&& header[header.size()-2] == '\n') {
// linux end case
break;
} else if ( header.size() > 2
&& header[header.size()-1] == '\r'
&& header[header.size()-2] == '\r') {
// Mac end case
break;
}
}
if (m_connection.getConnectionStatus() != enet::Tcp::status::link) {
ENET_WARNING("server disconnected");
return false;
}
ENET_VERBOSE("Read HTTP Header [STOP] : '" << header << "'");
m_headerIsSend = true;
// parse header :
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;
m_receiveHeader.clear();
m_header.m_map.clear();
for (auto element : list) {
if (headerEnded == false) {
header = element;
headerEnded = true;
m_header.setReq(element);
} else {
size_t found = element.find(":");
if (found == std::string::npos) {
@ -151,10 +232,10 @@ bool enet::Http::receiveData() {
continue;
}
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.second == "close") {
ENET_DEBUG("connection closed by remote :");
@ -164,67 +245,29 @@ bool enet::Http::receiveData() {
}
}
}
/*
ENET_INFO("header : '" << header << "'");
for (auto &it : m_receiveHeader) {
ENET_INFO("header : key='" << it.first << "' value='" << it.second << "'");
m_header.display();
if (m_observerRequest != nullptr) {
m_observerRequest(*this, m_header);
}
*/
// 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) {
m_receiveData.clear();
m_receiveHeader.clear();
if (connect() == false) {
return false;
}
std::string req = "GET http://" + m_connection.getHostName();
m_header.m_map.clear();
std::string req = "GET http://" + m_connection.getName();
if (_address != "") {
req += "/";
req += _address;
}
req += " HTTP/1.0\n";
req += " HTTP/1.1\r\n";
setSendHeaderProperties("Content-Length", "0");
// add header properties :
for (auto &it : m_sendHeader) {
req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\n";
for (auto &it : m_header.m_map) {
req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\r\n";
}
// end of header
req += "\n";
req += "\r\n";
// no body:
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());
return false;
}
return receiveData();
//return receiveData();
return false;
}
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) {
m_receiveData.clear();
m_receiveHeader.clear();
if (connect() == false) {
return false;
}
m_header.m_map.clear();
// First create body :
std::string body;
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) {
m_receiveData.clear();
m_receiveHeader.clear();
if (connect() == false) {
return false;
}
std::string req = "POST http://" + m_connection.getHostName();
m_header.m_map.clear();
std::string req = "POST http://" + m_connection.getName();
if (_address != "") {
req += "/";
req += _address;
@ -275,8 +313,8 @@ bool enet::Http::post(const std::string& _address, const std::string& _contentTy
setSendHeaderProperties("Content-Type", _contentType);
setSendHeaderProperties("Content-Length", etk::to_string(_data.size()));
// add header properties :
for (auto &it : m_sendHeader) {
req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\n";
for (auto &it : m_header.m_map) {
req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\r\n";
}
// end of header
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());
return false;
}
return receiveData();
//return receiveData();
return false;
}
@ -302,5 +341,56 @@ std::string enet::Http::dataString() {
}
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 <vector>
#include <map>
#include <thread>
#include <ethread/tools.h>
#if 0
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 {
public:
Http();
Http(enet::Tcp _connection, bool _isServer=false);
virtual ~Http();
private:
bool m_isServer;
public:
bool getServerState() {
return m_isServer;
}
private:
enet::Tcp m_connection;
bool m_headerIsSend;
std::thread* m_thread;
bool m_threadRunning;
std::vector<uint8_t> m_temporaryBuffer;
private:
bool m_keepAlive;
void threadCallback();
public:
void setKeepAlive(bool _keepAlive) {
m_keepAlive = true;
@ -27,32 +145,104 @@ namespace enet {
return m_keepAlive;
}
private:
// key, val
std::map<std::string, std::string> m_sendHeader;
std::map<std::string, std::string> m_receiveHeader;
HttpHeader m_header;
std::vector<uint8_t> m_receiveData;
bool connect();
bool reset();
void getHeader();
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);
std::string getSendHeaderProperties(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 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);
int32_t dataSize() {
return m_receiveData.size();
}
const std::vector<uint8_t>& data() {
return m_receiveData;
}
std::string dataString();
void writeAnswerHeader(enum enet::HTTPAnswerCode _value);
std::string escapeChar(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) {
ENET_VERBOSE("read [START]");
if (m_status != status::link) {
ENET_ERROR("Can not read on unlink connection");
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.
int timeout = (3 * 60 * 1000);
// Call poll() and wait 3 minutes for it to complete.
ENET_VERBOSE("Waiting on poll()...");
int rc = poll(m_fds, nfds, timeout);
// Check to see if the poll call failed.
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
if (rc == 0) {
ENET_ERROR(" Connection closed");
ENET_INFO(" Connection closed");
closeConn = true;
}
if (closeConn == false) {
// Data was received
size = rc;
ENET_VERBOSE(" " << size << " bytes received");
} else {
// If the close_conn flag was turned on, we need to clean up this active connection.
// 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;
}
ENET_VERBOSE("read [STOP]");
return size;
}

View File

@ -12,9 +12,7 @@ namespace enet {
class Tcp {
private:
int32_t m_socketId; //!< socket linux interface generic
#if 1
struct pollfd m_fds[1];
#endif
struct pollfd m_fds[1];
public:
Tcp();
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 <enet/Tcp.h>
#include <enet/TcpClient.h>
#include <enet/Http.h>
#include <etk/etk.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[]) {
etk::init(_argc, _argv);
@ -27,35 +36,25 @@ int main(int _argc, const char *_argv[]) {
TEST_INFO("== Test HTTP client ==");
TEST_INFO("==================================");
#ifndef __TARGET_OS__Windows
// client mode ...
enet::Http connection;
connection.setServer("127.0.0.1");
// connect on TCP server:
enet::Tcp tcpConnection = std::move(enet::connectTcpClient("127.0.0.1", 12345));
// TODO : Check if connection is valid ...
// Create a HTTP connection in Client mode
enet::Http connection(std::move(tcpConnection), false);
connection.setKeepAlive(true);
TEST_INFO("----------------------------");
TEST_INFO("GET data : ");
if (connection.get("") == false) {
TEST_ERROR("can not GET data...");
return -1;
}
TEST_INFO("data : " << connection.dataString());
// Set callbacks:
connection.connect(appl::onReceiveData);
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());
// start http connection (the actual state is just TCP start ...)
connection.start();
connection.get("plop.txt");
TEST_INFO("----------------------------");
TEST_INFO("POST xml : ");
if (connection.post("", /*"application/xml"*/ "text/xml; charset=utf-8", "<plop><string>value1</string></plop>") == false) {
TEST_ERROR("can not POST XML data...");
return -1;
while (connection.isAlive() == true) {
usleep(100000);
}
TEST_INFO("data : " << connection.dataString());
#else
TEST_CRITICAL("not implemented");
#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;
}