enet/enet/Http.cpp

844 lines
29 KiB
C++

/** @file
* @author Edouard DUPIN
* @copyright 2014, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
#include <enet/debug.hpp>
#include <enet/Http.hpp>
#include <etk/Map.hpp>
#include <etk/Stream.hpp>
#include <etk/stdTools.hpp>
#include <enet/TcpClient.hpp>
#include <enet/pourcentEncoding.hpp>
extern "C" {
#include <string.h>
}
static etk::String escapeChar(const etk::String& _value) {
return _value;
}
static etk::String unEscapeChar(const etk::String& _value) {
return _value;
}
static etk::String removeStartAndStopSpace(const etk::String& _value) {
etk::String out;
out.reserve(_value.size());
bool findSpace = false;
for (auto &it : _value) {
if (it != ' ') {
if ( findSpace == true
&& out.size() != 0) {
out += ' ';
}
out += it;
findSpace = false;
} else {
findSpace = true;
}
}
return out;
}
static const etk::Map<enet::HTTPAnswerCode, etk::String>& getProtocolName() {
static etk::Map<enet::HTTPAnswerCode, etk::String> protocolName;
static bool isInit = false;
if (isInit == true) {
return protocolName;
}
isInit = true;
protocolName.add(enet::HTTPAnswerCode::c100_continue, "Continue");
protocolName.add(enet::HTTPAnswerCode::c101_switchingProtocols, "Switching Protocols");
protocolName.add(enet::HTTPAnswerCode::c103_checkpoint, "Checkpoint");
protocolName.add(enet::HTTPAnswerCode::c200_ok, "OK");
protocolName.add(enet::HTTPAnswerCode::c201_created, "Created");
protocolName.add(enet::HTTPAnswerCode::c202_accepted, "Accepted");
protocolName.add(enet::HTTPAnswerCode::c203_nonAuthoritativeInformation, "Non-Authoritative Information");
protocolName.add(enet::HTTPAnswerCode::c204_noContent, "No Content");
protocolName.add(enet::HTTPAnswerCode::c205_resetContent, "Reset Content");
protocolName.add(enet::HTTPAnswerCode::c206_partialContent, "Partial Content");
protocolName.add(enet::HTTPAnswerCode::c300_multipleChoices, "Multiple Choices");
protocolName.add(enet::HTTPAnswerCode::c301_movedPermanently, "Moved Permanently");
protocolName.add(enet::HTTPAnswerCode::c302_found, "Found");
protocolName.add(enet::HTTPAnswerCode::c303_seeOther, "See Other");
protocolName.add(enet::HTTPAnswerCode::c304_notModified, "Not Modified");
protocolName.add(enet::HTTPAnswerCode::c306_switchProxy, "Switch Proxy");
protocolName.add(enet::HTTPAnswerCode::c307_temporaryRedirect, "Temporary Redirect");
protocolName.add(enet::HTTPAnswerCode::c308_resumeIncomplete, "Resume Incomplete");
protocolName.add(enet::HTTPAnswerCode::c400_badRequest, "Bad Request");
protocolName.add(enet::HTTPAnswerCode::c401_unauthorized, "Unauthorized");
protocolName.add(enet::HTTPAnswerCode::c402_paymentRequired, "Payment Required");
protocolName.add(enet::HTTPAnswerCode::c403_forbidden, "Forbidden");
protocolName.add(enet::HTTPAnswerCode::c404_notFound, "Not Found");
protocolName.add(enet::HTTPAnswerCode::c405_methodNotAllowed, "Method Not Allowed");
protocolName.add(enet::HTTPAnswerCode::c406_notAcceptable, "Not Acceptable");
protocolName.add(enet::HTTPAnswerCode::c407_proxyAuthenticationRequired, "Proxy Authentication Required");
protocolName.add(enet::HTTPAnswerCode::c408_requestTimeout, "Request Timeout");
protocolName.add(enet::HTTPAnswerCode::c409_conflict, "Conflict");
protocolName.add(enet::HTTPAnswerCode::c410_gone, "Gone");
protocolName.add(enet::HTTPAnswerCode::c411_lengthRequired, "Length Required");
protocolName.add(enet::HTTPAnswerCode::c412_preconditionFailed, "Precondition Failed");
protocolName.add(enet::HTTPAnswerCode::c413_requestEntityTooLarge, "Request Entity Too Large");
protocolName.add(enet::HTTPAnswerCode::c414_requestURITooLong, "Request-URI Too Long");
protocolName.add(enet::HTTPAnswerCode::c415_unsupportedMediaType, "Unsupported Media Type");
protocolName.add(enet::HTTPAnswerCode::c416_requestedRangeNotSatisfiable, "Requested Range Not Satisfiable");
protocolName.add(enet::HTTPAnswerCode::c417_expectationFailed, "Expectation Failed");
protocolName.add(enet::HTTPAnswerCode::c500_internalServerError, "Internal Server Error");
protocolName.add(enet::HTTPAnswerCode::c501_notImplemented, "Not Implemented");
protocolName.add(enet::HTTPAnswerCode::c502_badGateway, "Bad Gateway");
protocolName.add(enet::HTTPAnswerCode::c503_serviceUnavailable, "Service Unavailable");
protocolName.add(enet::HTTPAnswerCode::c504_gatewayTimeout, "Gateway Timeout");
protocolName.add(enet::HTTPAnswerCode::c505_httpVersionNotSupported, "HTTP Version Not Supported");
protocolName.add(enet::HTTPAnswerCode::c511_networkAuthenticationRequired, "Network Authentication Required");
return protocolName;
};
static etk::Map<int32_t, etk::String> getErrorList() {
static etk::Map<int32_t, etk::String> g_list;
return g_list;
}
enet::Http::Http(enet::Tcp _connection, bool _isServer) :
m_isServer(_isServer),
m_connection(etk::move(_connection)),
m_headerIsSend(false),
m_thread(nullptr),
m_threadRunning(false) {
//setSendHeaderProperties("User-Agent", "e-net (ewol network interface)");
/*
if (m_keepAlive == true) {
setSendHeaderProperties("Connection", "Keep-Alive");
}
*/
}
enet::Http::~Http() {
stop();
}
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();
if (m_headerIsSend == false) {
continue;
}
}
if (m_observerRaw != nullptr) {
m_observerRaw(m_connection);
} else {
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(m_temporaryBuffer);
}
}
}
}
m_threadRunning = false;
ENET_DEBUG("End of thread HTTP");
}
void enet::Http::redirectTo(const etk::String& _addressRedirect, bool _inThreadStop) {
if (m_isServer == true) {
ENET_ERROR("Request a redirect in Server mode ==> not authorised");
return;
}
stop(_inThreadStop);
m_headerIsSend = false;
m_connection = etk::move(connectTcpClient(_addressRedirect, 5, echrono::seconds(1)));
}
void enet::Http::start() {
ENET_DEBUG("connect [START]");
m_threadRunning = true;
m_thread = ETK_NEW(ethread::Thread, [&](){ threadCallback();});
if (m_thread == nullptr) {
m_threadRunning = false;
ENET_ERROR("creating callback thread!");
return;
}
while ( m_threadRunning == true
&& m_connection.getConnectionStatus() != enet::Tcp::status::link) {
ethread::sleepMilliSeconds((50));
}
//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) {
uint32_t size = 0xFFFFFFFF;
m_connection.write(&size, 4);
}
*/
if (m_connection.getConnectionStatus() != enet::Tcp::status::unlink) {
m_connection.unlink();
}
if (_inThreadStop == false) {
if (m_thread != nullptr) {
ENET_DEBUG("wait join Thread ...");
m_thread->join();
ENET_DEBUG("wait join Thread (done)");
ETK_DELETE(ethread::Thread, m_thread);
m_thread = nullptr;
}
}
ENET_DEBUG("disconnect [STOP]");
}
/*
void enet::Http::writeAnswerHeader(enum enet::HTTPAnswerCode _value) {
etk::String out;
out = "HTTP/1.1 ";
out += etk::toString(int32_t(_value));
auto it = getProtocolName().find(_value);
if (it == getProtocolName().end() ) {
out += " ???";
} else {
out += " " + it->second;
}
out += "\r\n\r\n";
ENET_WARNING("Write header :" << out);
write(out, false);
}
*/
namespace etk {
template <>
bool from_string<enum enet::HTTPAnswerCode>(enum enet::HTTPAnswerCode& _variableRet, const etk::String& _value) {
_variableRet = enet::HTTPAnswerCode::c000_unknow;
for (auto &it : getProtocolName()) {
if (etk::toString(int32_t(it.first)) == _value) {
_variableRet = it.first;
return true;
}
}
return false;
}
template <>
etk::String toString<enum enet::HTTPAnswerCode>(const enum enet::HTTPAnswerCode& _value) {
return etk::toString(int32_t(_value));
}
template <>
bool from_string<enum enet::HTTPReqType>(enum enet::HTTPReqType& _variableRet, const etk::String& _value) {
_variableRet = enet::HTTPReqType::HTTP_GET;
if (_value == "GET") {
_variableRet = enet::HTTPReqType::HTTP_GET;
return true;
} else if (_value == "HEAD") {
_variableRet = enet::HTTPReqType::HTTP_HEAD;
return true;
} else if (_value == "POST") {
_variableRet = enet::HTTPReqType::HTTP_POST;
return true;
} else if (_value == "PUT") {
_variableRet = enet::HTTPReqType::HTTP_PUT;
return true;
} else if (_value == "DELETE") {
_variableRet = enet::HTTPReqType::HTTP_DELETE;
return true;
} else if (_value == "OPTIONS") {
_variableRet = enet::HTTPReqType::HTTP_OPTIONS;
return true;
}
return false;
}
template <>
etk::String toString<enum enet::HTTPReqType>(const enum enet::HTTPReqType& _value) {
switch (_value) {
case enet::HTTPReqType::HTTP_GET: return "GET";
case enet::HTTPReqType::HTTP_HEAD: return "HEAD";
case enet::HTTPReqType::HTTP_POST: return "POST";
case enet::HTTPReqType::HTTP_PUT: return "PUT";
case enet::HTTPReqType::HTTP_DELETE: return "DELETE";
case enet::HTTPReqType::HTTP_OPTIONS: return "OPTIONS";
}
return "UNKNOW";
}
template <>
bool from_string<enum enet::HTTPProtocol>(enum enet::HTTPProtocol& _variableRet, const etk::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 <>
etk::String toString<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_isServer == true) {
if (m_requestHeader.getKey("Server") == "") {
m_requestHeader.setKey("Server", "e-net (ewol network interface)");
}
} else {
if (m_requestHeader.getKey("User-Agent") == "") {
m_requestHeader.setKey("User-Agent", "e-net (ewol network interface)");
}
}
etk::String value = m_requestHeader.generate();
write(value, false);
}
void enet::Http::setAnswerHeader(const enet::HttpAnswer& _req) {
m_answerHeader = _req;
if (m_isServer == true) {
if (m_requestHeader.getKey("Server") == "") {
m_requestHeader.setKey("Server", "e-net (ewol network interface)");
}
} else {
if (m_requestHeader.getKey("User-Agent") == "") {
m_requestHeader.setKey("User-Agent", "e-net (ewol network interface)");
}
}
etk::String value = m_answerHeader.generate();
write(value, false);
}
void enet::Http::getHeader() {
ENET_VERBOSE("Read HTTP Header [START]");
bool headerEnded = false;
etk::String header;
while (m_connection.getConnectionStatus() == enet::Tcp::status::link) {
char type = '?';
int32_t len = m_connection.read(&type, 1);
if (len == 0) {
ethread::sleepMilliSeconds(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_ERROR("Read HTTP Header [STOP] : '" << header << "' ==> status move in unlink ...");
return;
}
ENET_VERBOSE("Read HTTP Header [STOP] : '" << header << "'");
m_headerIsSend = true;
// parse header :
etk::Vector<etk::String> list = etk::split(header, '\n');
for (auto &it : list) {
if ( it.size()>0
&& it[it.size()-1] == '\r') {
it.resize(it.size()-1);
}
}
//parse first element:
etk::Vector<etk::String> listLineOne = etk::split(list[0], ' ');
if (listLineOne.size() < 2) {
ENET_ERROR("can not parse answear : " << listLineOne);
// answer bad request and close connection ...
return;
}
if ( listLineOne.size() >= 3
&& ( listLineOne[0] == "GET"
|| listLineOne[0] == "POST"
|| listLineOne[0] == "HEAD"
|| listLineOne[0] == "DELETE"
|| listLineOne[0] == "PUT"
|| listLineOne[0] == "OPTIONS" ) ) {
// 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:
etk::String basicUri = listLineOne[1];
size_t pos = basicUri.find('?');
if (pos == etk::String::npos) {
m_requestHeader.setUri(listLineOne[1]);
} else {
m_requestHeader.setUri(basicUri.extract(0, pos));
m_requestHeader.setQuery(pourcentUriDecode(basicUri.extract(pos+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:
etk::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 == etk::String::npos) {
// nothing
continue;
}
etk::String key = unEscapeChar(etk::String(list[iii], 0, found));
key = removeStartAndStopSpace(key);
etk::String value = unEscapeChar(etk::String(list[iii], found+2));
value = removeStartAndStopSpace(value);
ENET_VERBOSE("header : key='" << key << "' value='" << value << "'");
if (m_isServer == false) {
m_answerHeader.setKey(key,value);
} else {
m_requestHeader.setKey(key,value);
}
if ( key == "Connection"
&& value == "close") {
ENET_DEBUG("connection closed by remote :");
m_connection.unlink();
}
}
m_headerIsSend = true;
if (m_isServer == false) {
if (m_observerAnswer != nullptr) {
m_observerAnswer(m_answerHeader);
}
} else {
if (m_observerRequest != nullptr) {
m_observerRequest(m_requestHeader);
}
}
}
/*
bool enet::Http::get(const etk::String& _address) {
m_header.m_map.clear();
etk::String req = "GET http://" + m_connection.getName();
if (_address != "") {
req += "/";
req += _address;
}
req += " HTTP/1.1\r\n";
setSendHeaderProperties("Content-Length", "0");
// add header properties :
for (auto &it : m_header.m_map) {
req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\r\n";
}
// end of header
req += "\r\n";
// no body:
int32_t len = m_connection.write(req, false);
ENET_VERBOSE("read write=" << len << " data: " << req);
if (len != req.size()) {
ENET_ERROR("An error occured when sending data " << len << "!=" << req.size());
return false;
}
//return receiveData();
return false;
}
bool enet::Http::post(const etk::String& _address, const etk::Map<etk::String, etk::String>& _values) {
m_header.m_map.clear();
// First create body :
etk::String body;
for (auto &it : _values) {
if (body.size() > 0) {
body += "&";
}
body += escapeChar(it.first) + "=" + escapeChar(it.second);
}
return post(_address, "application/x-www-form-urlencoded", body);
}
bool enet::Http::post(const etk::String& _address, const etk::String& _contentType, const etk::String& _data) {
m_header.m_map.clear();
etk::String req = "POST http://" + m_connection.getName();
if (_address != "") {
req += "/";
req += _address;
}
req += " HTTP/1.0\n";
setSendHeaderProperties("Content-Type", _contentType);
setSendHeaderProperties("Content-Length", etk::toString(_data.size()));
// add header properties :
for (auto &it : m_header.m_map) {
req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\r\n";
}
// end of header
req += "\n";
req += _data;
int32_t len = m_connection.write(req, false);
ENET_VERBOSE("read write=" << len << " data: " << req);
if (len != req.size()) {
ENET_ERROR("An error occured when sending data " << len << "!=" << req.size());
return false;
}
//return receiveData();
return false;
}
*/
int32_t enet::Http::write(const void* _data, int32_t _len) {
return m_connection.write(_data, _len);
}
void enet::HttpHeader::setKey(const etk::String& _key, const etk::String& _value) {
auto it = m_map.find(_key);
if (it == m_map.end()) {
m_map.add(_key, _value);
} else {
it->second = _value;
}
}
void enet::HttpHeader::rmKey(const etk::String& _key) {
auto it = m_map.find(_key);
if (it != m_map.end()) {
m_map.erase(it);
}
}
etk::String enet::HttpHeader::getKey(const etk::String& _key) const {
auto it = m_map.find(_key);
if (it != m_map.end()) {
return it->second;
}
return "";
}
bool enet::HttpHeader::existKey(const etk::String& _key) const {
auto it = m_map.find(_key);
if (it != m_map.end()) {
return true;
}
return false;
}
etk::String enet::HttpHeader::generateKeys() const {
etk::String out;
for (auto &it : m_map) {
if ( it.first != ""
&& it.second != "") {
out += escapeChar(it.first) + ": " + escapeChar(it.second) + "\r\n";
}
}
return out;
}
void enet::HttpHeader::setQuery(const etk::Map<etk::String, etk::String>& _value) {
m_query = _value;
}
void enet::HttpHeader::setQueryKey(const etk::String& _key, const etk::String& _value) {
auto it = m_query.find(_key);
if (it == m_query.end()) {
m_query.add(_key, _value);
} else {
it->second = _value;
}
}
void enet::HttpHeader::rmQueryKey(const etk::String& _key) {
auto it = m_query.find(_key);
if (it != m_query.end()) {
m_query.erase(it);
}
}
etk::String enet::HttpHeader::getQueryKey(const etk::String& _key) const {
auto it = m_query.find(_key);
if (it != m_query.end()) {
return it->second;
}
return "";
}
bool enet::HttpHeader::existQueryKey(const etk::String& _key) const {
auto it = m_query.find(_key);
if (it != m_query.end()) {
return true;
}
return false;
}
etk::String enet::HttpHeader::generateQueryKeys() const {
return enet::pourcentUriEncode(m_query);
}
enet::HttpHeader::HttpHeader():
m_protocol(enet::HTTPProtocol::http_1_0) {
}
// -----------------------------------------------------------------------------------------
enet::HttpAnswer::HttpAnswer(enum HTTPAnswerCode _code, const etk::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 + "'");
}
}
ENET_PRINT(" query:");
for (auto &it : m_query) {
if ( it.first != ""
&& it.second != "") {
ENET_PRINT(" '" + it.first + "' = '" + it.second + "'");
}
}
}
etk::String enet::HttpAnswer::generate() const {
etk::String out;
out += etk::toString(m_protocol);
out += " ";
out += etk::toString(int32_t(m_what));
out += " ";
if (m_helpMessage != "") {
out += escapeChar(m_helpMessage);
} else {
auto it = getProtocolName().find(m_what);
if (it != getProtocolName().end()) {
out += escapeChar(it->second);
} else {
out += "???";
}
}
out += "\r\n";
out += generateKeys();
out += "\r\n";
return out;
}
enet::HttpServer::HttpServer(enet::Tcp _connection) :
enet::Http(etk::move(_connection), true) {
}
enet::HttpClient::HttpClient(enet::Tcp _connection) :
enet::Http(etk::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) {
if ( it.first != ""
&& it.second != "") {
ENET_PRINT(" '" + it.first + "' = '" + it.second + "'");
}
}
ENET_PRINT(" query:");
for (auto &it : m_query) {
if ( it.first != ""
&& it.second != "") {
ENET_PRINT(" '" + it.first + "' = '" + it.second + "'");
}
}
}
etk::String enet::HttpRequest::generate() const {
etk::String out;
out += etk::toString(m_req);
out += " ";
out += m_uri;
etk::String querryData = generateQueryKeys();
if (querryData.empty() != 0) {
out += "?" + querryData;
}
out += " ";
out += etk::toString(m_protocol);
out += "\r\n";
out += generateKeys();
out += "\r\n";
return out;
}
etk::Stream& enet::operator <<(etk::Stream& _os, enum enet::HTTPProtocol _obj) {
_os << "enet::HTTPProtocol::" <<etk::toString(_obj);
return _os;
}
etk::Stream& enet::operator <<(etk::Stream& _os, enum enet::HTTPAnswerCode _obj) {
_os << "enet::HTTPAnswerCode::" << etk::toString(_obj);
return _os;
}
etk::Stream& enet::operator <<(etk::Stream& _os, enum enet::HTTPReqType _obj) {
_os << "enet::HTTPReqType::" << etk::toString(_obj);
return _os;
}