Skip to content

Instantly share code, notes, and snippets.

@optman
Last active December 20, 2021 05:06
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save optman/620518202fcde3e5e5acfaf3f6035df6 to your computer and use it in GitHub Desktop.
Save optman/620518202fcde3e5e5acfaf3f6035df6 to your computer and use it in GitHub Desktop.
libevent websocket client test
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/event.h>
#include <event2/dns.h>
#include <arpa/inet.h>
#include <iostream>
using namespace std;
#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
#define ntohll(x) htonll(x)
const char* uri = "/echo";
const char* host = "127.0.0.1";
unsigned short port = 1234;
const char* fixed_key = "dGhlIHNhbXBsZSBub25jZQ==";
const char* fixed_accept = "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=";
bool upgraded = false;
static void send(evbuffer* buf, const char* msg){
size_t len = strlen(msg);
uint8_t a = 0;
a |= 1 << 7; //fin
a |= 1; //text frame
uint8_t b = 0;
b |= 1 << 7; //mask
uint16_t c = 0;
uint64_t d = 0;
//payload len
if(len < 126){
b |= len;
}
else if(len < (1 << 16)){
b |= 126;
c = htons(len);
}else{
b |= 127;
d = htonll(len);
}
evbuffer_add(buf, &a, 1);
evbuffer_add(buf, &b, 1);
if(c) evbuffer_add(buf, &c, sizeof(c));
else if(d) evbuffer_add(buf, &d, sizeof(d));
uint8_t mask_key[4] = {1, 2, 3, 4}; //should be random
evbuffer_add(buf, &mask_key, 4);
uint8_t m;
for(int i = 0; i < len; i++){
m = msg[i] ^ mask_key[i%4];
evbuffer_add(buf, &m, 1);
}
}
static void receive(evbuffer* buf){
auto data_len = evbuffer_get_length(buf);
if(data_len < 2)
return;
unsigned char* data = evbuffer_pullup(buf, data_len);
int fin = !!(*data & 0x80);
int opcode = *data & 0x0F;
int mask = !!(*(data+1) & 0x80);
uint64_t payload_len = *(data+1) & 0x7F;
size_t header_len = 2 + (mask ? 4 : 0);
if(payload_len < 126){
if(header_len > data_len)
return;
}else if(payload_len == 126){
header_len += 2;
if(header_len > data_len)
return;
payload_len = ntohs(*(uint16_t*)(data+2));
}else if(payload_len == 127){
header_len += 8;
if(header_len > data_len)
return;
payload_len = ntohll(*(uint64_t*)(data+2));
}
if(header_len + payload_len > data_len)
return;
const unsigned char* mask_key = data + header_len - 4;
cout << "data_len " << data_len << " mask " << mask << " head_len " << header_len << " payload_len " << payload_len << endl;
for(int i = 0; mask && i < payload_len; i++)
data[header_len + i] ^= mask_key[i%4];
if(opcode == 0x01)
cout << ">" <<string((const char*)data + header_len, payload_len) << "<"<< endl;
if(!fin){
cout << "fram to be continue..." << endl;
}
evbuffer_drain(buf, header_len + payload_len);
//next frame
receive(buf);
}
static void request(bufferevent* bev){
cout << "request" << endl;
auto out = bufferevent_get_output(bev);
evbuffer_add_printf(out, "GET %s HTTP/1.1\r\n", uri);
evbuffer_add_printf(out, "Host:%s:%d\r\n",host, port);
evbuffer_add_printf(out, "Upgrade:websocket\r\n");
evbuffer_add_printf(out, "Connection:upgrade\r\n");
evbuffer_add_printf(out, "Sec-WebSocket-Key:%s\r\n", fixed_key);
evbuffer_add_printf(out, "Sec-WebSocket-Version:13\r\n");
evbuffer_add_printf(out, "Origin:http://%s:%d\r\n",host, port); //missing this key will lead to 403 response.
evbuffer_add_printf(out, "\r\n");
}
static void read_cb(bufferevent* bev, void* ptr){
cout << "read cb" << endl;
auto input = bufferevent_get_input(bev);
if(!upgraded){
auto data_len = evbuffer_get_length(input);
unsigned char* data = evbuffer_pullup(input, data_len);
if(!strstr((const char*)data, "\r\n\r\n"))
return;
if(strncmp((const char*)data, "HTTP/1.1 101", strlen("HTTP/1.1 101")) != 0
|| !strstr((const char*)data, fixed_accept)){
cout << string((const char*)data, data_len) << endl;
cout << "websocket upgrade fail!" << endl;
}else{
//drain
evbuffer_drain(input, data_len);
upgraded = true;
send(bufferevent_get_output(bev), "hello");
char long_msg[1024];
memset(long_msg, '#', sizeof(long_msg));
long_msg[0]= 'A';
long_msg[sizeof(long_msg)-2]= 'E';
long_msg[sizeof(long_msg)-1]= 0;
send(bufferevent_get_output(bev), long_msg);
char longlong_msg[(1<< 16) + 10];
memset(longlong_msg, '#', sizeof(longlong_msg));
longlong_msg[0]= 'A';
longlong_msg[sizeof(longlong_msg)-2]= 'X';
longlong_msg[sizeof(longlong_msg)-1]= 0;
send(bufferevent_get_output(bev), longlong_msg);
}
}else{
//handle frame
receive(input);
}
}
static void write_cb(bufferevent* bev, void* ptr){
cout << "write cb" << endl;
}
static void event_cb(bufferevent* bev, short events, void* ptr){
if(events & BEV_EVENT_CONNECTED){
cout << "connected" << endl;
request(bev);
}else{
cout << "diconnected " << events << endl;
}
}
int main(){
auto base = event_base_new();
auto dns_base = evdns_base_new(base, 1);
auto bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
bufferevent_enable(bev, EV_READ|EV_WRITE);
bufferevent_socket_connect_hostname(bev, dns_base, AF_INET, host, port);
event_base_dispatch(base);
return 0;
}
package main
import (
"golang.org/x/net/websocket"
"log"
"net/http"
)
func EchoServer(ws *websocket.Conn) {
for {
var msg string
if err := websocket.Message.Receive(ws, &msg); err != nil {
log.Println("receive msg fail!!!")
return
} else {
log.Println("msg len", len(msg))
log.Println(msg)
websocket.Message.Send(ws, msg)
}
}
}
func main() {
http.Handle("/echo", websocket.Handler(EchoServer))
log.Fatal(http.ListenAndServe(":1234", nil))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment