From: Brian Chrzanowski Date: Wed, 24 Jul 2024 02:15:23 +0000 (-0400) Subject: websocket: add option to disable auto-pong reply X-Git-Tag: curl-8_14_0~266 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=c0df01fd945f17d873ae24fa5ac359e5d1bb1d7f;p=thirdparty%2Fcurl.git websocket: add option to disable auto-pong reply This adds another bitflag on CURLOPT_WS_OPTIONS (CURLWS_NOAUTOPONG) that disables the default and automatic PONG reply in the WebSocket layer. Assisted-by: Calvin Ruocco Closes #16744 --- diff --git a/docs/internals/WEBSOCKET.md b/docs/internals/WEBSOCKET.md index 2f9f5cb24d..230bb91249 100644 --- a/docs/internals/WEBSOCKET.md +++ b/docs/internals/WEBSOCKET.md @@ -39,7 +39,8 @@ WebSocket with libcurl can be done two ways. The new options to `curl_easy_setopt()`: `CURLOPT_WS_OPTIONS` - to control specific behavior. `CURLWS_RAW_MODE` makes - libcurl provide all WebSocket traffic raw in the callback. + libcurl provide all WebSocket traffic raw in the callback. `CURLWS_NOAUTOPONG` + disables automatic `PONG` replies. The new function calls: diff --git a/docs/libcurl/curl_ws_meta.md b/docs/libcurl/curl_ws_meta.md index ed9e154722..1fd8b6641f 100644 --- a/docs/libcurl/curl_ws_meta.md +++ b/docs/libcurl/curl_ws_meta.md @@ -106,7 +106,8 @@ This is a ping message. It may contain up to 125 bytes of payload text. libcurl does not verify that the payload is valid UTF-8. Upon receiving a ping message, libcurl automatically responds with a pong -message unless the **CURLWS_RAW_MODE** bit of CURLOPT_WS_OPTIONS(3) is set. +message unless the **CURLWS_NOAUTOPONG** or **CURLWS_RAW_MODE** bit of +CURLOPT_WS_OPTIONS(3) is set. ## CURLWS_PONG diff --git a/docs/libcurl/libcurl-ws.md b/docs/libcurl/libcurl-ws.md index d8f013acf2..1ef3074d5d 100644 --- a/docs/libcurl/libcurl-ws.md +++ b/docs/libcurl/libcurl-ws.md @@ -88,7 +88,8 @@ unidirectional heartbeat. libcurl automatically responds to server PING messages with a PONG that echoes the payload of the PING message. libcurl does neither send any PING messages -nor any unsolicited PONG messages automatically. +nor any unsolicited PONG messages automatically. The automatic reply to PING +messages can be disabled through CURLOPT_WS_OPTIONS(3). # MODELS diff --git a/docs/libcurl/opts/CURLOPT_WS_OPTIONS.md b/docs/libcurl/opts/CURLOPT_WS_OPTIONS.md index 92760a9283..35ae17cf47 100644 --- a/docs/libcurl/opts/CURLOPT_WS_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_WS_OPTIONS.md @@ -44,6 +44,12 @@ callback. In raw mode, libcurl does not handle pings or any other frame for the application. +## CURLWS_NOAUTOPONG (2) + +Disable the automatic reply to PING messages. This means users must +send a PONG message with curl_ws_send(3). This feature is added with +version 8.14.0. + # DEFAULT 0 diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 1fcacb9848..66b0b7c27f 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -1155,6 +1155,7 @@ CURLWARNING 7.66.0 CURLWS_BINARY 7.86.0 CURLWS_CLOSE 7.86.0 CURLWS_CONT 7.86.0 +CURLWS_NOAUTOPONG 8.14.0 CURLWS_OFFSET 7.86.0 CURLWS_PING 7.86.0 CURLWS_PONG 7.86.0 diff --git a/include/curl/websockets.h b/include/curl/websockets.h index 6ef6a2bc92..afb86b4ebc 100644 --- a/include/curl/websockets.h +++ b/include/curl/websockets.h @@ -73,7 +73,8 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, unsigned int flags); /* bits for the CURLOPT_WS_OPTIONS bitmask: */ -#define CURLWS_RAW_MODE (1<<0) +#define CURLWS_RAW_MODE (1<<0) +#define CURLWS_NOAUTOPONG (1<<1) CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl); diff --git a/lib/setopt.c b/lib/setopt.c index 07ccb933f8..193ef66e53 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1384,7 +1384,8 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, #endif /* ! CURL_DISABLE_ALTSVC */ #ifndef CURL_DISABLE_WEBSOCKETS case CURLOPT_WS_OPTIONS: - data->set.ws_raw_mode = (bool)(arg & CURLWS_RAW_MODE); + data->set.ws_raw_mode = (bool)(arg & CURLWS_RAW_MODE); + data->set.ws_no_auto_pong = (bool)(arg & CURLWS_NOAUTOPONG); break; #endif case CURLOPT_QUICK_EXIT: diff --git a/lib/url.c b/lib/url.c index ba62b53328..473582ca40 100644 --- a/lib/url.c +++ b/lib/url.c @@ -480,6 +480,11 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) memset(&set->priority, 0, sizeof(set->priority)); #endif set->quick_exit = 0L; +#ifndef CURL_DISABLE_WEBSOCKETS + set->ws_raw_mode = FALSE; + set->ws_no_auto_pong = FALSE; +#endif + return result; } diff --git a/lib/urldata.h b/lib/urldata.h index 591e166cc8..e76d4a1043 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1802,6 +1802,7 @@ struct UserDefined { BIT(http09_allowed); /* allow HTTP/0.9 responses */ #ifndef CURL_DISABLE_WEBSOCKETS BIT(ws_raw_mode); + BIT(ws_no_auto_pong); #endif }; diff --git a/lib/ws.c b/lib/ws.c index d6ba74a440..3fb1d6edad 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -502,10 +502,12 @@ static ssize_t ws_cw_dec_next(const unsigned char *buf, size_t buflen, struct ws_cw_dec_ctx *ctx = user_data; struct Curl_easy *data = ctx->data; struct websocket *ws = ctx->ws; + bool auto_pong = !data->set.ws_no_auto_pong; curl_off_t remain = (payload_len - (payload_offset + buflen)); (void)frame_age; - if((frame_flags & CURLWS_PING) && !remain) { + + if(auto_pong && (frame_flags & CURLWS_PING) && !remain) { /* auto-respond to PINGs, only works for single-frame payloads atm */ size_t bytes; infof(data, "WS: auto-respond to PING with a PONG"); @@ -949,6 +951,8 @@ static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen, CURLcode *err) { struct ws_collect *ctx = userp; + struct Curl_easy *data = ctx->data; + bool auto_pong = !data->set.ws_no_auto_pong; size_t nwritten; curl_off_t remain = (payload_len - (payload_offset + buflen)); @@ -960,7 +964,7 @@ static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen, ctx->payload_len = payload_len; } - if((frame_flags & CURLWS_PING) && !remain) { + if(auto_pong && (frame_flags & CURLWS_PING) && !remain) { /* auto-respond to PINGs, only works for single-frame payloads atm */ size_t bytes; infof(ctx->data, "WS: auto-respond to PING with a PONG"); diff --git a/packages/OS400/curl.inc.in b/packages/OS400/curl.inc.in index 81ba82c358..9601b7b728 100644 --- a/packages/OS400/curl.inc.in +++ b/packages/OS400/curl.inc.in @@ -469,6 +469,8 @@ * d CURLWS_RAW_MODE... d c X'00000001' + d CURLWS_NOAUTOPONG... + d c X'00000002' * ************************************************************************** * Types diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 5193fc8102..8d83f11ed0 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -259,7 +259,7 @@ test2100 test2101 test2102 \ test2200 test2201 test2202 test2203 test2204 test2205 \ \ test2300 test2301 test2302 test2303 test2304 test2305 test2306 test2307 \ -test2308 test2309 test2310 test2311 \ +test2308 test2309 test2310 test2311 test2312 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 \ \ diff --git a/tests/data/test2312 b/tests/data/test2312 new file mode 100644 index 0000000000..964381de6e --- /dev/null +++ b/tests/data/test2312 @@ -0,0 +1,66 @@ + + + +WebSockets + + + +# +# Server-side + + +HTTP/1.1 101 Switching to WebSockets swsclose +Server: test-server/fake +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs= + +%hex[%89%00]hex% + +# allow upgrade + +upgrade + + + +# +# Client-side + +# for the forced CURL_ENTROPY + +debug +ws + + +http + + +WebSockets no auto ping + + +lib%TESTNUMBER + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: webbie-sox/3 +Accept: */* +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Version: 13 +Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ== + + + +0 + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index b93177f6fb..12ef21d9f4 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -74,7 +74,7 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq \ lib1960 lib1964 \ lib1970 lib1971 lib1972 lib1973 lib1974 lib1975 lib1977 lib1978 \ lib2301 lib2302 lib2304 lib2305 lib2306 lib2308 lib2309 lib2310 \ - lib2311 \ + lib2311 lib2312 \ lib2402 lib2404 lib2405 \ lib2502 \ lib3010 lib3025 lib3026 lib3027 \ @@ -713,6 +713,9 @@ lib2310_LDADD = $(TESTUTIL_LIBS) lib2311_SOURCES = lib2311.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) $(MULTIBYTE) lib2311_LDADD = $(TESTUTIL_LIBS) +lib2312_SOURCES = lib2312.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) $(MULTIBYTE) +lib2312_LDADD = $(TESTUTIL_LIBS) + lib2402_SOURCES = lib2402.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib2402_LDADD = $(TESTUTIL_LIBS) diff --git a/tests/libtest/lib2312.c b/tests/libtest/lib2312.c new file mode 100644 index 0000000000..53998d8369 --- /dev/null +++ b/tests/libtest/lib2312.c @@ -0,0 +1,102 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "test.h" + +#ifdef USE_WEBSOCKETS + +struct ping_check { + CURL *curl; + int pinged; +}; + +static size_t write_cb(char *b, size_t size, size_t nitems, void *p) +{ + struct ping_check *ping_check = p; + CURL *curl = ping_check->curl; + const struct curl_ws_frame *frame = curl_ws_meta(curl); + size_t sent = 0; + size_t i = 0; + + /* upon ping, respond with input data, disconnect, mark a success */ + if(frame->flags & CURLWS_PING) { + fprintf(stderr, "write_cb received ping with %zd bytes\n", + size * nitems); + fprintf(stderr, "\n"); + for(i = 0; i < size * nitems; i++) { + fprintf(stderr, "%02X%s", (int)b[i], + (i % 10 == 0 && i != 0) ? "\n" : " "); + } + fprintf(stderr, "\n"); + fprintf(stderr, "write_cb sending pong response\n"); + curl_ws_send(curl, b, size * nitems, &sent, 0, CURLWS_PONG); + fprintf(stderr, "write_cb closing websocket\n"); + curl_ws_send(curl, NULL, 0, &sent, 0, CURLWS_CLOSE); + ping_check->pinged = 1; + } + else { + fprintf(stderr, "ping_check_cb: non-ping message, frame->flags %x\n", + frame->flags); + } + + return size * nitems; +} + +CURLcode test(char *URL) +{ + CURL *curl; + CURLcode res = CURLE_OK; + struct ping_check state; + + global_init(CURL_GLOBAL_ALL); + + curl = curl_easy_init(); + if(curl) { + state.curl = curl; + state.pinged = 0; + + curl_easy_setopt(curl, CURLOPT_URL, URL); + + /* use the callback style, without auto-pong */ + curl_easy_setopt(curl, CURLOPT_USERAGENT, "webbie-sox/3"); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_WS_OPTIONS, (long)CURLWS_NOAUTOPONG); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &state); + + res = curl_easy_perform(curl); + fprintf(stderr, "curl_easy_perform() returned %u\n", (int)res); + + res = state.pinged ? 0 : 1; + + /* always cleanup */ + curl_easy_cleanup(curl); + } + curl_global_cleanup(); + return res; +} + +#else /* no websockets */ +NO_SUPPORT_BUILT_IN +#endif