[DEV] add websocket nearly correct
This commit is contained in:
parent
83c9cf69b0
commit
4f82e11375
@ -17,6 +17,24 @@ static std::string escapeChar(const std::string& _value) {
|
||||
static std::string unEscapeChar(const std::string& _value) {
|
||||
return _value;
|
||||
}
|
||||
static std::string removeStartAndStopSpace(const std::string& _value) {
|
||||
std::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 std::map<enet::HTTPAnswerCode, std::string> protocolName = {
|
||||
{enet::HTTPAnswerCode::c100_continue, "Continue"},
|
||||
@ -100,12 +118,16 @@ void enet::Http::threadCallback() {
|
||||
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(m_temporaryBuffer);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -346,21 +368,25 @@ void enet::Http::getHeader() {
|
||||
continue;
|
||||
}
|
||||
header += type;
|
||||
if ( header.size() > 4
|
||||
if ( header.size() > 6
|
||||
&& header[header.size()-1] == '\n'
|
||||
&& header[header.size()-2] == '\r'
|
||||
&& header[header.size()-3] == '\n'
|
||||
&& header[header.size()-4] == '\r') {
|
||||
&& header[header.size()-4] == '\r'
|
||||
&& header[header.size()-5] == '\n'
|
||||
&& header[header.size()-6] == '\r') {
|
||||
// Normal end case ...
|
||||
break;
|
||||
} else if ( header.size() > 2
|
||||
} else if ( header.size() > 3
|
||||
&& header[header.size()-1] == '\n'
|
||||
&& header[header.size()-2] == '\n') {
|
||||
&& header[header.size()-2] == '\n'
|
||||
&& header[header.size()-3] == '\n') {
|
||||
// linux end case
|
||||
break;
|
||||
} else if ( header.size() > 2
|
||||
} else if ( header.size() > 3
|
||||
&& header[header.size()-1] == '\r'
|
||||
&& header[header.size()-2] == '\r') {
|
||||
&& header[header.size()-2] == '\r'
|
||||
&& header[header.size()-3] == '\r') {
|
||||
// Mac end case
|
||||
break;
|
||||
}
|
||||
@ -455,7 +481,9 @@ void enet::Http::getHeader() {
|
||||
continue;
|
||||
}
|
||||
std::string key = unEscapeChar(std::string(list[iii], 0, found));
|
||||
key = removeStartAndStopSpace(key);
|
||||
std::string value = unEscapeChar(std::string(list[iii], found+2));
|
||||
value = removeStartAndStopSpace(value);
|
||||
ENET_VERBOSE("header : key='" << key << "' value='" << value << "'");
|
||||
if (m_isServer == false) {
|
||||
m_answerHeader.setKey(key,value);
|
||||
@ -572,6 +600,13 @@ void enet::HttpHeader::rmKey(const std::string& _key) {
|
||||
}
|
||||
|
||||
std::string enet::HttpHeader::getKey(const std::string& _key) const {
|
||||
ENET_WARNING("search key: " << _key << " in:");
|
||||
for (auto &it : m_map) {
|
||||
ENET_WARNING(" '" << it.first << "' : '" << it.second << "'");
|
||||
if (it.first == _key) {
|
||||
ENET_WARNING(" ==> Find");
|
||||
}
|
||||
}
|
||||
auto it = m_map.find(_key);
|
||||
if (it != m_map.end()) {
|
||||
return it->second;
|
||||
|
22
enet/Http.h
22
enet/Http.h
@ -145,13 +145,13 @@ namespace enet {
|
||||
void setErrorCode(enum HTTPAnswerCode _value) {
|
||||
m_what = _value;
|
||||
}
|
||||
enum HTTPAnswerCode getErrorCode() {
|
||||
enum HTTPAnswerCode getErrorCode() const {
|
||||
return m_what;
|
||||
}
|
||||
void setHelp(const std::string& _value) {
|
||||
m_helpMessage = _value;
|
||||
}
|
||||
const std::string& getHelp() {
|
||||
const std::string& getHelp() const {
|
||||
return m_helpMessage;
|
||||
}
|
||||
};
|
||||
@ -246,6 +246,24 @@ namespace enet {
|
||||
void connect(Observer _func) {
|
||||
m_observer = _func;
|
||||
}
|
||||
public:
|
||||
using ObserverRaw = std::function<void(enet::Tcp&)>; //!< Define an Observer: function pointer
|
||||
ObserverRaw m_observerRaw;
|
||||
/**
|
||||
* @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 connectRaw(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(enet::Tcp&)) {
|
||||
m_observerRaw = [=](enet::Tcp& _connection){
|
||||
(*_class.*_func)(_connection);
|
||||
};
|
||||
}
|
||||
void connectRaw(ObserverRaw _func) {
|
||||
m_observerRaw = _func;
|
||||
}
|
||||
public:
|
||||
using ObserverRequest = std::function<void(const enet::HttpRequest&)>; //!< Define an Observer: function pointer
|
||||
protected:
|
||||
|
@ -9,12 +9,25 @@
|
||||
#include <map>
|
||||
#include <etk/stdTools.h>
|
||||
#include <string.h>
|
||||
#include <random>
|
||||
|
||||
|
||||
namespace enet {
|
||||
namespace websocket {
|
||||
static const uint32_t FLAG_FIN = 0x80;
|
||||
static const uint32_t FLAG_MASK = 0x80;
|
||||
static const uint32_t OPCODE_FRAME_TEXT = 0x01;
|
||||
static const uint32_t OPCODE_FRAME_BINARY = 0x02;
|
||||
static const uint32_t OPCODE_FRAME_CLOSE = 0x08;
|
||||
static const uint32_t OPCODE_FRAME_PING = 0x09;
|
||||
static const uint32_t OPCODE_FRAME_PONG = 0x0A;
|
||||
}
|
||||
}
|
||||
|
||||
enet::WebSocket::WebSocket(enet::Tcp _connection, bool _isServer) :
|
||||
m_interface(),
|
||||
m_observer(nullptr) {
|
||||
m_observer(nullptr),
|
||||
m_observerUriCheck(nullptr) {
|
||||
if (_isServer == true) {
|
||||
ememory::SharedPtr<enet::HttpServer> interface = std::make_shared<enet::HttpServer>(std::move(_connection));
|
||||
interface->connectHeader(this, &enet::WebSocket::onReceiveRequest);
|
||||
@ -24,7 +37,7 @@ enet::WebSocket::WebSocket(enet::Tcp _connection, bool _isServer) :
|
||||
interface->connectHeader(this, &enet::WebSocket::onReceiveAnswer);
|
||||
m_interface = interface;
|
||||
}
|
||||
m_interface->connect(this, &enet::WebSocket::onReceiveData);
|
||||
m_interface->connectRaw(this, &enet::WebSocket::onReceiveData);
|
||||
}
|
||||
|
||||
enet::WebSocket::~WebSocket() {
|
||||
@ -44,7 +57,14 @@ void enet::WebSocket::start(const std::string& _uri) {
|
||||
if (m_interface->isServer() == false) {
|
||||
enet::HttpRequest req(enet::HTTPReqType::GET);
|
||||
req.setUri(_uri);
|
||||
std::dynamic_pointer_cast<enet::HttpClient>(m_interface)->setHeader(req);
|
||||
req.setKey("Upgrade", "websocket");
|
||||
req.setKey("Connection", "Upgrade");
|
||||
req.setKey("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ=="); // this is an example key ...
|
||||
req.setKey("Sec-WebSocket-Version", "13");
|
||||
ememory::SharedPtr<enet::HttpClient> interface = std::dynamic_pointer_cast<enet::HttpClient>(m_interface);
|
||||
if (interface!=nullptr) {
|
||||
interface->setHeader(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,20 +77,197 @@ void enet::WebSocket::stop(bool _inThread) {
|
||||
m_interface.reset();
|
||||
}
|
||||
|
||||
void enet::WebSocket::onReceiveData(std::vector<uint8_t>& _data) {
|
||||
if (m_interface == nullptr) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
void enet::WebSocket::onReceiveData(enet::Tcp& _connection) {
|
||||
ENET_VERBOSE("Read Binary [START]");
|
||||
uint8_t opcode = 0;
|
||||
int32_t len = _connection.read(&opcode, sizeof(uint8_t));
|
||||
if (len <= 0) {
|
||||
if (len < 0) {
|
||||
ENET_ERROR("Protocol error occured ...");
|
||||
ENET_VERBOSE("ReadRaw 1 [STOP]");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
ENET_ERROR("Time out ... ==> not managed ...");
|
||||
ENET_VERBOSE("ReadRaw 2 [STOP]");
|
||||
return;
|
||||
}
|
||||
ENET_ERROR("manage receive data event ...");
|
||||
ENET_VERBOSE("Read opcode : " << uint32_t(opcode));
|
||||
if ((opcode & 0x80) == 0) {
|
||||
ENET_ERROR("Multiple frames ... NOT managed ...");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
int8_t size1 = 0;
|
||||
len = _connection.read(&size1, sizeof(uint8_t));
|
||||
ENET_VERBOSE("Read payload : " << uint32_t(size1));
|
||||
if (len <= 0) {
|
||||
if (len < 0) {
|
||||
ENET_ERROR("Protocol error occured ...");
|
||||
ENET_VERBOSE("ReadRaw 1 [STOP]");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
ENET_ERROR("Time out ... ==> not managed ...");
|
||||
ENET_VERBOSE("ReadRaw 2 [STOP]");
|
||||
return;
|
||||
}
|
||||
uint64_t totalSize = size1 & 0x7F;
|
||||
if (totalSize == 126) {
|
||||
uint16_t tmpSize;
|
||||
len = _connection.read(&tmpSize, sizeof(uint16_t));
|
||||
if (len <= 1) {
|
||||
if (len < 0) {
|
||||
ENET_ERROR("Protocol error occured ...");
|
||||
ENET_VERBOSE("ReadRaw 1 [STOP]");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
ENET_ERROR("Time out ... ==> not managed ...");
|
||||
ENET_VERBOSE("ReadRaw 2 [STOP]");
|
||||
return;
|
||||
}
|
||||
totalSize = tmpSize;
|
||||
} else if (totalSize == 127) {
|
||||
len = _connection.read(&totalSize, sizeof(uint64_t));
|
||||
if (len <= 7) {
|
||||
if (len < 0) {
|
||||
ENET_ERROR("Protocol error occured ...");
|
||||
ENET_VERBOSE("ReadRaw 1 [STOP]");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
ENET_ERROR("Time out ... ==> not managed ...");
|
||||
ENET_VERBOSE("ReadRaw 2 [STOP]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
uint8_t dataMask[4];
|
||||
// Need get the mask:
|
||||
if ((size1 & 0x80) != 0) {
|
||||
len = _connection.read(&dataMask, sizeof(uint32_t));
|
||||
if (len <= 3) {
|
||||
if (len < 0) {
|
||||
ENET_ERROR("Protocol error occured ...");
|
||||
ENET_VERBOSE("ReadRaw 1 [STOP]");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
ENET_ERROR("Time out ... ==> not managed ...");
|
||||
ENET_VERBOSE("ReadRaw 2 [STOP]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_buffer.resize(totalSize);
|
||||
if (totalSize > 0) {
|
||||
uint64_t offset = 0;
|
||||
while (offset != totalSize) {
|
||||
len = _connection.read(&m_buffer[offset], totalSize-offset);
|
||||
offset += len;
|
||||
if (len == 0) {
|
||||
ENET_WARNING("Read No data");
|
||||
}
|
||||
if (len < 0) {
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Need apply the mask:
|
||||
if ((size1 & 0x80) != 0) {
|
||||
for (size_t iii= 0; iii<m_buffer.size(); ++iii) {
|
||||
m_buffer[iii] ^= dataMask[iii%4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check opcode:
|
||||
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_CLOSE) {
|
||||
// Close the conection by remote:
|
||||
ENET_INFO("Close connection by remote :");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_PING) {
|
||||
// Close the conection by remote:
|
||||
ENET_INFO("Receive a ping (send a pong)");
|
||||
controlPong();
|
||||
return;
|
||||
}
|
||||
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_PONG) {
|
||||
// Close the conection by remote:
|
||||
ENET_INFO("Receive a pong");
|
||||
return;
|
||||
}
|
||||
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_TEXT) {
|
||||
// Close the conection by remote:
|
||||
ENET_DEBUG("Receive a Text(UTF-8) data " << m_buffer.size() << " Bytes");
|
||||
if (m_observer != nullptr) {
|
||||
m_observer(m_buffer, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_BINARY) {
|
||||
// Close the conection by remote:
|
||||
ENET_DEBUG("Receive a binary data " << m_buffer.size() << " Bytes");
|
||||
if (m_observer != nullptr) {
|
||||
m_observer(m_buffer, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ENET_ERROR("ReadRaw [STOP] (no opcode manage ... " << int32_t(opcode & 0x0F));
|
||||
|
||||
}
|
||||
|
||||
void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) {
|
||||
if (m_interface == nullptr) {
|
||||
ememory::SharedPtr<enet::HttpServer> interface = std::dynamic_pointer_cast<enet::HttpServer>(m_interface);
|
||||
if (interface == nullptr) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
_data.display();
|
||||
if (_data.getType() != enet::HTTPReqType::GET) {
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c400_badRequest, "support only GET");
|
||||
answer.setKey("Connection", "close");
|
||||
interface->setHeader(answer);
|
||||
interface->stop(true);
|
||||
return;
|
||||
}
|
||||
if (_data.getKey("Connection") == "close") {
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c200_ok);
|
||||
answer.setKey("Connection", "close");
|
||||
interface->setHeader(answer);
|
||||
interface->stop(true);
|
||||
return;
|
||||
}
|
||||
if (_data.getKey("Upgrade") != "websocket") {
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c400_badRequest, "websocket support only with Upgrade: websocket");
|
||||
answer.setKey("Connection", "close");
|
||||
interface->setHeader(answer);
|
||||
interface->stop(true);
|
||||
return;
|
||||
}
|
||||
if (_data.getKey("Sec-WebSocket-Key") == "") {
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c400_badRequest, "websocket missing 'Sec-WebSocket-Key'");
|
||||
answer.setKey("Connection", "close");
|
||||
interface->setHeader(answer);
|
||||
interface->stop(true);
|
||||
return;
|
||||
}
|
||||
if (m_observerUriCheck != nullptr) {
|
||||
if (m_observerUriCheck(_data.getUri()) == false) {
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c404_notFound);
|
||||
answer.setKey("Connection", "close");
|
||||
interface->setHeader(answer);
|
||||
interface->stop(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c101_switchingProtocols);
|
||||
answer.setKey("Upgrade", "websocket");
|
||||
answer.setKey("Connection", "Upgrade");
|
||||
// TODO: Do it better:
|
||||
answer.setKey("Sec-WebSocket-Accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="); //base64::encode());
|
||||
interface->setHeader(answer);
|
||||
}
|
||||
|
||||
void enet::WebSocket::onReceiveAnswer(const enet::HttpAnswer& _data) {
|
||||
@ -79,13 +276,116 @@ void enet::WebSocket::onReceiveAnswer(const enet::HttpAnswer& _data) {
|
||||
return;
|
||||
}
|
||||
_data.display();
|
||||
if (_data.getErrorCode() != enet::HTTPAnswerCode::c101_switchingProtocols) {
|
||||
ENET_ERROR("change protocol has not been accepted ... " << _data.getErrorCode() << " with message : " << _data.getHelp());
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
if (_data.getKey("Connection") != "Upgrade") {
|
||||
ENET_ERROR("Missing key : 'Connection : Upgrade' get '" << _data.getKey("Connection") << "'");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
if (_data.getKey("Upgrade") != "websocket") {
|
||||
ENET_ERROR("Missing key : 'Upgrade : websocket' get '" << _data.getKey("Upgrade") << "'");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
// NOTE : This is a temporary magic check ...
|
||||
if (_data.getKey("Sec-WebSocket-Accept") != "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") {
|
||||
ENET_ERROR("Wrong key : 'Sec-WebSocket-Accept : xxx' get '" << _data.getKey("Sec-WebSocket-Accept") << "'");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int32_t enet::WebSocket::write(const void* _data, int32_t _len) {
|
||||
int32_t enet::WebSocket::write(const void* _data, int32_t _len, bool _isString, bool _mask) {
|
||||
if (m_interface == nullptr) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return -1;
|
||||
}
|
||||
// TODO : ...
|
||||
return -1;
|
||||
}
|
||||
uint8_t mask = 0;
|
||||
if (_mask == true) {
|
||||
mask = enet::websocket::FLAG_MASK;
|
||||
}
|
||||
uint8_t header = enet::websocket::FLAG_FIN;
|
||||
if (_isString == false) {
|
||||
header |= enet::websocket::OPCODE_FRAME_BINARY;
|
||||
} else {
|
||||
header |= enet::websocket::OPCODE_FRAME_TEXT;
|
||||
}
|
||||
m_interface->write(&header, sizeof(uint8_t));
|
||||
ENET_VERBOSE("write opcode : " << int32_t(header));
|
||||
if (_len < 126) {
|
||||
uint8_t size = _len | mask;
|
||||
ENET_VERBOSE("write payload : " << int32_t(size));
|
||||
m_interface->write(&size, sizeof(uint8_t));
|
||||
} else if (_len < 65338) {
|
||||
uint8_t payload = 126 | mask;
|
||||
ENET_VERBOSE("write payload : " << int32_t(payload));
|
||||
m_interface->write(&payload, sizeof(uint8_t));
|
||||
uint16_t size = _len;
|
||||
ENET_VERBOSE("write size : " << int32_t(size));
|
||||
m_interface->write(&size, sizeof(uint16_t));
|
||||
} else {
|
||||
uint8_t payload = 127 | mask;
|
||||
ENET_VERBOSE("write payload : " << int32_t(payload));
|
||||
m_interface->write(&payload, sizeof(uint8_t));
|
||||
uint64_t size = _len;
|
||||
ENET_VERBOSE("write size : " << size);
|
||||
m_interface->write(&size, sizeof(uint64_t));
|
||||
}
|
||||
if (mask != 0 ) {
|
||||
std::random_device rd;
|
||||
// Engine
|
||||
std::mt19937 e2(rd());
|
||||
// Distribtuions
|
||||
std::uniform_real_distribution<> dist(0, 0xFF);
|
||||
uint8_t dataMask[4];
|
||||
dataMask[0] = uint8_t(dist(e2));
|
||||
dataMask[1] = uint8_t(dist(e2));
|
||||
dataMask[2] = uint8_t(dist(e2));
|
||||
dataMask[3] = uint8_t(dist(e2));
|
||||
m_interface->write(&dataMask, sizeof(uint32_t));
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(_len);
|
||||
const uint8_t* pdata = static_cast<const uint8_t*>(_data);
|
||||
for (size_t iii= 0; iii<_len; ++iii) {
|
||||
data[iii] = pdata[iii] ^ dataMask[iii%4];
|
||||
}
|
||||
return m_interface->write(&data[0], data.size());
|
||||
}
|
||||
return m_interface->write(_data, _len);
|
||||
}
|
||||
|
||||
void enet::WebSocket::controlPing() {
|
||||
if (m_interface == nullptr) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
uint16_t header = ( enet::websocket::FLAG_FIN
|
||||
| enet::websocket::OPCODE_FRAME_PING) << 8;
|
||||
m_interface->write(&header, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
void enet::WebSocket::controlPong() {
|
||||
if (m_interface == nullptr) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
uint16_t header = ( enet::websocket::FLAG_FIN
|
||||
| enet::websocket::OPCODE_FRAME_PONG) << 8;
|
||||
m_interface->write(&header, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
void enet::WebSocket::contolClose() {
|
||||
if (m_interface == nullptr) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
uint16_t header = ( enet::websocket::FLAG_FIN
|
||||
| enet::websocket::OPCODE_FRAME_CLOSE) << 8;
|
||||
m_interface->write(&header, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,9 @@
|
||||
|
||||
namespace enet {
|
||||
class WebSocket {
|
||||
private:
|
||||
protected:
|
||||
ememory::SharedPtr<enet::Http> m_interface;
|
||||
std::vector<uint8_t> m_buffer;
|
||||
public:
|
||||
WebSocket(enet::Tcp _connection, bool _isServer=false);
|
||||
virtual ~WebSocket();
|
||||
@ -22,12 +23,12 @@ namespace enet {
|
||||
bool isAlive() {
|
||||
return m_interface->isAlive();
|
||||
}
|
||||
void onReceiveData(std::vector<uint8_t>& _data);
|
||||
void onReceiveData(enet::Tcp& _data);
|
||||
void onReceiveRequest(const enet::HttpRequest& _data);
|
||||
void onReceiveAnswer(const enet::HttpAnswer& _data);
|
||||
public:
|
||||
using Observer = std::function<void(std::vector<uint8_t>&)>; //!< Define an Observer: function pointer
|
||||
private:
|
||||
using Observer = std::function<void(std::vector<uint8_t>&, bool)>; //!< Define an Observer: function pointer
|
||||
protected:
|
||||
Observer m_observer;
|
||||
public:
|
||||
/**
|
||||
@ -37,14 +38,35 @@ namespace enet {
|
||||
* @param[in] _args Argument optinnal the user want to add.
|
||||
*/
|
||||
template<class CLASS_TYPE>
|
||||
void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(std::vector<uint8_t>&)) {
|
||||
m_observer = [=](std::vector<uint8_t>& _value){
|
||||
(*_class.*_func)(_value);
|
||||
void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(std::vector<uint8_t>&, bool)) {
|
||||
m_observer = [=](std::vector<uint8_t>& _value, bool _isString){
|
||||
(*_class.*_func)(_value, _isString);
|
||||
};
|
||||
}
|
||||
void connect(Observer _func) {
|
||||
m_observer = _func;
|
||||
}
|
||||
// Only server:
|
||||
public:
|
||||
using ObserverUriCheck = std::function<bool(const std::string&)>; //!< Define an Observer: function pointer
|
||||
protected:
|
||||
ObserverUriCheck m_observerUriCheck;
|
||||
public:
|
||||
/**
|
||||
* @brief Connect an function member on the signal with the shared_ptr object.
|
||||
* @param[in] _class shared_ptr Object on whe we need to call ==> the object is get in keeped in weak_ptr.
|
||||
* @param[in] _func Function to call.
|
||||
* @param[in] _args Argument optinnal the user want to add.
|
||||
*/
|
||||
template<class CLASS_TYPE>
|
||||
void connectUri(CLASS_TYPE* _class, bool (CLASS_TYPE::*_func)(const std::string&)) {
|
||||
m_observerUriCheck = [=](const std::string& _value){
|
||||
(*_class.*_func)(_value);
|
||||
};
|
||||
}
|
||||
void connectUri(ObserverUriCheck _func) {
|
||||
m_observerUriCheck = _func;
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Write a chunk of data on the socket
|
||||
@ -53,7 +75,8 @@ namespace enet {
|
||||
* @return >0 byte size on the socket write
|
||||
* @return -1 an error occured.
|
||||
*/
|
||||
int32_t write(const void* _data, int32_t _len);
|
||||
//TODO : ...
|
||||
int32_t write(const void* _data, int32_t _len, bool _isString=false, bool _mask= false);
|
||||
/**
|
||||
* @brief Write a chunk of data on the socket
|
||||
* @param[in] _data String to rite on the soccket
|
||||
@ -66,9 +89,9 @@ namespace enet {
|
||||
return 0;
|
||||
}
|
||||
if (_writeBackSlashZero == true) {
|
||||
return write(_data.c_str(), _data.size()+1);
|
||||
return write(_data.c_str(), _data.size()+1, true);
|
||||
}
|
||||
return write(_data.c_str(), _data.size());
|
||||
return write(_data.c_str(), _data.size(), true);
|
||||
}
|
||||
/**
|
||||
* @brief Write a chunk of data on the socket
|
||||
@ -88,5 +111,9 @@ namespace enet {
|
||||
}
|
||||
return ret/sizeof(T);
|
||||
}
|
||||
protected:
|
||||
void controlPing();
|
||||
void controlPong();
|
||||
void contolClose();
|
||||
};
|
||||
}
|
||||
|
41
lutin_enet-test-client-websocket.py
Normal file
41
lutin_enet-test-client-websocket.py
Normal 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-client-websocket.cpp'
|
||||
])
|
||||
return my_module
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
41
lutin_enet-test-server-websocket.py
Normal file
41
lutin_enet-test-server-websocket.py
Normal 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-websocket.cpp'
|
||||
])
|
||||
return my_module
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
72
test/main-client-websocket.cpp
Normal file
72
test/main-client-websocket.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
/** @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/TcpClient.h>
|
||||
#include <enet/Http.h>
|
||||
#include <enet/WebSocket.h>
|
||||
#include <etk/etk.h>
|
||||
|
||||
#include <etk/stdTools.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace appl {
|
||||
void onReceiveData(std::vector<uint8_t>& _data, bool _isString) {
|
||||
TEST_INFO("Receive Datas : " << _data.size() << " bytes");
|
||||
if (_isString == true) {
|
||||
_data.resize(_data.size()+1);
|
||||
_data[_data.size()-1] = '\0';
|
||||
TEST_INFO("string data: '" << (char*)&_data[0] << "'");
|
||||
} else {
|
||||
TEST_INFO("binary data: ... ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 WebSocket client ==");
|
||||
TEST_INFO("==================================");
|
||||
#ifndef __TARGET_OS__Windows
|
||||
// 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::WebSocket connection(std::move(tcpConnection), false);
|
||||
// Set callbacks:
|
||||
connection.connect(appl::onReceiveData);
|
||||
|
||||
// start http connection (the actual state is just TCP start ...)
|
||||
connection.start("plop.txt");
|
||||
|
||||
// send some data to play ...
|
||||
connection.write("coucou comment ca vas ???");
|
||||
|
||||
int32_t timeout = 20;
|
||||
while (connection.isAlive() == true
|
||||
&& timeout > 0) {
|
||||
usleep(100000);
|
||||
timeout--;
|
||||
}
|
||||
|
||||
#else
|
||||
TEST_CRITICAL("not implemented");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
@ -20,20 +20,20 @@ namespace appl {
|
||||
TEST_INFO("Receive Header data:");
|
||||
_data.display();
|
||||
if (_data.getType() == enet::HTTPReqType::GET) {
|
||||
if (_data.getUri() == "http://127.0.0.1:12345/plop.txt") {
|
||||
if (_data.getUri() == "plop.txt") {
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c200_ok);
|
||||
std::string data = "<html><head></head></body>coucou</body></html>";
|
||||
answer.setKey("Content-Length", etk::to_string(data.size()));
|
||||
_interface->setHeader(answer);
|
||||
_interface->write(data);
|
||||
_interface->stop();
|
||||
_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c404_notFound);
|
||||
answer.setKey("Connection", "close");
|
||||
_interface->setHeader(answer);
|
||||
_interface->stop();
|
||||
_interface->stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
87
test/main-server-websocket.cpp
Normal file
87
test/main-server-websocket.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
/** @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/WebSocket.h>
|
||||
#include <enet/TcpServer.h>
|
||||
#include <etk/etk.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <etk/stdTools.h>
|
||||
namespace appl {
|
||||
void onReceiveData(enet::WebSocket* _interface, std::vector<uint8_t>& _data, bool _isString) {
|
||||
TEST_INFO("Receive Datas : " << _data.size() << " bytes");
|
||||
if (_isString == true) {
|
||||
_data.resize(_data.size()+1);
|
||||
_data[_data.size()-1] = '\0';
|
||||
TEST_INFO("string data: '" << (char*)&_data[0] << "'");
|
||||
_interface->write("Très bien, merci ...");
|
||||
} else {
|
||||
TEST_INFO("binary data: ... ");
|
||||
}
|
||||
}
|
||||
bool onReceiveUri(enet::WebSocket* _interface, const std::string& _uri) {
|
||||
TEST_INFO("Receive Header uri: " << _uri);
|
||||
if (_uri == "plop.txt") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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 WebSocket 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::WebSocket connection(std::move(tcpConnection), true);
|
||||
enet::WebSocket* tmp = &connection;
|
||||
// Set callbacks:
|
||||
connection.connect([=](std::vector<uint8_t>& _value, bool _isString){
|
||||
appl::onReceiveData(tmp, _value, _isString);
|
||||
});
|
||||
connection.connectUri([=](const std::string& _value){
|
||||
return appl::onReceiveUri(tmp, _value);
|
||||
});
|
||||
|
||||
// start http connection (the actual state is just TCP start ...)
|
||||
connection.start();
|
||||
|
||||
while (connection.isAlive() == true) {
|
||||
usleep(100000);
|
||||
}
|
||||
#else
|
||||
TEST_CRITICAL("not implemented");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user