|
| 1 | + |
| 2 | +#include "WebSocket.h" |
| 3 | + |
| 4 | +#include "../../utils/Utils.h" |
| 5 | +#include "../../socket/Socket.h" |
| 6 | + |
| 7 | +#ifdef POSIX |
| 8 | + #include <arpa/inet.h> |
| 9 | +#endif |
| 10 | + |
| 11 | +namespace HttpClient |
| 12 | +{ |
| 13 | + WebSocket::WebSocket(const WebSocket &obj) noexcept : sock(obj.sock) |
| 14 | + { |
| 15 | + |
| 16 | + } |
| 17 | + |
| 18 | + WebSocket::WebSocket(Socket::Adapter *adapter) noexcept : sock(adapter) |
| 19 | + { |
| 20 | + |
| 21 | + } |
| 22 | + |
| 23 | + std::vector<char> WebSocket::packDataToMessageFrame(const void *data, const size_t size) |
| 24 | + { |
| 25 | + std::vector<char> frame; |
| 26 | + |
| 27 | + if (0 == size) |
| 28 | + { |
| 29 | + return frame; |
| 30 | + } |
| 31 | + |
| 32 | + constexpr size_t header_max_size = 14; |
| 33 | + frame.reserve(size + header_max_size); |
| 34 | + frame.resize(header_max_size); |
| 35 | + |
| 36 | + frame[0] = 0x81; |
| 37 | + |
| 38 | + size_t cur_pos = sizeof(uint8_t) * 2; |
| 39 | + |
| 40 | + if (size <= 125) |
| 41 | + { |
| 42 | + frame[1] = size; |
| 43 | + } |
| 44 | + else if (size <= 65536) |
| 45 | + { |
| 46 | + frame[1] = 126; |
| 47 | + |
| 48 | + *reinterpret_cast<uint16_t *>(&frame[2]) = htons(size); |
| 49 | + |
| 50 | + cur_pos += sizeof(uint16_t); |
| 51 | + } |
| 52 | + else // More |
| 53 | + { |
| 54 | + frame[1] = 127; |
| 55 | + |
| 56 | + *reinterpret_cast<uint64_t *>(&frame[2]) = Utils::hton64(size); |
| 57 | + |
| 58 | + cur_pos += sizeof(uint64_t); |
| 59 | + } |
| 60 | + |
| 61 | + frame.erase(frame.cbegin() + cur_pos, frame.cend() ); |
| 62 | + |
| 63 | + frame.insert(frame.cend(), reinterpret_cast<const char *>(data), reinterpret_cast<const char *>(data) + size); |
| 64 | + |
| 65 | + return frame; |
| 66 | + } |
| 67 | + |
| 68 | + Socket::Adapter *WebSocket::getSocket() noexcept |
| 69 | + { |
| 70 | + return this->sock; |
| 71 | + } |
| 72 | + |
| 73 | + const Socket::Adapter *WebSocket::getSocket() const noexcept |
| 74 | + { |
| 75 | + return this->sock; |
| 76 | + } |
| 77 | + |
| 78 | + long WebSocket::nonblock_recv(std::vector<char> &frame, const std::chrono::milliseconds &timeout) const |
| 79 | + { |
| 80 | + std::vector<char> buf(4096); |
| 81 | + |
| 82 | + const long recv_size = this->sock->nonblock_recv(buf, timeout); |
| 83 | + |
| 84 | + if (recv_size <= 0) |
| 85 | + { |
| 86 | + return recv_size; |
| 87 | + } |
| 88 | + |
| 89 | + // See @link: http://tools.ietf.org/html/rfc6455#page-28 |
| 90 | + |
| 91 | + const uint8_t info_frame = buf[0]; |
| 92 | + |
| 93 | + if ( (info_frame & 0x08) == 0x08) // opcode 0x08 — close connection |
| 94 | + { |
| 95 | + return -1; |
| 96 | + } |
| 97 | + |
| 98 | + uint8_t info_size = buf[1]; |
| 99 | + |
| 100 | + const bool is_mask_set = info_size & uint8_t(1 << 7); |
| 101 | + |
| 102 | + info_size &= ~uint8_t(1 << 7); // Unset first bit |
| 103 | + |
| 104 | + size_t cur_pos = sizeof(uint8_t) * 2; |
| 105 | + |
| 106 | + uint64_t frame_size; |
| 107 | + |
| 108 | + if (info_size <= 125) |
| 109 | + { |
| 110 | + frame_size = info_size; |
| 111 | + } |
| 112 | + else if (info_size == 126) |
| 113 | + { |
| 114 | + frame_size = ntohs(*reinterpret_cast<uint16_t *>(&buf[cur_pos]) ); |
| 115 | + |
| 116 | + cur_pos += sizeof(uint16_t); |
| 117 | + } |
| 118 | + else // if (info_size == 127) |
| 119 | + { |
| 120 | + frame_size = Utils::ntoh64(*reinterpret_cast<uint64_t *>(&buf[cur_pos]) ); |
| 121 | + |
| 122 | + cur_pos += sizeof(uint64_t); |
| 123 | + } |
| 124 | + |
| 125 | + if (frame_size > (buf.size() - cur_pos) ) |
| 126 | + { |
| 127 | + return -1; // Close connection |
| 128 | + } |
| 129 | + |
| 130 | + uint32_t mask; |
| 131 | + |
| 132 | + if (is_mask_set) |
| 133 | + { |
| 134 | + mask = *reinterpret_cast<uint32_t *>(&buf[cur_pos]); |
| 135 | + |
| 136 | + cur_pos += sizeof(uint32_t); |
| 137 | + } |
| 138 | + |
| 139 | + const uint8_t align = (recv_size - cur_pos) % sizeof(uint32_t); |
| 140 | + |
| 141 | + frame.reserve(recv_size - cur_pos + align); |
| 142 | + |
| 143 | + frame.assign(buf.cbegin() + cur_pos, buf.cbegin() + recv_size); |
| 144 | + |
| 145 | + if (is_mask_set) |
| 146 | + { |
| 147 | + if (align) |
| 148 | + { |
| 149 | + frame.insert(frame.cend(), align, 0); |
| 150 | + } |
| 151 | + |
| 152 | + uint32_t *addr = reinterpret_cast<uint32_t *>(frame.data() ); |
| 153 | + |
| 154 | + for (size_t i = 0; i < frame.size() / sizeof(uint32_t); ++i) |
| 155 | + { |
| 156 | + addr[i] ^= mask; |
| 157 | + } |
| 158 | + |
| 159 | + if (align) |
| 160 | + { |
| 161 | + frame.erase(frame.cend() - align, frame.cend() ); |
| 162 | + } |
| 163 | + } |
| 164 | + |
| 165 | + return recv_size - cur_pos; |
| 166 | + } |
| 167 | + |
| 168 | + long WebSocket::nonblock_send(const void *data, const size_t length, const std::chrono::milliseconds &timeout) const |
| 169 | + { |
| 170 | + const std::vector<char> frame = WebSocket::packDataToMessageFrame(data, length); |
| 171 | + |
| 172 | + if (frame.empty() ) |
| 173 | + { |
| 174 | + return 0; |
| 175 | + } |
| 176 | + |
| 177 | + return this->sock->nonblock_send(frame.data(), frame.size(), timeout); |
| 178 | + } |
| 179 | + |
| 180 | + long WebSocket::nonblock_send(const std::string &str, const std::chrono::milliseconds &timeout) const |
| 181 | + { |
| 182 | + const std::vector<char> frame = WebSocket::packDataToMessageFrame(str.data(), str.length() ); |
| 183 | + |
| 184 | + if (frame.empty() ) |
| 185 | + { |
| 186 | + return 0; |
| 187 | + } |
| 188 | + |
| 189 | + return this->sock->nonblock_send(frame.data(), frame.size(), timeout); |
| 190 | + } |
| 191 | + |
| 192 | + void WebSocket::close() noexcept |
| 193 | + { |
| 194 | + this->sock->close(); |
| 195 | + } |
| 196 | +}; |
0 commit comments