#include "WebSocket.h"
#include "../../utils/Utils.h"
#include "../../socket/Socket.h"
#ifdef POSIX
#include
#endif
namespace HttpClient
{
WebSocket::WebSocket(const WebSocket &obj) noexcept : sock(obj.sock) {}
WebSocket::WebSocket(Socket::Adapter *adapter) noexcept : sock(adapter) {}
std::vector WebSocket::packDataToMessageFrame(const void *data, const size_t size)
{
std::vector frame;
if (0 == size) {
return frame;
}
constexpr size_t header_max_size = 14;
frame.reserve(size + header_max_size);
frame.resize(header_max_size);
frame[0] = 0x81;
size_t cur_pos = sizeof(uint8_t) * 2;
if (size <= 125) {
frame[1] = size;
}
else if (size <= 65536) {
frame[1] = 126;
*reinterpret_cast(&frame[2]) = htons(size);
cur_pos += sizeof(uint16_t);
} else { // More
frame[1] = 127;
*reinterpret_cast(&frame[2]) = Utils::hton64(size);
cur_pos += sizeof(uint64_t);
}
frame.erase(frame.cbegin() + cur_pos, frame.cend() );
frame.insert(frame.cend(), reinterpret_cast(data), reinterpret_cast(data) + size);
return frame;
}
Socket::Adapter *WebSocket::getSocket() noexcept {
return this->sock;
}
const Socket::Adapter *WebSocket::getSocket() const noexcept {
return this->sock;
}
long WebSocket::nonblock_recv(std::vector &frame, const std::chrono::milliseconds &timeout) const
{
std::vector buf(4096);
const long recv_size = this->sock->nonblock_recv(buf, timeout);
if (recv_size <= 0) {
return recv_size;
}
// See @link: http://tools.ietf.org/html/rfc6455#page-28
const uint8_t info_frame = buf[0];
if ( (info_frame & 0x08) == 0x08) { // opcode 0x08 — close connection
return -1;
}
uint8_t info_size = buf[1];
const bool is_mask_set = info_size & uint8_t(1 << 7);
info_size &= ~uint8_t(1 << 7); // Unset first bit
size_t cur_pos = sizeof(uint8_t) * 2;
uint64_t frame_size;
if (info_size <= 125) {
frame_size = info_size;
}
else if (info_size == 126) {
frame_size = ntohs(*reinterpret_cast(&buf[cur_pos]) );
cur_pos += sizeof(uint16_t);
} else { // if (info_size == 127)
frame_size = Utils::ntoh64(*reinterpret_cast(&buf[cur_pos]) );
cur_pos += sizeof(uint64_t);
}
if (frame_size > (buf.size() - cur_pos) ) {
return -1; // Close connection
}
uint32_t mask;
if (is_mask_set) {
mask = *reinterpret_cast(&buf[cur_pos]);
cur_pos += sizeof(uint32_t);
}
const uint8_t align = (recv_size - cur_pos) % sizeof(uint32_t);
frame.reserve(recv_size - cur_pos + align);
frame.assign(buf.cbegin() + cur_pos, buf.cbegin() + recv_size);
if (is_mask_set) {
if (align) {
frame.insert(frame.cend(), align, 0);
}
uint32_t *addr = reinterpret_cast(frame.data() );
for (size_t i = 0; i < frame.size() / sizeof(uint32_t); ++i) {
addr[i] ^= mask;
}
if (align) {
frame.erase(frame.cend() - align, frame.cend() );
}
}
return recv_size - cur_pos;
}
long WebSocket::nonblock_send(const void *data, const size_t length, const std::chrono::milliseconds &timeout) const
{
const std::vector frame = WebSocket::packDataToMessageFrame(data, length);
if (frame.empty() ) {
return 0;
}
return this->sock->nonblock_send(frame.data(), frame.size(), timeout);
}
long WebSocket::nonblock_send(const std::string &str, const std::chrono::milliseconds &timeout) const
{
const std::vector frame = WebSocket::packDataToMessageFrame(str.data(), str.length() );
if (frame.empty() ) {
return 0;
}
return this->sock->nonblock_send(frame.data(), frame.size(), timeout);
}
void WebSocket::close() noexcept {
this->sock->close();
}
}