examples/websocket.c: check for CURLE_AGAIN on recv_pong()

The example file docs/examples/websocket.c does not work out of the
box, because the recv_pong() function does not consider that the
curl_ws_recv() function might return CURLE_AGAIN if there is no data
to read, causing the program to terminate prematurely.

This commit addresses this by using select(2) to make receiving pong
blocking, and thus being able to continue with the main loop.

This also occurs in the example file sendrecv.c, which also makes use
of select(2) for sending and receiving data, which I based on for
this commit.
This commit is contained in:
Davidson Francis 2024-02-23 22:13:28 -03:00 committed by BIOS
parent f274fc5c68
commit e8d0cd0e95

View file

@ -30,6 +30,39 @@
#include <unistd.h>
#include <curl/curl.h>
/* Auxiliary function that waits on the socket. */
static int wait_on_socket(curl_socket_t sockfd, long timeout_ms)
{
struct timeval tv;
fd_set infd, outfd, errfd;
int res;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (int)(timeout_ms % 1000) * 1000;
FD_ZERO(&infd);
FD_ZERO(&outfd);
FD_ZERO(&errfd);
/* Avoid this warning with pre-2020 Cygwin/MSYS releases:
* warning: conversion to 'long unsigned int' from 'curl_socket_t' {aka 'int'}
* may change the sign of the result [-Wsign-conversion]
*/
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
FD_SET(sockfd, &errfd); /* always check for error */
FD_SET(sockfd, &infd);
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
/* select() returns the number of signalled sockets or -1 */
res = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv);
return res;
}
static int ping(CURL *curl, const char *send_payload)
{
size_t sent;
@ -44,27 +77,47 @@ static int recv_pong(CURL *curl, const char *expected_payload)
size_t rlen;
const struct curl_ws_frame *meta;
char buffer[256];
CURLcode result = curl_ws_recv(curl, buffer, sizeof(buffer), &rlen, &meta);
if(!result) {
if(meta->flags & CURLWS_PONG) {
int same = 0;
fprintf(stderr, "ws: got PONG back\n");
if(rlen == strlen(expected_payload)) {
if(!memcmp(expected_payload, buffer, rlen)) {
fprintf(stderr, "ws: got the same payload back\n");
same = 1;
}
}
if(!same)
fprintf(stderr, "ws: did NOT get the same payload back\n");
}
else {
fprintf(stderr, "recv_pong: got %u bytes rflags %x\n", (int)rlen,
meta->flags);
}
CURLcode result;
curl_socket_t sockfd;
result = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd);
if(result != CURLE_OK) {
printf("Error: %s\n", curl_easy_strerror(result));
return 1;
}
fprintf(stderr, "ws: curl_ws_recv returned %u, received %u\n",
(unsigned int)result, (unsigned int)rlen);
do {
result = curl_ws_recv(curl, buffer, sizeof(buffer), &rlen, &meta);
if(result == CURLE_AGAIN && !wait_on_socket(sockfd, 1000)) {
printf("Error: timeout.\n");
break;
}
} while (result == CURLE_AGAIN);
if(result != CURLE_OK) {
fprintf(stderr, "ws: curl_ws_recv returned %u, received %u\n",
(unsigned int)result, (unsigned int)rlen);
return (int)result;
}
if(meta->flags & CURLWS_PONG) {
int same = 0;
fprintf(stderr, "ws: got PONG back\n");
if(rlen == strlen(expected_payload)) {
if(!memcmp(expected_payload, buffer, rlen)) {
fprintf(stderr, "ws: got the same payload back\n");
same = 1;
}
}
if(!same)
fprintf(stderr, "ws: did NOT get the same payload back\n");
}
else {
fprintf(stderr, "recv_pong: got %u bytes rflags %x\n", (int)rlen,
meta->flags);
}
return (int)result;
}