fixes close status handling

This commit is contained in:
Peter Thorson
2011-10-27 09:42:11 -05:00
parent af2fa69822
commit ccb34c98b4
6 changed files with 91 additions and 30 deletions

View File

@@ -53,12 +53,12 @@ int main(int argc, char* argv[]) {
websocketpp::client_ptr client(new websocketpp::client(io_service,c));
client->init();
client->set_header("User Agent","WebSocket++/2011-09-25");
client->set_header("User Agent","WebSocket++/2011-10-27");
client->connect("ws://localhost:9001/getCaseCount");
io_service.run();
std::cout << "case count: " << c->m_case_count;
std::cout << "case count: " << c->m_case_count << std::endl;
for (int i = 1; i <= c->m_case_count; i++) {
io_service.reset();
@@ -74,12 +74,12 @@ int main(int argc, char* argv[]) {
client->set_elog_level(websocketpp::LOG_OFF);
client->init();
client->set_header("User Agent","WebSocket++/2011-09-25");
client->set_header("User Agent","WebSocket++/2011-10-27");
std::stringstream foo;
foo << "ws://localhost:9001/runCase?case=" << i << "&agent=\"WebSocket++Snapshot/2011-10-08\"";
foo << "ws://localhost:9001/runCase?case=" << i << "&agent=\"WebSocket++Snapshot/2011-10-27\"";
client->connect(foo.str());
io_service.run();

View File

@@ -285,7 +285,9 @@ size_t frame::get_payload_size() const {
}
uint16_t frame::get_close_status() const {
if (get_payload_size() >= 2) {
if (get_payload_size() == 0) {
return close::status::NO_STATUS;
} else if (get_payload_size() >= 2) {
char val[2];
val[0] = m_payload[0];
@@ -295,14 +297,25 @@ uint16_t frame::get_close_status() const {
reinterpret_cast<uint16_t*>(&val[0])
));
return code;
if (close::status::invalid(code)) {
return close::status::PROTOCOL_ERROR;
} else {
return code;
}
} else {
return 1005; // defined in spec as "no status recieved"
return close::status::PROTOCOL_ERROR;
}
}
std::string frame::get_close_msg() const {
if (get_payload_size() > 2) {
uint32_t state = utf8_validator::UTF8_ACCEPT;
uint32_t codep = 0;
validate_utf8(&state,&codep,2);
if (state != utf8_validator::UTF8_ACCEPT) {
throw frame_error("Invalid UTF-8 Data",
frame::FERR_PAYLOAD_VIOLATION);
}
return std::string(m_payload.begin()+2,m_payload.end());
} else {
return std::string();
@@ -360,12 +373,16 @@ void frame::set_payload_helper(size_t s) {
void frame::set_status(uint16_t status,const std::string message) {
// check for valid statuses
if (status < 1000 || status > 4999) {
throw frame_error("Status codes must be in the range 1000-4999");
if (close::status::invalid(status)) {
std::stringstream err;
err << "Status code " << status << " is invalid";
throw frame_error(err.str());
}
if (status == 1005 || status == 1006) {
throw frame_error("Status codes 1005 and 1006 are reserved for internal use and cannot be written to a frame.");
if (close::status::reserved(status)) {
std::stringstream err;
err << "Status code " << status << " is reserved";
throw frame_error(err.str());
}
m_payload.resize(2+message.size());
@@ -506,8 +523,8 @@ void frame::process_payload2() {
}
}
void frame::validate_utf8(uint32_t* state,uint32_t* codep) const {
for (size_t i = 0; i < m_payload.size(); i++) {
void frame::validate_utf8(uint32_t* state,uint32_t* codep, size_t offset) const {
for (size_t i = offset; i < m_payload.size(); i++) {
using utf8_validator::decode;
if (decode(state,codep,m_payload[i]) == utf8_validator::UTF8_REJECT) {

View File

@@ -130,7 +130,7 @@ public:
uint16_t get_close_status() const;
std::string get_close_msg() const;
std::vector<unsigned char> &get_payload();
void set_payload(const std::vector<unsigned char> source);
@@ -149,7 +149,7 @@ public:
void process_payload();
void process_payload2(); // experiment with more efficient masking code.
void validate_utf8(uint32_t* state,uint32_t* codep) const;
void validate_utf8(uint32_t* state,uint32_t* codep,size_t offset = 0) const;
void validate_basic_header() const;
void generate_masking_key();

View File

@@ -183,15 +183,20 @@ void session::send_close(uint16_t status,const std::string &message) {
m_write_frame.set_fin(true);
m_write_frame.set_opcode(frame::CONNECTION_CLOSE);
if (status == CLOSE_STATUS_NO_STATUS) {
m_write_frame.set_status(CLOSE_STATUS_NORMAL,"");
} else if (status == CLOSE_STATUS_ABNORMAL_CLOSE) {
// Internal implimentation error. There is no good close code for this.
m_write_frame.set_status(CLOSE_STATUS_POLICY_VIOLATION,message);
} else {
m_write_frame.set_status(status,message);
// echo close value unless there is a good reason not to.
try {
if (status == CLOSE_STATUS_NO_STATUS) {
m_write_frame.set_status(CLOSE_STATUS_NORMAL,"");
} else if (status == CLOSE_STATUS_ABNORMAL_CLOSE) {
// Internal implimentation error. There is no good close code for this.
m_write_frame.set_status(CLOSE_STATUS_POLICY_VIOLATION,message);
} else {
m_write_frame.set_status(status,message);
}
} catch (const frame_error& e) {
m_write_frame.set_status(close::status::PROTOCOL_ERROR,e.what());
}
write_frame();
}
@@ -544,18 +549,21 @@ void session::process_continuation() {
}
void session::process_close() {
uint16_t status = m_read_frame.get_close_status();
std::string message = m_read_frame.get_close_msg();
m_remote_close_code = status;
m_remote_close_msg = message;
m_remote_close_code = m_read_frame.get_close_status();
m_remote_close_msg = m_read_frame.get_close_msg();
if (m_state == STATE_OPEN) {
log("process_close sending ack",LOG_DEBUG);
// This is the case where the remote initiated the close.
m_closed_by_me = false;
// send acknowledgement
send_close(status,message);
// check if the remote close code
if (m_remote_close_code >= close::status::RSV_START) {
}
send_close(m_remote_close_code,m_remote_close_msg);
} else if (m_state == STATE_CLOSING) {
log("process_close got ack",LOG_DEBUG);
// this is an ack of our close message

View File

@@ -135,6 +135,7 @@ public:
static const uint16_t CLOSE_STATUS_POLICY_VIOLATION = 1008;
static const uint16_t CLOSE_STATUS_MESSAGE_TOO_BIG = 1009;
static const uint16_t CLOSE_STATUS_EXTENSION_REQUIRE = 1010;
static const uint16_t CLOSE_STATUS_MAXIMUM = 1011;
session (boost::asio::io_service& io_service,
connection_handler_ptr defc,

View File

@@ -59,7 +59,42 @@ namespace websocketpp {
static const uint16_t ALOG_CONTROL = ALOG_CONNECT
& ALOG_DISCONNECT
& ALOG_MISC_CONTROL;
static const uint16_t ALOG_ALL = 0xFFFF;
static const uint16_t ALOG_ALL = 0xFFFF;
namespace close {
namespace status {
enum value {
INVALID_END = 999,
NORMAL = 1000,
GOING_AWAY = 1001,
PROTOCOL_ERROR = 1002,
UNSUPPORTED_DATA = 1003,
RSV_ADHOC_1 = 1004,
NO_STATUS = 1005,
ABNORMAL_CLOSE = 1006,
INVALID_PAYLOAD = 1007,
POLICY_VIOLATION = 1008,
MESSAGE_TOO_BIG = 1009,
EXTENSION_REQUIRE = 1010,
RSV_START = 1011,
RSV_END = 2999,
INVALID_START = 5000
};
inline bool reserved(uint16_t s) {
return ((s >= RSV_START && s <= RSV_END) ||
s == RSV_ADHOC_1);
}
inline bool invalid(uint16_t s) {
return ((s <= INVALID_END || s >= INVALID_START) ||
s == NO_STATUS ||
s == ABNORMAL_CLOSE);
}
}
}
}
#include "websocket_session.hpp"