mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-03 16:56:48 +00:00
more unfinished header writing work
This commit is contained in:
@@ -99,53 +99,52 @@ namespace websocketpp {
|
||||
}
|
||||
|
||||
// TODO functions for application ranges?
|
||||
}
|
||||
}
|
||||
} // namespace status
|
||||
} // namespace close
|
||||
|
||||
namespace frame {
|
||||
// Opcodes are 4 bits
|
||||
// See spec section 5.2
|
||||
namespace opcode {
|
||||
enum value {
|
||||
CONTINUATION = 0x0,
|
||||
TEXT = 0x1,
|
||||
BINARY = 0x2,
|
||||
RSV3 = 0x3,
|
||||
RSV4 = 0x4,
|
||||
RSV5 = 0x5,
|
||||
RSV6 = 0x6,
|
||||
RSV7 = 0x7,
|
||||
CLOSE = 0x8,
|
||||
PING = 0x9,
|
||||
PONG = 0xA,
|
||||
CONTROL_RSVB = 0xB,
|
||||
CONTROL_RSVC = 0xC,
|
||||
CONTROL_RSVD = 0xD,
|
||||
CONTROL_RSVE = 0xE,
|
||||
CONTROL_RSVF = 0xF,
|
||||
};
|
||||
|
||||
inline bool reserved(value v) {
|
||||
return (v >= RSV3 && v <= RSV7) ||
|
||||
(v >= CONTROL_RSVB && v <= CONTROL_RSVF);
|
||||
// Opcodes are 4 bits
|
||||
// See spec section 5.2
|
||||
namespace opcode {
|
||||
enum value {
|
||||
CONTINUATION = 0x0,
|
||||
TEXT = 0x1,
|
||||
BINARY = 0x2,
|
||||
RSV3 = 0x3,
|
||||
RSV4 = 0x4,
|
||||
RSV5 = 0x5,
|
||||
RSV6 = 0x6,
|
||||
RSV7 = 0x7,
|
||||
CLOSE = 0x8,
|
||||
PING = 0x9,
|
||||
PONG = 0xA,
|
||||
CONTROL_RSVB = 0xB,
|
||||
CONTROL_RSVC = 0xC,
|
||||
CONTROL_RSVD = 0xD,
|
||||
CONTROL_RSVE = 0xE,
|
||||
CONTROL_RSVF = 0xF,
|
||||
};
|
||||
|
||||
inline bool reserved(value v) {
|
||||
return (v >= RSV3 && v <= RSV7) ||
|
||||
(v >= CONTROL_RSVB && v <= CONTROL_RSVF);
|
||||
}
|
||||
|
||||
inline bool invalid(value v) {
|
||||
return (v > 0xF || v < 0);
|
||||
}
|
||||
|
||||
inline bool is_control(value v) {
|
||||
return v >= 0x8;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool invalid(value v) {
|
||||
return (v > 0xF || v < 0);
|
||||
namespace limits {
|
||||
static const uint8_t PAYLOAD_SIZE_BASIC = 125;
|
||||
static const uint16_t PAYLOAD_SIZE_EXTENDED = 0xFFFF; // 2^16, 65535
|
||||
static const uint64_t PAYLOAD_SIZE_JUMBO = 0x7FFFFFFFFFFFFFFF;//2^63
|
||||
}
|
||||
|
||||
inline bool is_control(value v) {
|
||||
return v >= 0x8;
|
||||
}
|
||||
}
|
||||
|
||||
namespace limits {
|
||||
static const uint8_t PAYLOAD_SIZE_BASIC = 125;
|
||||
static const uint16_t PAYLOAD_SIZE_EXTENDED = 0xFFFF; // 2^16, 65535
|
||||
static const uint64_t PAYLOAD_SIZE_JUMBO = 0x7FFFFFFFFFFFFFFF;//2^63
|
||||
}
|
||||
} // namespace frame
|
||||
|
||||
}
|
||||
|
||||
#endif // WEBSOCKET_CONSTANTS_HPP
|
||||
|
||||
@@ -140,3 +140,6 @@ void data::process() {
|
||||
}
|
||||
}
|
||||
|
||||
void data::set_header(const std::string& header) {
|
||||
m_header = header;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,8 @@ public:
|
||||
// immediately and throws processor::exception if it fails
|
||||
void set_payload(const std::string& payload);
|
||||
|
||||
void set_header(const std::string& header);
|
||||
|
||||
// Performs masking and header generation if it has not been done already.
|
||||
void process();
|
||||
|
||||
|
||||
@@ -104,7 +104,10 @@ private:
|
||||
template <class connection_type>
|
||||
class hybi : public processor_base {
|
||||
public:
|
||||
hybi(connection_type &connection) : m_connection(connection),m_write_frame(connection) {
|
||||
hybi(connection_type &connection)
|
||||
: m_connection(connection),
|
||||
m_write_frame(connection)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
@@ -276,7 +279,8 @@ public:
|
||||
m_control_message = m_connection.get_control_message();
|
||||
|
||||
if (!m_control_message) {
|
||||
throw processor::exception("Out of control messages",processor::error::OUT_OF_MESSAGES);
|
||||
throw processor::exception("Out of control messages",
|
||||
processor::error::OUT_OF_MESSAGES);
|
||||
}
|
||||
|
||||
m_control_message->reset(m_header.get_opcode(),m_header.get_masking_key());
|
||||
@@ -300,7 +304,8 @@ public:
|
||||
m_data_message = m_connection.get_data_message();
|
||||
|
||||
if (!m_data_message) {
|
||||
throw processor::exception("Out of data messages",processor::error::OUT_OF_MESSAGES);
|
||||
throw processor::exception("Out of data messages",
|
||||
processor::error::OUT_OF_MESSAGES);
|
||||
}
|
||||
|
||||
m_data_message->reset(m_header.get_opcode());
|
||||
@@ -488,6 +493,19 @@ public:
|
||||
return response;
|
||||
}
|
||||
|
||||
// new prepare frame stuff
|
||||
void prepare_frame(message::data_ptr msg, bool masked, int32_t mask) {
|
||||
m_write_header.reset();
|
||||
m_write_header.set_fin(true);
|
||||
m_write_header.set_opcode(msg->get_opcode());
|
||||
m_write_header.set_masked(masked,mask);
|
||||
m_write_header.set_payload_size(msg->get_payload().size());
|
||||
m_write_header.complete();
|
||||
|
||||
msg->set_header(m_write_header.get_header_bytes());
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
connection_type& m_connection;
|
||||
int m_state;
|
||||
@@ -495,8 +513,10 @@ private:
|
||||
message::data_ptr m_data_message;
|
||||
message::control_ptr m_control_message;
|
||||
hybi_header m_header;
|
||||
hybi_header m_write_header;
|
||||
uint64_t m_payload_left;
|
||||
|
||||
|
||||
frame::parser<connection_type> m_write_frame; // TODO: refactor this out
|
||||
};
|
||||
|
||||
|
||||
@@ -29,53 +29,23 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using websocketpp::processor::hybi_header_writer;
|
||||
|
||||
hybi_header_writer::hybi_header_writer() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void hybi_header_writer::reset() {
|
||||
memset(m_header, 0x00, MAX_HEADER_LENGTH);
|
||||
}
|
||||
|
||||
std::string hybi_header_writer::get_header_bytes() const {
|
||||
return std::string(m_header);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
using websocketpp::processor::hybi_header;
|
||||
|
||||
hybi_header::hybi_header(bool reading) {
|
||||
reset(reading);
|
||||
hybi_header::hybi_header() {
|
||||
reset();
|
||||
}
|
||||
|
||||
uint64_t hybi_header::get_bytes_needed() const {
|
||||
return m_bytes_needed;
|
||||
}
|
||||
|
||||
void hybi_header::reset(bool reading) {
|
||||
if (reading) {
|
||||
m_state = STATE_BASIC_HEADER;
|
||||
m_bytes_needed = BASIC_HEADER_LENGTH;
|
||||
} else {
|
||||
m_state = STATE_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
bool hybi_header::ready() const {
|
||||
return m_state == STATE_READY;
|
||||
void hybi_header::reset() {
|
||||
memset(m_header, 0x00, MAX_HEADER_LENGTH);
|
||||
m_state = STATE_BASIC_HEADER;
|
||||
m_bytes_needed = BASIC_HEADER_LENGTH;
|
||||
}
|
||||
|
||||
// Writing interface (parse a byte stream)
|
||||
void hybi_header::consume(std::istream& input) {
|
||||
switch (m_state) {
|
||||
case STATE_BASIC_HEADER:
|
||||
input.read(&m_header[BASIC_HEADER_LENGTH-m_bytes_needed],m_bytes_needed);
|
||||
input.read(&m_header[BASIC_HEADER_LENGTH-m_bytes_needed],
|
||||
m_bytes_needed);
|
||||
|
||||
m_bytes_needed -= input.gcount();
|
||||
|
||||
@@ -93,7 +63,8 @@ void hybi_header::consume(std::istream& input) {
|
||||
}
|
||||
break;
|
||||
case STATE_EXTENDED_HEADER:
|
||||
input.read(&m_header[get_header_len()-m_bytes_needed],m_bytes_needed);
|
||||
input.read(&m_header[get_header_len()-m_bytes_needed],
|
||||
m_bytes_needed);
|
||||
|
||||
m_bytes_needed -= input.gcount();
|
||||
|
||||
@@ -106,7 +77,113 @@ void hybi_header::consume(std::istream& input) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint64_t hybi_header::get_bytes_needed() const {
|
||||
return m_bytes_needed;
|
||||
}
|
||||
bool hybi_header::ready() const {
|
||||
return m_state == STATE_READY;
|
||||
}
|
||||
|
||||
// Writing interface (set fields directly)
|
||||
void hybi_header::set_fin(bool fin) {
|
||||
set_header_bit(BPB0_FIN,0,fin);
|
||||
}
|
||||
void hybi_header::set_rsv1(bool b) {
|
||||
set_header_bit(BPB0_RSV1,0,b);
|
||||
}
|
||||
void hybi_header::set_rsv2(bool b) {
|
||||
set_header_bit(BPB0_RSV2,0,b);
|
||||
}
|
||||
void hybi_header::set_rsv3(bool b) {
|
||||
set_header_bit(BPB0_RSV3,0,b);
|
||||
}
|
||||
void hybi_header::set_opcode(frame::opcode::value op) {
|
||||
m_header[0] &= (0xFF ^ BPB0_OPCODE); // clear op bits
|
||||
m_header[0] |= op; // set op bits
|
||||
}
|
||||
void hybi_header::set_masked(bool masked,int32_t key) {
|
||||
if (masked) {
|
||||
m_header[1] |= BPB1_MASK;
|
||||
set_masking_key(key);
|
||||
} else {
|
||||
m_header[1] &= (0xFF ^ BPB1_MASK);
|
||||
clear_masking_key();
|
||||
}
|
||||
}
|
||||
void hybi_header::set_payload_size(uint64_t size) {
|
||||
if (size <= frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
m_header[1] &= (size & BPB1_PAYLOAD);
|
||||
m_payload_size = size;
|
||||
} else if (size <= frame::limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
if (get_masked()) {
|
||||
// shift mask bytes to the correct position given the new size
|
||||
unsigned int mask_offset = get_header_len()-4;
|
||||
m_header[1] &= (BASIC_PAYLOAD_16BIT_CODE & BPB1_PAYLOAD);
|
||||
memcpy(&m_header[get_header_len()-4], &m_header[mask_offset], 4);
|
||||
} else {
|
||||
m_header[1] &= (BASIC_PAYLOAD_16BIT_CODE & BPB1_PAYLOAD);
|
||||
}
|
||||
m_payload_size = size;
|
||||
*(reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])) = htons(size);
|
||||
} else if (size <= frame::limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
if (get_masked()) {
|
||||
// shift mask bytes to the correct position given the new size
|
||||
unsigned int mask_offset = get_header_len()-4;
|
||||
m_header[1] &= (BASIC_PAYLOAD_64BIT_CODE & BPB1_PAYLOAD);
|
||||
memcpy(&m_header[get_header_len()-4], &m_header[mask_offset], 4);
|
||||
} else {
|
||||
m_header[1] &= (BASIC_PAYLOAD_64BIT_CODE & BPB1_PAYLOAD);
|
||||
}
|
||||
m_payload_size = size;
|
||||
*(reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])) = htonll(size);
|
||||
} else {
|
||||
throw processor::exception("set_payload_size called with value that was too large (>2^63)",processor::error::MESSAGE_TOO_BIG);
|
||||
}
|
||||
}
|
||||
void hybi_header::complete() {
|
||||
validate_basic_header();
|
||||
m_state = STATE_READY;
|
||||
}
|
||||
|
||||
// Reading interface (get string of bytes)
|
||||
std::string hybi_header::get_header_bytes() const {
|
||||
return std::string(m_header,get_header_len());
|
||||
}
|
||||
|
||||
// Reading interface (get fields directly)
|
||||
bool hybi_header::get_fin() const {
|
||||
return ((m_header[0] & BPB0_FIN) == BPB0_FIN);
|
||||
}
|
||||
bool hybi_header::get_rsv1() const {
|
||||
return ((m_header[0] & BPB0_RSV1) == BPB0_RSV1);
|
||||
}
|
||||
bool hybi_header::get_rsv2() const {
|
||||
return ((m_header[0] & BPB0_RSV2) == BPB0_RSV2);
|
||||
}
|
||||
bool hybi_header::get_rsv3() const {
|
||||
return ((m_header[0] & BPB0_RSV3) == BPB0_RSV3);
|
||||
}
|
||||
websocketpp::frame::opcode::value hybi_header::get_opcode() const {
|
||||
return frame::opcode::value(m_header[0] & BPB0_OPCODE);
|
||||
}
|
||||
bool hybi_header::get_masked() const {
|
||||
return ((m_header[1] & BPB1_MASK) == BPB1_MASK);
|
||||
}
|
||||
int32_t hybi_header::get_masking_key() const {
|
||||
if (!get_masked()) {
|
||||
return 0;
|
||||
}
|
||||
return *reinterpret_cast<const int32_t*>(&m_header[get_header_len()-4]);
|
||||
}
|
||||
uint64_t hybi_header::get_payload_size() const {
|
||||
return m_payload_size;
|
||||
}
|
||||
|
||||
bool hybi_header::is_control() const {
|
||||
return (frame::opcode::is_control(get_opcode()));
|
||||
}
|
||||
|
||||
// private
|
||||
unsigned int hybi_header::get_header_len() const {
|
||||
unsigned int temp = 2;
|
||||
|
||||
@@ -123,175 +200,9 @@ unsigned int hybi_header::get_header_len() const {
|
||||
return temp;
|
||||
}
|
||||
|
||||
int32_t hybi_header::get_masking_key() {
|
||||
if (m_state != STATE_READY) {
|
||||
throw processor::exception("attempted to get masking_key before reading full header");
|
||||
}
|
||||
if (!get_masked()) {
|
||||
// masking key of 0 and not masked are equivalent
|
||||
return 0;
|
||||
}
|
||||
|
||||
return *reinterpret_cast<int32_t*>(&m_header[get_header_len()-4]);
|
||||
}
|
||||
|
||||
// get and set header bits
|
||||
bool hybi_header::get_fin() const {
|
||||
return ((m_header[0] & BPB0_FIN) == BPB0_FIN);
|
||||
}
|
||||
void hybi_header::set_fin(bool fin) {
|
||||
if (fin) {
|
||||
m_header[0] |= BPB0_FIN;
|
||||
} else {
|
||||
m_header[0] &= (0xFF ^ BPB0_FIN);
|
||||
}
|
||||
}
|
||||
|
||||
bool hybi_header::get_rsv1() const {
|
||||
return ((m_header[0] & BPB0_RSV1) == BPB0_RSV1);
|
||||
}
|
||||
void hybi_header::set_rsv1(bool b) {
|
||||
if (b) {
|
||||
m_header[0] |= BPB0_RSV1;
|
||||
} else {
|
||||
m_header[0] &= (0xFF ^ BPB0_RSV1);
|
||||
}
|
||||
}
|
||||
|
||||
bool hybi_header::get_rsv2() const {
|
||||
return ((m_header[0] & BPB0_RSV2) == BPB0_RSV2);
|
||||
}
|
||||
void hybi_header::set_rsv2(bool b) {
|
||||
if (b) {
|
||||
m_header[0] |= BPB0_RSV2;
|
||||
} else {
|
||||
m_header[0] &= (0xFF ^ BPB0_RSV2);
|
||||
}
|
||||
}
|
||||
|
||||
bool hybi_header::get_rsv3() const {
|
||||
return ((m_header[0] & BPB0_RSV3) == BPB0_RSV3);
|
||||
}
|
||||
void hybi_header::set_rsv3(bool b) {
|
||||
if (b) {
|
||||
m_header[0] |= BPB0_RSV3;
|
||||
} else {
|
||||
m_header[0] &= (0xFF ^ BPB0_RSV3);
|
||||
}
|
||||
}
|
||||
|
||||
websocketpp::frame::opcode::value hybi_header::get_opcode() const {
|
||||
return frame::opcode::value(m_header[0] & BPB0_OPCODE);
|
||||
}
|
||||
void hybi_header::set_opcode(frame::opcode::value op) {
|
||||
if (frame::opcode::reserved(op)) {
|
||||
throw processor::exception("reserved opcode",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
if (frame::opcode::invalid(op)) {
|
||||
throw processor::exception("invalid opcode",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
if (is_control() && get_basic_size() > frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
throw processor::exception("control frames can't have large payloads",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
m_header[0] &= (0xFF ^ BPB0_OPCODE); // clear op bits
|
||||
m_header[0] |= op; // set op bits
|
||||
}
|
||||
|
||||
bool hybi_header::get_masked() const {
|
||||
return ((m_header[1] & BPB1_MASK) == BPB1_MASK);
|
||||
}
|
||||
void hybi_header::set_masked(bool masked,int32_t key) {
|
||||
if (masked) {
|
||||
m_header[1] |= BPB1_MASK;
|
||||
set_masking_key(key);
|
||||
} else {
|
||||
m_header[1] &= (0xFF ^ BPB1_MASK);
|
||||
clear_masking_key();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t hybi_header::get_basic_size() const {
|
||||
return m_header[1] & BPB1_PAYLOAD;
|
||||
}
|
||||
size_t hybi_header::get_payload_size() const {
|
||||
if (m_state != STATE_READY) {
|
||||
// TODO: how to handle errors like this?
|
||||
throw "attempted to get payload size before reading full header";
|
||||
}
|
||||
|
||||
return m_payload_size;
|
||||
}
|
||||
|
||||
void hybi_header::set_payload_size(size_t size) {
|
||||
if (size <= frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
// encode in byte 2
|
||||
m_header[1] &= (size & BPB1_PAYLOAD);
|
||||
} else if (size <= frame::limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
// encode two byte
|
||||
m_header[1] &= (BASIC_PAYLOAD_16BIT_CODE & BPB1_PAYLOAD);
|
||||
|
||||
if (get_masked()) {
|
||||
|
||||
}
|
||||
|
||||
*(reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])) = htons(size);
|
||||
|
||||
|
||||
|
||||
|
||||
} else if (size <= frame::limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
// encode two byte
|
||||
m_header[1] &= (BASIC_PAYLOAD_64BIT_CODE & BPB1_PAYLOAD);
|
||||
} else {
|
||||
throw processor::exception("Client attempted to send a message that was too big",processor::error::MESSAGE_TOO_BIG);
|
||||
}
|
||||
}
|
||||
|
||||
bool hybi_header::is_control() const {
|
||||
return (frame::opcode::is_control(get_opcode()));
|
||||
}
|
||||
|
||||
void hybi_header::process_basic_header() {
|
||||
m_bytes_needed = get_header_len() - BASIC_HEADER_LENGTH;
|
||||
}
|
||||
void hybi_header::process_extended_header() {
|
||||
uint8_t s = get_basic_size();
|
||||
|
||||
if (s <= frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
m_payload_size = s;
|
||||
} else if (s == BASIC_PAYLOAD_16BIT_CODE) {
|
||||
// reinterpret the second two bytes as a 16 bit integer in network
|
||||
// byte order. Convert to host byte order and store locally.
|
||||
m_payload_size = ntohs(*(
|
||||
reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (m_payload_size < s) {
|
||||
std::stringstream err;
|
||||
err << "payload length not minimally encoded. Using 16 bit form for payload size: " << m_payload_size;
|
||||
throw processor::exception(err.str(),processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
} else if (s == BASIC_PAYLOAD_64BIT_CODE) {
|
||||
// reinterpret the second eight bytes as a 64 bit integer in
|
||||
// network byte order. Convert to host byte order and store.
|
||||
m_payload_size = ntohll(*(
|
||||
reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (m_payload_size <= frame::limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
throw processor::exception("payload length not minimally encoded",
|
||||
processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO: shouldn't be here how to handle?
|
||||
throw processor::exception("invalid get_basic_size in process_extended_header");
|
||||
}
|
||||
}
|
||||
|
||||
void hybi_header::validate_basic_header() const {
|
||||
// check for control frame size
|
||||
@@ -308,6 +219,11 @@ void hybi_header::validate_basic_header() const {
|
||||
if (frame::opcode::reserved(get_opcode())) {
|
||||
throw processor::exception("Reserved opcode used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for invalid opcodes
|
||||
if (frame::opcode::invalid(get_opcode())) {
|
||||
throw processor::exception("Invalid opcode used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for fragmented control message
|
||||
if (is_control() && !get_fin()) {
|
||||
@@ -315,6 +231,53 @@ void hybi_header::validate_basic_header() const {
|
||||
}
|
||||
}
|
||||
|
||||
void hybi_header::process_basic_header() {
|
||||
m_bytes_needed = get_header_len() - BASIC_HEADER_LENGTH;
|
||||
}
|
||||
void hybi_header::process_extended_header() {
|
||||
uint8_t s = get_basic_size();
|
||||
|
||||
if (s <= frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
m_payload_size = s;
|
||||
} else if (s == BASIC_PAYLOAD_16BIT_CODE) {
|
||||
// reinterpret the second two bytes as a 16 bit integer in network
|
||||
// byte order. Convert to host byte order and store locally.
|
||||
m_payload_size = ntohs(*(
|
||||
reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (m_payload_size < s) {
|
||||
std::stringstream err;
|
||||
err << "payload length not minimally encoded. Using 16 bit form for payload size: " << m_payload_size;
|
||||
throw processor::exception(err.str(),processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
} else if (s == BASIC_PAYLOAD_64BIT_CODE) {
|
||||
// reinterpret the second eight bytes as a 64 bit integer in
|
||||
// network byte order. Convert to host byte order and store.
|
||||
m_payload_size = ntohll(*(
|
||||
reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (m_payload_size <= frame::limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
throw processor::exception("payload length not minimally encoded",
|
||||
processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO: shouldn't be here how to handle?
|
||||
throw processor::exception("invalid get_basic_size in process_extended_header");
|
||||
}
|
||||
}
|
||||
|
||||
void hybi_header::set_header_bit(uint8_t bit,int byte,bool value) {
|
||||
if (value) {
|
||||
m_header[byte] |= bit;
|
||||
} else {
|
||||
m_header[byte] &= (0xFF ^ bit);
|
||||
}
|
||||
}
|
||||
|
||||
void hybi_header::set_masking_key(int32_t key) {
|
||||
*(reinterpret_cast<int32_t *>(&m_header[get_header_len()-4])) = key;
|
||||
}
|
||||
|
||||
@@ -29,108 +29,92 @@
|
||||
#define WEBSOCKET_PROCESSOR_HYBI_HEADER_HPP
|
||||
|
||||
#include "processor.hpp"
|
||||
#include "../websocket_frame.hpp" // TODO: remove this dependency
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
class hybi_header_writer {
|
||||
/// Describes a processor for reading and writing WebSocket frame headers
|
||||
/**
|
||||
* The hybi_header class provides a processor capable of reading and writing
|
||||
* WebSocket frame headers. It has two writing modes and two reading modes.
|
||||
*
|
||||
* Writing method 1: call consume() until ready()
|
||||
* Writing method 2: call set_* methods followed by complete()
|
||||
*
|
||||
* Writing methods are valid only when ready() returns false. Use reset() to
|
||||
* reset the header for writing again. Mixing writing methods between calls to
|
||||
* reset() may behave unpredictably.
|
||||
*
|
||||
* Reading method 1: call get_header_bytes() to return a string of bytes
|
||||
* Reading method 2: call get_* methods to read individual values
|
||||
*
|
||||
* Reading methods are valid only when ready() is true.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe
|
||||
*/
|
||||
class hybi_header {
|
||||
public:
|
||||
hybi_header_writer();
|
||||
void reset();
|
||||
/// Construct a header processor and initialize for writing
|
||||
hybi_header();
|
||||
/// Reset a header processor for writing
|
||||
void reset();
|
||||
|
||||
// Writing interface (parse a byte stream)
|
||||
// valid only if ready() returns false
|
||||
// Consume will throw a processor::exception in the case that the bytes it
|
||||
// read do not form a valid WebSocket frame header.
|
||||
void consume(std::istream& input);
|
||||
uint64_t get_bytes_needed() const;
|
||||
bool ready() const;
|
||||
|
||||
// Writing interface (set fields directly)
|
||||
// valid only if ready() returns false
|
||||
// set_* may allow invalid values. Call complete() once values are set to
|
||||
// check for header validity.
|
||||
void set_fin(bool fin);
|
||||
void set_rsv1(bool b);
|
||||
void set_rsv2(bool b);
|
||||
void set_rsv3(bool b);
|
||||
void set_opcode(frame::opcode::value op);
|
||||
void set_masked(bool masked,int32_t key);
|
||||
void set_payload_size(size_t size);
|
||||
void set_payload_size(uint64_t size);
|
||||
// Complete will throw a processor::exception in the case that the
|
||||
// combination of values set do not form a valid WebSocket frame header.
|
||||
void complete();
|
||||
|
||||
// Reading interface (get string of bytes)
|
||||
// valid only if ready() returns true
|
||||
std::string get_header_bytes() const;
|
||||
private:
|
||||
// basic payload byte flags
|
||||
static const uint8_t BPB0_OPCODE = 0x0F;
|
||||
static const uint8_t BPB0_RSV3 = 0x10;
|
||||
static const uint8_t BPB0_RSV2 = 0x20;
|
||||
static const uint8_t BPB0_RSV1 = 0x40;
|
||||
static const uint8_t BPB0_FIN = 0x80;
|
||||
static const uint8_t BPB1_PAYLOAD = 0x7F;
|
||||
static const uint8_t BPB1_MASK = 0x80;
|
||||
|
||||
static const uint8_t BASIC_PAYLOAD_16BIT_CODE = 0x7E; // 126
|
||||
static const uint8_t BASIC_PAYLOAD_64BIT_CODE = 0x7F; // 127
|
||||
|
||||
static const unsigned int BASIC_HEADER_LENGTH = 2;
|
||||
static const unsigned int MAX_HEADER_LENGTH = 15;
|
||||
|
||||
char m_header[MAX_HEADER_LENGTH];
|
||||
};
|
||||
|
||||
|
||||
// hybi header can be used for the following two tasks:
|
||||
// - parse a sequence of bytes into a complete header and read fields
|
||||
// - set fields and then generate a sequence of header bytes
|
||||
|
||||
class hybi_header {
|
||||
public:
|
||||
// constructs/resets a hybi_header for the purpose of either reading or writing
|
||||
hybi_header(bool reading = true);
|
||||
void reset(bool reading = true);
|
||||
|
||||
// Reading interface (Parse a byte stream)
|
||||
void consume(std::istream& input);
|
||||
uint64_t get_bytes_needed() const;
|
||||
bool ready() const;
|
||||
|
||||
// Reading interface (get fields directly)
|
||||
// valid only if ready() returns true
|
||||
bool get_fin() const;
|
||||
bool get_rsv1() const;
|
||||
bool get_rsv2() const;
|
||||
bool get_rsv3() const;
|
||||
frame::opcode::value get_opcode() const;
|
||||
bool get_masked() const;
|
||||
// will return zero in the case where get_masked() is false. Note:
|
||||
// a masking key of zero is slightly different than no mask at all.
|
||||
int32_t get_masking_key() const;
|
||||
uint64_t get_payload_size() const;
|
||||
|
||||
// Writing interface (Set fields directly)
|
||||
void set_fin(bool fin);
|
||||
void set_rsv1(bool b);
|
||||
void set_rsv2(bool b);
|
||||
void set_rsv3(bool b);
|
||||
void set_opcode(frame::opcode::value op);
|
||||
void set_masked(bool masked,int32_t key);
|
||||
void set_payload_size(size_t size);
|
||||
|
||||
std::string get_header_bytes() const;
|
||||
|
||||
|
||||
unsigned int get_header_len() const;
|
||||
|
||||
int32_t get_masking_key();
|
||||
|
||||
// get and set header bits
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
uint8_t get_basic_size() const;
|
||||
size_t get_payload_size() const;
|
||||
|
||||
|
||||
|
||||
bool is_control() const;
|
||||
|
||||
void process_basic_header();
|
||||
void process_extended_header();
|
||||
|
||||
void validate_basic_header() const;
|
||||
|
||||
void set_masking_key(int32_t key);
|
||||
void clear_masking_key();
|
||||
|
||||
bool is_control() const;
|
||||
private:
|
||||
// general helper functions
|
||||
unsigned int get_header_len() const;
|
||||
uint8_t get_basic_size() const;
|
||||
void validate_basic_header() const;
|
||||
|
||||
// helper functions for writing
|
||||
void process_basic_header();
|
||||
void process_extended_header();
|
||||
void set_header_bit(uint8_t bit,int byte,bool value);
|
||||
void set_masking_key(int32_t key);
|
||||
void clear_masking_key();
|
||||
|
||||
// basic payload byte flags
|
||||
static const uint8_t BPB0_OPCODE = 0x0F;
|
||||
static const uint8_t BPB0_RSV3 = 0x10;
|
||||
|
||||
Reference in New Issue
Block a user