2805: update libwebsocket to 4.x and fix bug and make client able to use websocket r=def- a=QingGo



Co-authored-by: QingGo <zyqingjohn@qq.com>
This commit is contained in:
bors[bot] 2020-09-13 19:10:36 +00:00 committed by GitHub
commit 5d64ed34aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 66 deletions

View file

@ -1501,10 +1501,15 @@ int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size
}
#if defined(CONF_WEBSOCKETS)
if(addr->type&NETTYPE_WEBSOCKET_IPV4)
if(addr->type & NETTYPE_WEBSOCKET_IPV4)
{
if(sock.web_ipv4sock >= 0)
d = websocket_send(sock.web_ipv4sock, (const unsigned char*)data, size, addr->port);
{
char addr_str[NETADDR_MAXSTRSIZE];
str_format(addr_str, sizeof(addr_str), "%d.%d.%d.%d", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3]);
d = websocket_send(sock.web_ipv4sock, (const unsigned char *)data, size, addr_str, addr->port);
}
else
dbg_msg("net", "can't send websocket_ipv4 traffic to this socket");
}

View file

@ -708,6 +708,12 @@ void CClient::Connect(const char *pAddress, const char *pPassword)
str_format(aBuf, sizeof(aBuf), "connecting to '%s'", m_aServerAddressStr);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf);
bool is_websocket = false;
if(strncmp(m_aServerAddressStr, "ws://", 5) == 0)
{
is_websocket = true;
str_copy(m_aServerAddressStr, pAddress + 5, sizeof(m_aServerAddressStr));
}
ServerInfoRequest();
if(net_host_lookup(m_aServerAddressStr, &m_ServerAddress, m_NetClient[CLIENT_MAIN].NetType()) != 0)
@ -735,6 +741,10 @@ void CClient::Connect(const char *pAddress, const char *pPassword)
m_pConsole->DeregisterTempAll();
if(m_ServerAddress.port == 0)
m_ServerAddress.port = Port;
if(is_websocket)
{
m_ServerAddress.type = NETTYPE_WEBSOCKET_IPV4;
}
m_NetClient[CLIENT_MAIN].Connect(&m_ServerAddress);
SetState(IClient::STATE_CONNECTING);

View file

@ -1,13 +1,14 @@
#if defined(CONF_WEBSOCKETS)
#include <string.h>
#include <map>
#include <stdlib.h>
#include <string>
#include <libwebsockets.h>
#include "base/system.h"
#include "protocol.h"
#include "ringbuffer.h"
#include <arpa/inet.h>
#include <libwebsockets.h>
extern "C" {
@ -16,10 +17,14 @@ extern "C" {
// not sure why would anyone need more than one but well...
#define WS_CONTEXTS 4
// ddnet client opens two connections for whatever reason
#define WS_CLIENTS MAX_CLIENTS*2
#define WS_CLIENTS (MAX_CLIENTS * 2)
typedef TStaticRingBuffer<unsigned char, WS_CLIENTS * 4 * 1024, CRingBufferBase::FLAG_RECYCLE> TRecvBuffer;
typedef TStaticRingBuffer<unsigned char, 4 * 1024, CRingBufferBase::FLAG_RECYCLE> TSendBuffer;
typedef TStaticRingBuffer<unsigned char, WS_CLIENTS * 4 * 1024,
CRingBufferBase::FLAG_RECYCLE>
TRecvBuffer;
typedef TStaticRingBuffer<unsigned char, 4 * 1024,
CRingBufferBase::FLAG_RECYCLE>
TSendBuffer;
typedef struct
{
@ -32,7 +37,7 @@ typedef struct
struct per_session_data
{
struct lws *wsi;
int port;
std::string addr_str;
sockaddr_in addr;
TSendBuffer send_buffer;
};
@ -40,14 +45,15 @@ struct per_session_data
struct context_data
{
lws_context *context;
per_session_data *port_map[WS_CLIENTS];
std::map<std::string, per_session_data *> port_map;
TRecvBuffer recv_buffer;
int last_used_port;
};
static int receive_chunk(context_data *ctx_data, struct per_session_data *pss, void *in, size_t len)
static int receive_chunk(context_data *ctx_data, struct per_session_data *pss,
void *in, size_t len)
{
websocket_chunk *chunk = (websocket_chunk *)ctx_data->recv_buffer.Allocate(len + sizeof(websocket_chunk));
websocket_chunk *chunk = (websocket_chunk *)ctx_data->recv_buffer.Allocate(
len + sizeof(websocket_chunk));
if(chunk == 0)
return 1;
chunk->size = len;
@ -57,68 +63,64 @@ static int receive_chunk(context_data *ctx_data, struct per_session_data *pss, v
return 0;
}
static int websocket_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
static int websocket_callback(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct per_session_data *pss = (struct per_session_data *)user;
lws_context *context = lws_get_context(wsi);
context_data *ctx_data = (context_data *)lws_context_user(context);
switch(reason)
{
case LWS_CALLBACK_WSI_CREATE:
if(pss == NULL)
{
return 0;
}
/* FALLTHRU */
case LWS_CALLBACK_ESTABLISHED:
{
int port = -1;
for(int i = 0; i < WS_CLIENTS; i++)
{
int j = (ctx_data->last_used_port + i + 1) % WS_CLIENTS;
if(ctx_data->port_map[j] == NULL)
{
port = j;
break;
}
}
if(port == -1)
{
dbg_msg("websockets", "no free ports, dropping");
pss->port = -1;
return -1;
}
ctx_data->last_used_port = port;
pss->wsi = wsi;
int fd = lws_get_socket_fd(wsi);
socklen_t addr_size = sizeof(pss->addr);
getpeername(fd, (struct sockaddr *)&pss->addr, &addr_size);
int orig_port = ntohs(pss->addr.sin_port);
pss->addr.sin_port = htons(port);
pss->send_buffer.Init();
pss->port = port;
ctx_data->port_map[port] = pss;
char addr_str[NETADDR_MAXSTRSIZE];
inet_ntop(AF_INET, &pss->addr.sin_addr, addr_str, sizeof(addr_str));
dbg_msg("websockets", "connection established with %s:%d , assigned fake port %d", addr_str, orig_port, port);
dbg_msg("websockets",
"connection established with %s:%d",
addr_str, orig_port);
char buf[100];
snprintf(buf, sizeof(buf), "%s:%d", addr_str, orig_port);
pss->addr_str = std::string(buf);
ctx_data->port_map[pss->addr_str] = pss;
}
break;
case LWS_CALLBACK_CLOSED:
{
dbg_msg("websockets", "connection with fake port %d closed", pss->port);
if (pss->port > -1) {
unsigned char close_packet[] = { 0x10, 0x0e, 0x00, 0x04 };
dbg_msg("websockets", "connection with addr string %s closed", pss->addr_str.c_str());
if(!pss->addr_str.empty())
{
unsigned char close_packet[] = {0x10, 0x0e, 0x00, 0x04};
receive_chunk(ctx_data, pss, &close_packet, sizeof(close_packet));
pss->wsi = 0;
ctx_data->port_map[pss->port] = NULL;
ctx_data->port_map.erase(pss->addr_str);
}
}
break;
case LWS_CALLBACK_CLIENT_WRITEABLE:
/* FALLTHRU */
case LWS_CALLBACK_SERVER_WRITEABLE:
{
websocket_chunk *chunk = (websocket_chunk *)pss->send_buffer.First();
if(chunk == NULL)
break;
int len = chunk->size - chunk->read;
int n = lws_write(wsi, &chunk->data[LWS_SEND_BUFFER_PRE_PADDING + chunk->read], chunk->size - chunk->read, LWS_WRITE_BINARY);
int n =
lws_write(wsi, &chunk->data[LWS_SEND_BUFFER_PRE_PADDING + chunk->read],
chunk->size - chunk->read, LWS_WRITE_BINARY);
if(n < 0)
return 1;
if(n < len)
@ -132,10 +134,12 @@ static int websocket_callback(struct lws *wsi, enum lws_callback_reasons reason,
}
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
/* FALLTHRU */
case LWS_CALLBACK_RECEIVE:
if(pss->port == -1)
if(pss->addr_str.empty())
return -1;
if(!receive_chunk(ctx_data, pss, in, len))
if(receive_chunk(ctx_data, pss, in, len))
return 1;
break;
@ -146,14 +150,15 @@ static int websocket_callback(struct lws *wsi, enum lws_callback_reasons reason,
return 0;
}
static struct lws_protocols protocols[] = { {
"binary", /* name */
websocket_callback, /* callback */
sizeof(struct per_session_data) /* per_session_data_size */
},
{
NULL, NULL, 0 /* End of list */
} };
static struct lws_protocols protocols[] = {
{
"binary", /* name */
websocket_callback, /* callback */
sizeof(struct per_session_data) /* per_session_data_size */
},
{
NULL, NULL, 0 /* End of list */
}};
static context_data contexts[WS_CONTEXTS];
@ -188,9 +193,7 @@ int websocket_create(const char *addr, int port)
{
return -1;
}
memset(ctx_data->port_map, 0, sizeof(ctx_data->port_map));
ctx_data->recv_buffer.Init();
ctx_data->last_used_port = 0;
return first_free;
}
@ -204,12 +207,13 @@ int websocket_destroy(int socket)
return 0;
}
int websocket_recv(int socket, unsigned char *data, size_t maxsize, struct sockaddr_in *sockaddrbuf, size_t fromLen)
int websocket_recv(int socket, unsigned char *data, size_t maxsize,
struct sockaddr_in *sockaddrbuf, size_t fromLen)
{
lws_context *context = contexts[socket].context;
if(context == NULL)
return -1;
int n = lws_service(context, 0);
int n = lws_service(context, -1);
if(n < 0)
return n;
context_data *ctx_data = (context_data *)lws_context_user(context);
@ -233,16 +237,41 @@ int websocket_recv(int socket, unsigned char *data, size_t maxsize, struct socka
}
}
int websocket_send(int socket, const unsigned char *data, size_t size, int port)
int websocket_send(int socket, const unsigned char *data, size_t size,
const char *addr_str, int port)
{
lws_context *context = contexts[socket].context;
if(context == NULL)
{
return -1;
}
context_data *ctx_data = (context_data *)lws_context_user(context);
struct per_session_data *pss = ctx_data->port_map[port];
char buf[100];
snprintf(buf, sizeof(buf), "%s:%d", addr_str, port);
std::string addr_str_with_port = std::string(buf);
struct per_session_data *pss = ctx_data->port_map[addr_str_with_port];
if(pss == NULL)
return -1;
websocket_chunk *chunk = (websocket_chunk *)pss->send_buffer.Allocate(size + sizeof(websocket_chunk) + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING);
{
struct lws_client_connect_info ccinfo = {0};
ccinfo.context = context;
ccinfo.address = addr_str;
ccinfo.port = port;
ccinfo.protocol = protocols[0].name;
lws *wsi = lws_client_connect_via_info(&ccinfo);
if(wsi == NULL)
{
return -1;
}
lws_service(context, -1);
pss = ctx_data->port_map[addr_str_with_port];
if(pss == NULL)
{
return -1;
}
}
websocket_chunk *chunk = (websocket_chunk *)pss->send_buffer.Allocate(
size + sizeof(websocket_chunk) + LWS_SEND_BUFFER_PRE_PADDING +
LWS_SEND_BUFFER_POST_PADDING);
if(chunk == NULL)
return -1;
chunk->size = size;
@ -250,6 +279,7 @@ int websocket_send(int socket, const unsigned char *data, size_t size, int port)
memcpy(&chunk->addr, &pss->addr, sizeof(sockaddr_in));
memcpy(&chunk->data[LWS_SEND_BUFFER_PRE_PADDING], data, size);
lws_callback_on_writable(pss->wsi);
lws_service(context, -1);
return size;
}
@ -260,12 +290,11 @@ int websocket_fd_set(int socket, fd_set *set)
return -1;
context_data *ctx_data = (context_data *)lws_context_user(context);
int max = 0;
for(int i = 0; i < WS_CLIENTS; i++)
for(auto const &x : ctx_data->port_map)
{
per_session_data *pss = ctx_data->port_map[i];
if(pss == NULL)
if(x.second == NULL)
continue;
int fd = lws_get_socket_fd(pss->wsi);
int fd = lws_get_socket_fd(x.second->wsi);
if(fd > max)
max = fd;
FD_SET(fd, set);
@ -273,4 +302,4 @@ int websocket_fd_set(int socket, fd_set *set)
return max;
}
}
#endif
#endif

View file

@ -2,7 +2,7 @@
#define ENGINE_SHARED_WEBSOCKETS_H
#if !defined(CONF_FAMILY_UNIX)
#error websockets only work on unix, sorry
#error websockets only work on unix, sorry
#endif
#include <netinet/in.h>
@ -10,7 +10,8 @@
int websocket_create(const char *addr, int port);
int websocket_destroy(int socket);
int websocket_recv(int socket, unsigned char *data, size_t maxsize, struct sockaddr_in *sockaddrbuf, size_t fromLen);
int websocket_send(int socket, const unsigned char *data, size_t size, int port);
int websocket_send(int socket, const unsigned char *data, size_t size,
const char *addr_str, int port);
int websocket_fd_set(int socket, fd_set *set);
#endif // ENGINE_SHARED_WEBSOCKETS_H