264 lines
8.6 KiB
C++
264 lines
8.6 KiB
C++
#include <string.h>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
#include <sys/epoll.h>
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <a8/a8.h>
|
|
#include <a8/openssl.h>
|
|
#include <a8/websocketsession.h>
|
|
|
|
const unsigned char FIN = 0x80;
|
|
const unsigned char RSV1 = 0x40;
|
|
const unsigned char RSV2 = 0x20;
|
|
const unsigned char RSV3 = 0x10;
|
|
const unsigned char RSV_MASK = RSV1 | RSV2 | RSV3;
|
|
const unsigned char OPCODE_MASK = 0x0F;
|
|
|
|
const unsigned char TEXT_MODE = 0x01;
|
|
const unsigned char BINARY_MODE = 0x02;
|
|
|
|
const unsigned char WEBSOCKET_OPCODE = 0x0F;
|
|
const unsigned char WEBSOCKET_FRAME_CONTINUE = 0x0;
|
|
const unsigned char WEBSOCKET_FRAME_TEXT = 0x1;
|
|
const unsigned char WEBSOCKET_FRAME_BINARY = 0x2;
|
|
const unsigned char WEBSOCKET_FRAME_CLOSE = 0x8;
|
|
const unsigned char WEBSOCKET_FRAME_PING = 0x9;
|
|
const unsigned char WEBSOCKET_FRAME_PONG = 0xA;
|
|
|
|
const unsigned char WEBSOCKET_MASK = 0x80;
|
|
const unsigned char WEBSOCKET_PAYLOAD_LEN = 0x7F;
|
|
const unsigned char WEBSOCKET_PAYLOAD_LEN_UINT16 = 126;
|
|
const unsigned char WEBSOCKET_PAYLOAD_LEN_UINT64 = 127;
|
|
|
|
const char* WEB_SOCKET_KEY = "Sec-WebSocket-Key: ";
|
|
const char* WEB_SOCKET_KEY2 = "Sec-Websocket-Key: ";
|
|
|
|
namespace a8
|
|
{
|
|
|
|
void WebSocketSession::SendBuff(const char* buff, unsigned int bufflen)
|
|
{
|
|
if (!handshook_) {
|
|
a8::TcpSession::SendBuff(buff, bufflen);
|
|
} else {
|
|
unsigned char szbuff [1024 * 65];
|
|
szbuff[0] = FIN | BINARY_MODE | 0;
|
|
if (bufflen < 126) {
|
|
szbuff[1] = bufflen;
|
|
memmove(szbuff + 2, buff, bufflen);
|
|
a8::TcpSession::SendBuff((char*)szbuff, bufflen + 2);
|
|
} else if (bufflen <= 0xFFFF) {
|
|
szbuff[1] = 126;
|
|
szbuff[2] = (bufflen >> 8) & 0xFF;
|
|
szbuff[3] = bufflen & 0xFF;
|
|
memmove(szbuff + 4, buff, bufflen);
|
|
a8::TcpSession::SendBuff((char*)szbuff, bufflen + 4);
|
|
} else {
|
|
assert(false);
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
void WebSocketSession::Reset()
|
|
{
|
|
a8::BaseHttpSession::Reset();
|
|
if (!decoded_buff_) {
|
|
decoded_buff_ = (char *)malloc(max_packet_len_ + 1);
|
|
decoded_bufflen_ = 0;
|
|
}
|
|
handshook_ = false;
|
|
}
|
|
|
|
void WebSocketSession::Destory()
|
|
{
|
|
a8::TcpSession::Destory();
|
|
if (decoded_buff_) {
|
|
free(decoded_buff_);
|
|
decoded_buff_ = nullptr;
|
|
}
|
|
decoded_bufflen_ = 0;
|
|
handshook_ = false;
|
|
}
|
|
|
|
void WebSocketSession::DecodePacket(char* buf, int& offset, unsigned int buflen)
|
|
{
|
|
if (!handshook_) {
|
|
buf[buflen] = '\0';
|
|
ProcessHandShake(buf, offset, buflen);
|
|
} else {
|
|
DecodeFrame(buf, offset, buflen);
|
|
}
|
|
}
|
|
|
|
bool WebSocketSession::HandleRedirect(const std::string& url, const std::string& querystr,
|
|
std::string& location)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void WebSocketSession::ProcessHandShake(char* buf, int& offset, unsigned int buflen)
|
|
{
|
|
char* pend = strstr(buf + offset, "\r\n\r\n");
|
|
if (!pend) {
|
|
return;
|
|
}
|
|
if (strstr(buf + offset, WEB_SOCKET_KEY) || strstr(buf + offset, WEB_SOCKET_KEY2)) {
|
|
ProcessWsHandShake(buf, offset, buflen);
|
|
} else {
|
|
ProcessHttpHandShake(buf, offset, buflen);
|
|
}
|
|
}
|
|
|
|
void WebSocketSession::ProcessWsHandShake(char* buf, int& offset, unsigned int buflen)
|
|
{
|
|
char* pend = strstr(buf + offset, "\r\n\r\n");
|
|
if (!pend) {
|
|
return;
|
|
}
|
|
if (strncmp(buf + offset, "GET ", strlen("GET ")) == 0) {
|
|
std::string url;
|
|
std::string querystr;
|
|
std::string location;
|
|
|
|
a8::ParserQueryStr(buf + offset + 4, url, querystr);
|
|
bool need_redirect = HandleRedirect(url, querystr, location);
|
|
if (need_redirect) {
|
|
std::string response = a8::Format(
|
|
"HTTP/1.1 %d OK\r\n"
|
|
"Location: %s\r\n"
|
|
"Connection: close\r\n"
|
|
"Content-Type: text/plain\r\n"
|
|
"Content-Length: 0\r\n\r\n",
|
|
{
|
|
301,
|
|
location,
|
|
});
|
|
SendBuff(response.data(), response.size());
|
|
return;
|
|
}
|
|
}
|
|
std::string server_key;
|
|
{
|
|
char* p1 = strstr(buf + offset, WEB_SOCKET_KEY);
|
|
if (!p1) {
|
|
p1 = strstr(buf + offset, WEB_SOCKET_KEY2);
|
|
}
|
|
if (p1) {
|
|
p1 += strlen(WEB_SOCKET_KEY);
|
|
char* p2 = strstr(p1 , "\r\n");
|
|
if (p2) {
|
|
server_key.append(p1, p2-p1);
|
|
}
|
|
}
|
|
}
|
|
server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
std::string sha1_data = a8::openssl::Sha1Encode(server_key);
|
|
std::string hash_data;
|
|
a8::openssl::Base64Encode(sha1_data, hash_data);
|
|
|
|
std::string response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
|
"Upgrade: websocket\r\n"
|
|
"Connection: Upgrade\r\n"
|
|
"Sec-WebSocket-Accept: " + hash_data + "\r\n" +
|
|
"\r\n";
|
|
SendBuff(response.data(), response.size());
|
|
handshook_ = true;
|
|
offset += pend - buf - offset + strlen("\r\n\r\n");
|
|
}
|
|
|
|
void WebSocketSession::ProcessUserPacket()
|
|
{
|
|
int offset = 0;
|
|
int prev_offset = 0;
|
|
do {
|
|
prev_offset = offset;
|
|
DecodeUserPacket(decoded_buff_, offset, decoded_bufflen_);
|
|
} while (prev_offset < offset && offset < decoded_bufflen_);
|
|
|
|
if (offset > 0 && offset < decoded_bufflen_){
|
|
memmove(decoded_buff_, decoded_buff_ + offset, decoded_bufflen_ - offset);
|
|
}
|
|
decoded_bufflen_ -= offset;
|
|
if (decoded_bufflen_ >= max_packet_len_) {
|
|
//收到超长包
|
|
Close();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void WebSocketSession::DecodeFrame(char* buf, int& offset, unsigned int buflen)
|
|
{
|
|
if (offset + 2 < buflen) {
|
|
return;
|
|
}
|
|
char* real_buf = buf + offset;
|
|
unsigned int ava_len = buflen - offset;
|
|
unsigned char header = real_buf[0];
|
|
unsigned char mask_payloadlen = real_buf[1];
|
|
|
|
bool is_final_frame = (header & FIN) == FIN;
|
|
#if 0
|
|
bool reserved_bits = (header & FIN) == RSV_MASK;
|
|
#endif
|
|
unsigned char opcode = header & OPCODE_MASK;
|
|
#if 0
|
|
bool opcode_is_control = opcode & 0x8;
|
|
#endif
|
|
|
|
if (opcode == WEBSOCKET_FRAME_CLOSE) {
|
|
Close();
|
|
return;
|
|
}
|
|
if (!is_final_frame) {
|
|
return;
|
|
}
|
|
|
|
bool is_masked = (mask_payloadlen & 0x80) == 0x80;
|
|
if (opcode != BINARY_MODE) {
|
|
return;
|
|
}
|
|
assert(opcode == BINARY_MODE);
|
|
assert(is_masked);
|
|
|
|
unsigned char payloadlen = mask_payloadlen & 0x7F;
|
|
unsigned int framelen = 0;
|
|
int mask_offset = 0;
|
|
|
|
if (payloadlen < 126) {
|
|
framelen = payloadlen;
|
|
mask_offset = 2;
|
|
} else if (payloadlen == 126 && ava_len >= 4) {
|
|
framelen = ntohs( *(u_short*) (real_buf + 2) );
|
|
mask_offset = 4;
|
|
} else if (payloadlen == 127 && ava_len >= 8) {
|
|
//int32 or int64?
|
|
framelen = ntohl( *(u_long*) (real_buf + 2) );
|
|
mask_offset = 8;
|
|
} else {
|
|
return;
|
|
}
|
|
unsigned int real_pkg_len = mask_offset + framelen + (is_masked ? 4 : 0);
|
|
if (ava_len < real_pkg_len) {
|
|
return;
|
|
}
|
|
|
|
if (is_masked) {
|
|
unsigned char *frame_mask = (unsigned char*)(real_buf + mask_offset);
|
|
memcpy(&decoded_buff_[decoded_bufflen_], real_buf + mask_offset + 4, framelen);
|
|
for (unsigned int i = 0; i < framelen; i++) {
|
|
decoded_buff_[decoded_bufflen_ + i] = (decoded_buff_[i] ^ frame_mask[i%4]);
|
|
}
|
|
} else {
|
|
memcpy(&decoded_buff_[decoded_bufflen_], real_buf + mask_offset, framelen);
|
|
}
|
|
decoded_bufflen_ += framelen;
|
|
|
|
ProcessUserPacket();
|
|
offset += real_pkg_len;
|
|
}
|
|
|
|
}
|