mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-07-02 09:26:39 +02:00
JSON-RPC over Websockets: Fix bug with >64Kb responses.
Eg if there are 10.000 users online and you do user.list. The old websocket framing assumed no response was >64Kb. This also creates a new function websocket_create_packet_ex()
This commit is contained in:
@@ -852,6 +852,7 @@ extern MODVAR void (*rpc_error)(Client *client, json_t *request, JsonRpcError er
|
||||
extern MODVAR void (*rpc_error_fmt)(Client *client, json_t *request, JsonRpcError error_code, FORMAT_STRING(const char *fmt), ...) __attribute__((format(printf,4,5)));
|
||||
extern MODVAR int (*websocket_handle_websocket)(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len));
|
||||
extern MODVAR int (*websocket_create_packet)(int opcode, char **buf, int *len);
|
||||
extern MODVAR int (*websocket_create_packet_ex)(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize);
|
||||
extern MODVAR int (*websocket_create_packet_simple)(int opcode, const char **buf, int *len);
|
||||
/* /Efuncs */
|
||||
|
||||
@@ -897,6 +898,7 @@ extern void rpc_error_default_handler(Client *client, json_t *request, JsonRpcEr
|
||||
extern void rpc_error_fmt_default_handler(Client *client, json_t *request, JsonRpcError error_code, const char *fmt, ...);
|
||||
extern int websocket_handle_websocket_default_handler(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len));
|
||||
extern int websocket_create_packet_default_handler(int opcode, char **buf, int *len);
|
||||
extern int websocket_create_packet_ex_default_handler(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize);
|
||||
extern int websocket_create_packet_simple_default_handler(int opcode, const char **buf, int *len);
|
||||
/* End of default handlers for efunctions */
|
||||
|
||||
|
||||
@@ -2538,6 +2538,7 @@ enum EfunctionType {
|
||||
EFUNC_RPC_ERROR_FMT,
|
||||
EFUNC_WEBSOCKET_HANDLE_WEBSOCKET,
|
||||
EFUNC_WEBSOCKET_CREATE_PACKET,
|
||||
EFUNC_WEBSOCKET_CREATE_PACKET_EX,
|
||||
EFUNC_WEBSOCKET_CREATE_PACKET_SIMPLE,
|
||||
};
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ void (*rpc_error)(Client *client, json_t *request, JsonRpcError error_code, cons
|
||||
void (*rpc_error_fmt)(Client *client, json_t *request, JsonRpcError error_code, const char *fmt, ...);
|
||||
int (*websocket_handle_websocket)(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len));
|
||||
int (*websocket_create_packet)(int opcode, char **buf, int *len);
|
||||
int (*websocket_create_packet_ex)(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize);
|
||||
int (*websocket_create_packet_simple)(int opcode, const char **buf, int *len);
|
||||
|
||||
Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)())
|
||||
@@ -430,5 +431,6 @@ void efunctions_init(void)
|
||||
efunc_init_function(EFUNC_RPC_ERROR_FMT, rpc_error_fmt, rpc_error_fmt_default_handler);
|
||||
efunc_init_function(EFUNC_WEBSOCKET_HANDLE_WEBSOCKET, websocket_handle_websocket, websocket_handle_websocket_default_handler);
|
||||
efunc_init_function(EFUNC_WEBSOCKET_CREATE_PACKET, websocket_create_packet, websocket_create_packet_default_handler);
|
||||
efunc_init_function(EFUNC_WEBSOCKET_CREATE_PACKET_EX, websocket_create_packet_ex, websocket_create_packet_ex_default_handler);
|
||||
efunc_init_function(EFUNC_WEBSOCKET_CREATE_PACKET_SIMPLE, websocket_create_packet_simple, websocket_create_packet_simple_default_handler);
|
||||
}
|
||||
|
||||
@@ -1421,6 +1421,11 @@ int websocket_create_packet_default_handler(int opcode, char **buf, int *len)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int websocket_create_packet_ex_default_handler(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int websocket_create_packet_simple_default_handler(int opcode, const char **buf, int *len)
|
||||
{
|
||||
return -1;
|
||||
|
||||
@@ -431,11 +431,16 @@ void rpc_sendto(Client *client, const char *buf, int len)
|
||||
if (MyConnect(client) && IsRPC(client) && WSU(client) && WSU(client)->handshake_completed)
|
||||
{
|
||||
/* Websocket */
|
||||
static char utf8buf[65535]; // TODO: dynamic!!!
|
||||
char *newbuf = unrl_utf8_make_valid(buf, utf8buf, sizeof(utf8buf), 1);
|
||||
int utf8bufsize = len*2 + 16;
|
||||
char *utf8buf = safe_alloc(utf8bufsize);
|
||||
char *newbuf = unrl_utf8_make_valid(buf, utf8buf, utf8bufsize, 1);
|
||||
int newlen = strlen(newbuf);
|
||||
websocket_create_packet(WSOP_TEXT, &newbuf, &newlen);
|
||||
int ws_sendbufsize = newlen + 64 + ((newlen / 1024) * 64); // some random magic
|
||||
char *ws_sendbuf = safe_alloc(ws_sendbufsize);
|
||||
websocket_create_packet_ex(WSOP_TEXT, &newbuf, &newlen, ws_sendbuf, ws_sendbufsize);
|
||||
dbuf_put(&client->local->sendQ, newbuf, newlen);
|
||||
safe_free(ws_sendbuf);
|
||||
safe_free(utf8buf);
|
||||
} else {
|
||||
/* Unix domain socket or HTTP */
|
||||
dbuf_put(&client->local->sendQ, buf, len);
|
||||
|
||||
@@ -40,6 +40,7 @@ struct HTTPForwardedHeader
|
||||
/* Forward declarations - public functions */
|
||||
int _websocket_handle_websocket(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len));
|
||||
int _websocket_create_packet(int opcode, char **buf, int *len);
|
||||
int _websocket_create_packet_ex(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize);
|
||||
int _websocket_create_packet_simple(int opcode, const char **buf, int *len);
|
||||
/* Forward declarations - other */
|
||||
int websocket_handle_packet(Client *client, const char *readbuf, int length, int callback(Client *client, char *buf, int len));
|
||||
@@ -57,6 +58,7 @@ MOD_TEST()
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_HANDLE_WEBSOCKET, _websocket_handle_websocket);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_CREATE_PACKET, _websocket_create_packet);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_CREATE_PACKET_EX, _websocket_create_packet_ex);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_CREATE_PACKET_SIMPLE, _websocket_create_packet_simple);
|
||||
|
||||
/* Init first, since we manage sockets */
|
||||
@@ -289,7 +291,7 @@ int websocket_handle_packet_pong(Client *client, const char *buf, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Create a simple websocket packet that is ready to be send.
|
||||
/** Create a simple websocket packet that is ready to be sent.
|
||||
* This is the simple version that is used ONLY for WSOP_PONG,
|
||||
* as it does not take \r\n into account.
|
||||
*/
|
||||
@@ -322,15 +324,18 @@ int _websocket_create_packet_simple(int opcode, const char **buf, int *len)
|
||||
}
|
||||
|
||||
/** Create a websocket packet that is ready to be send.
|
||||
* This is the more complex version that takes into account
|
||||
* stripping off \r and \n, and possibly multi line due to
|
||||
* labeled-response. It is used for WSOP_TEXT and WSOP_BINARY.
|
||||
* This version takes into account stripping off \r and \n,
|
||||
* and possibly multi line due to labeled-response.
|
||||
* It is used for WSOP_TEXT and WSOP_BINARY.
|
||||
* The end result is one or more websocket frames,
|
||||
* all in a single packet *buf with size *len.
|
||||
*
|
||||
* This is the version that uses the specified buffer,
|
||||
* it is used from the JSON-RPC code,
|
||||
* and indirectly from websocket_create_packet().
|
||||
*/
|
||||
int _websocket_create_packet(int opcode, char **buf, int *len)
|
||||
int _websocket_create_packet_ex(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize)
|
||||
{
|
||||
static char sendbuf[WEBSOCKET_SEND_BUFFER_SIZE];
|
||||
char *s = *buf; /* points to start of current line */
|
||||
char *s2; /* used for searching of end of current line */
|
||||
char *lastbyte = *buf + *len - 1; /* points to last byte in *buf that can be safely read */
|
||||
@@ -359,10 +364,12 @@ int _websocket_create_packet(int opcode, char **buf, int *len)
|
||||
|
||||
if (bytes_to_copy < 126)
|
||||
bytes_single_frame = 2 + bytes_to_copy;
|
||||
else
|
||||
else if (bytes_to_copy < 65536)
|
||||
bytes_single_frame = 4 + bytes_to_copy;
|
||||
else
|
||||
bytes_single_frame = 10 + bytes_to_copy;
|
||||
|
||||
if (bytes_in_sendbuf + bytes_single_frame > sizeof(sendbuf))
|
||||
if (bytes_in_sendbuf + bytes_single_frame > sendbufsize)
|
||||
{
|
||||
/* Overflow. This should never happen. */
|
||||
unreal_log(ULOG_WARNING, "websocket", "BUG_WEBSOCKET_OVERFLOW", NULL,
|
||||
@@ -370,7 +377,7 @@ int _websocket_create_packet(int opcode, char **buf, int *len)
|
||||
"$bytes_in_sendbuf + $bytes_single_frame > $sendbuf_size",
|
||||
log_data_integer("bytes_in_sendbuf", bytes_in_sendbuf),
|
||||
log_data_integer("bytes_single_frame", bytes_single_frame),
|
||||
log_data_integer("sendbuf_size", sizeof(sendbuf)));
|
||||
log_data_integer("sendbuf_size", sendbufsize));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -382,12 +389,27 @@ int _websocket_create_packet(int opcode, char **buf, int *len)
|
||||
/* Short payload */
|
||||
o[1] = (char)bytes_to_copy;
|
||||
memcpy(&o[2], s, bytes_to_copy);
|
||||
} else {
|
||||
} else
|
||||
if (bytes_to_copy < 65536)
|
||||
{
|
||||
/* Long payload */
|
||||
o[1] = 126;
|
||||
o[2] = (char)((bytes_to_copy >> 8) & 0xFF);
|
||||
o[3] = (char)(bytes_to_copy & 0xFF);
|
||||
memcpy(&o[4], s, bytes_to_copy);
|
||||
} else {
|
||||
/* Longest payload */
|
||||
// XXX: yeah we don't support sending more than 4GB.
|
||||
o[1] = 127;
|
||||
o[2] = 0;
|
||||
o[3] = 0;
|
||||
o[4] = 0;
|
||||
o[5] = 0;
|
||||
o[6] = (char)((bytes_to_copy >> 24) & 0xFF);
|
||||
o[7] = (char)((bytes_to_copy >> 16) & 0xFF);
|
||||
o[8] = (char)((bytes_to_copy >> 8) & 0xFF);
|
||||
o[9] = (char)(bytes_to_copy & 0xFF);
|
||||
memcpy(&o[10], s, bytes_to_copy);
|
||||
}
|
||||
|
||||
/* Advance destination pointer and counter */
|
||||
@@ -403,6 +425,22 @@ int _websocket_create_packet(int opcode, char **buf, int *len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Create a websocket packet that is ready to be send.
|
||||
* This version takes into account stripping off \r and \n,
|
||||
* and possibly multi line due to labeled-response.
|
||||
* It is used for WSOP_TEXT and WSOP_BINARY.
|
||||
* The end result is one or more websocket frames,
|
||||
* all in a single packet *buf with size *len.
|
||||
*
|
||||
* This is the version that uses a static sendbuf buffer,
|
||||
* it is used from IRC websockets.
|
||||
*/
|
||||
int _websocket_create_packet(int opcode, char **buf, int *len)
|
||||
{
|
||||
static char sendbuf[WEBSOCKET_SEND_BUFFER_SIZE];
|
||||
return _websocket_create_packet_ex(opcode, buf, len, sendbuf, sizeof(sendbuf));
|
||||
}
|
||||
|
||||
/** Create and send a WSOP_PONG frame */
|
||||
int websocket_send_pong(Client *client, const char *buf, int len)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user