ws: tests and fixes

This started out as regression tests for the `curl_ws_recv()` and
`curl_ws_send()` implementation and ended up with a bugfix, additional
protocol validation and minor logging improvements.

- Fix reset of fragmented message decoder state when a PING/PONG is
  received in between message fragments.

- Fix undefined behavior (applying zero offset to null pointer) in
  curl_ws_send() when the given buffer is NULL.

- Detect invalid overlong PING/PONG/CLOSE frames.
- Detect invalid fragmented PING/PONG/CLOSE frames.
- Detect invalid sequences of fragmented frames.

  - a) A continuation frame (0x80...) is received without any ongoing
    fragmented message.
  - b) A new fragmented message is started (0x81/0x01/0x82/0x02...)
    before the ongoing fragmented message has terminated.

- Made logs for invalid opcodes easier to understand.
- Moved noisy logs to the `CURL_TRC_WS` log level.
- Unified the prefixes for WebSocket log messages: `[WS] ...`

- Add env var `CURL_WS_FORCE_ZERO_MASK` in debug builds.

  - If set, it forces the bit mask applied to outgoing payloads to
    0x00000000, which effectively means the payload is not masked at
    all. This drastically simplifies defining the expected `<protocol>`
    data in test cases.

- 2700: Frame types
- 2701: Invalid opcode 0x3
- 2702: Invalid opcode 0xB
- 2703: Invalid reserved bit RSV1 _(replaces 2310)_
- 2704: Invalid reserved bit RSV2
- 2705: Invalid reserved bit RSV3
- 2706: Invalid masked server message
- 2707: Peculiar frame sizes _(part. replaces 2311)_
- 2708: Automatic PONG
- 2709: No automatic PONG _(replaces 2312)_
- 2710: Unsolicited PONG
- 2711: Empty PING/PONG/CLOSE
- 2712: Max sized PING/PONG/CLOSE
- 2713: Invalid oversized PING _(replaces 2307)_
- 2714: Invalid oversized PONG
- 2715: Invalid oversized CLOSE
- 2716: Invalid fragmented PING
- 2717: Invalid fragmented PONG
- 2718: Invalid fragmented CLOSE
- 2719: Fragmented messages _(part. replaces 2311)_
- 2720: Fragmented messages with empty fragments
- 2721: Fragmented messages with interleaved pong
- 2722: Invalid fragmented message without initial frame
- 2723: Invalid fragmented message without final frame

- 2305: curl_ws_recv() loop reading three larger frames
  - This test involuntarily sent an invalid sequence of opcodes (0x01...,0x01...,0x81...) , but neither libcurl nor the test caught this! The correct sequence was tested in 2311 (0x01...,0x00...,0x80...). See below for 2311.
  - Validation of the opcode sequence was added to libcurl and is now tested in 2723.
  - Superseded by 2719 (fragmented message) and 2707 (large frames).
- 2307: overlong PING payload
  - The tested PING payload length check was actually missing, but the test didn't catch this since it involuntarily sent an invalid opcode (0x19... instead of 0x89...) so that the expected error occurred, but for the wrong reason.
  - Superseded by 2713.
- 2310: unknown reserved bit set in frame header
  - Superseded by 2703 and extended by 2704 and 2705.
- 2311: curl_ws_recv() read fragmented message
  - Superseded by 2719 (fragmented message) and 2707 (large frames).
- 2312: WebSockets no auto ping
  - Superseded by 2709.

- No tests for `CURLOPT_WRITEFUNCTION`.
- No tests for sending of invalid frames/fragments.

Closes #17136
This commit is contained in:
Ruocco, Calvin 2025-04-14 17:59:45 +02:00 committed by Daniel Stenberg
parent 04c3895ceb
commit d3594be653
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
41 changed files with 2224 additions and 828 deletions

View file

@ -258,8 +258,8 @@ test2100 test2101 test2102 \
\
test2200 test2201 test2202 test2203 test2204 test2205 \
\
test2300 test2301 test2302 test2303 test2304 test2305 test2306 test2307 \
test2308 test2309 test2310 test2311 test2312 \
test2300 test2301 test2302 test2303 test2304 test2306 \
test2308 test2309 \
\
test2400 test2401 test2402 test2403 test2404 test2405 test2406 \
\
@ -267,6 +267,10 @@ test2500 test2501 test2502 test2503 \
\
test2600 test2601 test2602 test2603 test2604 \
\
test2700 test2701 test2702 test2703 test2704 test2705 test2706 test2707 \
test2708 test2709 test2710 test2711 test2712 test2713 test2714 test2715 \
test2716 test2717 test2718 test2719 test2720 test2721 test2722 test2723 \
\
test3000 test3001 test3002 test3003 test3004 test3005 test3006 test3007 \
test3008 test3009 test3010 test3011 test3012 test3013 test3014 test3015 \
test3016 test3017 test3018 test3019 test3020 test3021 test3022 test3023 \

View file

@ -6,17 +6,16 @@ WebSockets
</info>
#
# Sends a PING + a 5 byte hello TEXT
# Close connection during websocket upgrade
<reply>
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets swsclose
Server: test-server/fake
Upgrade: websocket
Connection: Upgrade
Something: else
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%89%00%81%05hello]hex%
HTTP/1.1 101 Switching to WebSockets swsclose
Server: test-server/fake
Upgrade: websocket
Connection: Upgrade
Something: else
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
</data>
# allow upgrade
<servercmd>
@ -53,7 +52,7 @@ ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
<protocol nocheck="yes" crlf="yes">
GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: websocket/2304
User-Agent: websocket/%TESTNUMBER
Accept: */*
Upgrade: websocket
Connection: Upgrade

View file

@ -1,59 +0,0 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
#
# Sends three 4097 bytes TEXT frames, as one single message
<reply>
<data nocheck="yes">
HTTP/1.1 101 Switching to WebSockets
Server: test-server/fake
Upgrade: websocket
Connection: Upgrade
Something: else
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%01%7e%10%01]hex%%repeat[256 x helothisisdaniel]%
%hex[%01%7e%10%01]hex%%repeat[256 x helothisisdaniel]%
%hex[%81%7e%10%01]hex%%repeat[256 x helothisisdaniel]%
</data>
# allow upgrade
<servercmd>
upgrade
</servercmd>
</reply>
#
# Client-side
<client>
# require debug for the forced CURL_ENTROPY
<features>
Debug
ws
</features>
<server>
http
</server>
<name>
WebSocket curl_ws_recv() loop reading three larger frames
</name>
<tool>
lib%TESTNUMBER
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER %LOGDIR/save%TESTNUMBER
</command>
</client>
#
<verify>
<file name="%LOGDIR/save%TESTNUMBER" mode="text">
%repeat[256 x helothisisdaniel]%
%repeat[256 x helothisisdaniel]%
%repeat[256 x helothisisdaniel]%
</file>
</verify>
</testcase>

View file

@ -1,70 +0,0 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
#
# Sends a PING with overlong payload
<reply>
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: test-server/fake
Upgrade: websocket
Connection: Upgrade
Something: else
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%19%7f%ff%30%30%30%30%30%30%30%30%30%30%30%30]hex%
</data>
# allow upgrade
<servercmd>
upgrade
</servercmd>
</reply>
#
# Client-side
<client>
# require debug for the forced CURL_ENTROPY
<features>
Debug
ws
</features>
<server>
http
</server>
<name>
WebSockets, overlong PING payload
</name>
<tool>
lib2302
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
</client>
#
# PONG with no data and the 32 bit mask
#
<verify>
<protocol nocheck="yes" nonewline="yes">
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==
</protocol>
# 56 == CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
</verify>
</testcase>

View file

@ -1,68 +0,0 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
#
# Sends a PING + a TEXT with RSV1 set
<reply>
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: test-server/fake
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%89%00%C1%05hello]hex%
</data>
# allow upgrade
<servercmd>
upgrade
</servercmd>
</reply>
#
# Client-side
<client>
# require debug for the forced CURL_ENTROPY
<features>
Debug
ws
</features>
<server>
http
</server>
<name>
WebSockets unknown reserved bit set in frame header
</name>
<tool>
lib%TESTNUMBER
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
</client>
#
# PONG with no data and the 32 bit mask
#
<verify>
<protocol nocheck="yes" nonewline="yes">
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==
%hex[%8a%808321]hex%
</protocol>
<stdout mode="text">
Returned 56, should be 56.
</stdout>
</verify>
</testcase>

View file

@ -1,59 +0,0 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
#
# Sends three 4097 bytes TEXT frames, as fragments of one 12291 bytes message
<reply>
<data nocheck="yes">
HTTP/1.1 101 Switching to WebSockets
Server: test-server/fake
Upgrade: websocket
Connection: Upgrade
Something: else
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%01%7e%10%01]hex%%repeat[256 x helothisisdaniel]%
%hex[%00%7e%10%01]hex%%repeat[256 x helothisisdaniel]%
%hex[%80%7e%10%01]hex%%repeat[256 x helothisisdaniel]%
</data>
# allow upgrade
<servercmd>
upgrade
</servercmd>
</reply>
#
# Client-side
<client>
# require debug for the forced CURL_ENTROPY
<features>
Debug
ws
</features>
<server>
http
</server>
<name>
WebSocket curl_ws_recv() read fragmented message
</name>
<tool>
lib%TESTNUMBER
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER %LOGDIR/save%TESTNUMBER
</command>
</client>
#
<verify>
<file name="%LOGDIR/save%TESTNUMBER" mode="text">
%repeat[256 x helothisisdaniel]%
%repeat[256 x helothisisdaniel]%
%repeat[256 x helothisisdaniel]%
</file>
</verify>
</testcase>

View file

@ -1,66 +0,0 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
#
# Server-side
<reply>
<data nocheck="yes" nonewline="yes">
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%
</data>
# allow upgrade
<servercmd>
upgrade
</servercmd>
</reply>
#
# Client-side
<client>
# for the forced CURL_ENTROPY
<features>
debug
ws
</features>
<server>
http
</server>
<name>
WebSockets no auto ping
</name>
<tool>
lib%TESTNUMBER
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<protocol nocheck="yes">
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==
</protocol>
<errorcode>
0
</errorcode>
</verify>
</testcase>

76
tests/data/test2700 Normal file
View file

@ -0,0 +1,76 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Frame types
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# A TEXT/BINARY/PING/PONG/CLOSE message with payload
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%81%03txt]hex%%hex[%82%03bin]hex%%hex[%89%04ping]hex%%hex[%8a%04pong]hex%%hex[%88%07%03%e8close]hex%
</data>
</reply>
<verify>
# Exact echo of reply data with additional masking
<protocol nonewline="yes">
%hex[%81%83%00%00%00%00txt]hex%%hex[%82%83%00%00%00%00bin]hex%%hex[%89%84%00%00%00%00ping]hex%%hex[%8a%84%00%00%00%00pong]hex%%hex[%88%87%00%00%00%00%03%e8close]hex%
</protocol>
# PING is handled by lib and never given to application
<stdout>
txt fin [3] txt
bin fin [3] bin
ping [4] ping
pong [4] pong
close [7] %hex[%03%e8]hex%close
</stdout>
# CURLE_OK
<errorcode>
0
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2701 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid opcode 0x3
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# An empty frame with the reserved opcode 0x3
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%83%00]hex%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2702 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid opcode 0xB
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# An empty frame with the reserved opcode 0xB
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%8b%00]hex%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2703 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid reserved bit RSV1
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# An empty frame with the RSV1 bit set
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%C1%00]hex%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2704 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid reserved bit RSV2
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# An empty frame with the RSV2 bit set
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%A1%00]hex%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2705 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid reserved bit RSV3
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# An empty frame with the RSV3 bit set
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%91%00]hex%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2706 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid masked server message
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# An empty frame with masking
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%81%80%12%34%56%78]hex%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

84
tests/data/test2707 Normal file
View file

@ -0,0 +1,84 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Peculiar frame sizes
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# Frames with sizes around special cases of the frame encoding
# see https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
# - 0: empty frame
# - 125: largest payload with 7-bit encoding
# - 126: smallest payload with 16-bit encoding AND this value in first byte indicates 16-bit encoding
# - 127: this value in first byte indicates 64-bit encoding
# - 65535: largest payload with 16-bit length encoding
# - 65536: smallest payload with 64-bit length encoding
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%81%00]hex%%hex[%81%7d]hex%%repeat[125 x _]%%hex[%81%7e%00%7e]hex%%repeat[126 x _]%%hex[%81%7e%00%7f]hex%%repeat[127 x _]%%hex[%81%7e%ff%ff]hex%%repeat[65535 x _]%%hex[%81%7f%00%00%00%00%00%01%00%00]hex%%repeat[65536 x _]%%hex[%88%00]hex%
</data>
</reply>
<verify>
# Exact echo of reply data with additional masking
<protocol nonewline="yes">
%hex[%81%80%00%00%00%00]hex%%hex[%81%fd%00%00%00%00]hex%%repeat[125 x _]%%hex[%81%fe%00%7e%00%00%00%00]hex%%repeat[126 x _]%%hex[%81%fe%00%7f%00%00%00%00]hex%%repeat[127 x _]%%hex[%81%fe%ff%ff%00%00%00%00]hex%%repeat[65535 x _]%%hex[%81%ff%00%00%00%00%00%01%00%00%00%00%00%00]hex%%repeat[65536 x _]%%hex[%88%80%00%00%00%00]hex%
</protocol>
<stdout>
txt fin [0]
txt fin [125] %repeat[125 x _]%
txt fin [126] %repeat[126 x _]%
txt fin [127] %repeat[127 x _]%
txt fin [65535] %repeat[65535 x _]%
txt fin [65536] %repeat[65536 x _]%
close [0]
</stdout>
# CURLE_OK
<errorcode>
0
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

75
tests/data/test2708 Normal file
View file

@ -0,0 +1,75 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Automatic PONG
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
LIB2700_AUTO_PONG=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# PING "test"
# CLOSE
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%89%04test]hex%%hex[%88%00]hex%
</data>
</reply>
<verify>
# PONG "test" (payload MUST equal ping payload)
# see https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
# CLOSE
<protocol nonewline="yes">
%hex[%8A%84%00%00%00%00test%88%80%00%00%00%00]hex%
</protocol>
# PING is handled by lib and never given to application
<stdout>
close [0]
</stdout>
# CURLE_OK
<errorcode>
0
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

73
tests/data/test2709 Normal file
View file

@ -0,0 +1,73 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: No automatic PONG
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# PING "test"
# CLOSE
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%89%04test]hex%%hex[%88%00]hex%
</data>
</reply>
<verify>
# Exact echo of reply data with additional masking
<protocol nonewline="yes">
%hex[%89%84%00%00%00%00test]hex%%hex[%88%80%00%00%00%00]hex%
</protocol>
# PING is given to application
<stdout>
ping [4] test
close [0]
</stdout>
# CURLE_OK
<errorcode>
0
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

73
tests/data/test2710 Normal file
View file

@ -0,0 +1,73 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Unsolicited PONG
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# An unsolicited PONG with and without payload
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%8a%00]hex%%hex[%8a%04pong]hex%%hex[%88%00]hex%
</data>
</reply>
<verify>
# Exact echo of reply data with additional masking
<protocol nonewline="yes">
%hex[%8a%80%00%00%00%00]hex%%hex[%8a%84%00%00%00%00pong]hex%%hex[%88%80%00%00%00%00]hex%
</protocol>
<stdout>
pong [0]
pong [4] pong
close [0]
</stdout>
# CURLE_OK
<errorcode>
0
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

74
tests/data/test2711 Normal file
View file

@ -0,0 +1,74 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Empty PING/PONG/CLOSE
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# A PING/PONG/CLOSE message without payload
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%89%00]hex%%hex[%8a%00]hex%%hex[%88%00]hex%
</data>
</reply>
<verify>
# Exact echo of reply data with additional masking
<protocol nonewline="yes">
%hex[%89%80%00%00%00%00]hex%%hex[%8a%80%00%00%00%00]hex%%hex[%88%80%00%00%00%00]hex%
</protocol>
# PING is handled by lib and never given to application
<stdout>
ping [0]
pong [0]
close [0]
</stdout>
# CURLE_OK
<errorcode>
0
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

74
tests/data/test2712 Normal file
View file

@ -0,0 +1,74 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Max sized PING/PONG/CLOSE
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# A PING/PONG/CLOSE with 125 bytes payload each
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%89%7d]hex%%repeat[125 x _]%%hex[%8a%7d]hex%%repeat[125 x _]%%hex[%88%7d%03%e8]hex%%repeat[123 x _]%
</data>
</reply>
<verify>
# Exact echo of reply data with additional masking
<protocol nonewline="yes">
%hex[%89%fd%00%00%00%00]hex%%repeat[125 x _]%%hex[%8a%fd%00%00%00%00]hex%%repeat[125 x _]%%hex[%88%fd%00%00%00%00%03%e8]hex%%repeat[123 x _]%
</protocol>
# PING is handled by lib and never given to application
<stdout>
ping [125] %repeat[125 x _]%
pong [125] %repeat[125 x _]%
close [125] %hex[%03%e8]hex%%repeat[123 x _]%
</stdout>
# CURLE_OK
<errorcode>
0
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2713 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid oversized PING
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# A 126 byte long PING
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%89%7e%00%7e]hex%%repeat[126 x _]%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2714 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid oversized PONG
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# A 126 byte long PONG
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%8a%7e%00%7e]hex%%repeat[126 x _]%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2715 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid oversized CLOSE
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# A 126 byte long CLOSE
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%88%7e%00%7e%03%e8]hex%%repeat[124 x _]%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2716 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid fragmented PING
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# A fragmented PING
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%09%02p1]hex%%hex[%80%02p2]hex%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2717 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid fragmented PONG
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# A fragmented PONG
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%0a%02p1]hex%%hex[%80%02p2]hex%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

72
tests/data/test2718 Normal file
View file

@ -0,0 +1,72 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid fragmented CLOSE
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# A fragmented CLOSE
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%08%02p1]hex%%hex[%80%02p2]hex%
</data>
</reply>
<verify>
# No frames
<protocol nonewline="yes">
</protocol>
# No frames
<stdout nonewline="yes">
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

81
tests/data/test2719 Normal file
View file

@ -0,0 +1,81 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Fragmented messages
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# Fragmented TEXT/BINARY messages, each with 2/3 fragments
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%01%02t1]hex%%hex[%80%02t2]hex%%hex[%01%02t1]hex%%hex[%00%02t2]hex%%hex[%80%02t3]hex%%hex[%02%02b1]hex%%hex[%80%02b2]hex%%hex[%02%02b1]hex%%hex[%00%02b2]hex%%hex[%80%02b3]hex%%hex[%88%00]hex%
</data>
</reply>
<verify>
# Exact echo of reply data with additional masking
<protocol nonewline="yes">
%hex[%01%82%00%00%00%00t1]hex%%hex[%80%82%00%00%00%00t2]hex%%hex[%01%82%00%00%00%00t1]hex%%hex[%00%82%00%00%00%00t2]hex%%hex[%80%82%00%00%00%00t3]hex%%hex[%02%82%00%00%00%00b1]hex%%hex[%80%82%00%00%00%00b2]hex%%hex[%02%82%00%00%00%00b1]hex%%hex[%00%82%00%00%00%00b2]hex%%hex[%80%82%00%00%00%00b3]hex%%hex[%88%80%00%00%00%00]hex%
</protocol>
<stdout>
txt --- [2] t1
txt fin [2] t2
txt --- [2] t1
txt --- [2] t2
txt fin [2] t3
bin --- [2] b1
bin fin [2] b2
bin --- [2] b1
bin --- [2] b2
bin fin [2] b3
close [0]
</stdout>
# CURLE_OK
<errorcode>
0
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

86
tests/data/test2720 Normal file
View file

@ -0,0 +1,86 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Fragmented messages with empty fragments
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# 1st a message with an emtpy fragment at the beginning
# 2nd a message with an emtpy fragment in the middle
# 3rd a message with an emtpy fragment at the end
# 4th a message with only empty fragments
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%01%00]hex%%hex[%00%02a1]hex%%hex[%80%02a2]hex%%hex[%01%02b1]hex%%hex[%00%00]hex%%hex[%80%02b2]hex%%hex[%01%02c1]hex%%hex[%00%02c2]hex%%hex[%80%00]hex%%hex[%01%00]hex%%hex[%00%00]hex%%hex[%80%00]hex%%hex[%88%00]hex%
</data>
</reply>
<verify>
# Exact echo of reply data with additional masking
<protocol nonewline="yes">
%hex[%01%80%00%00%00%00]hex%%hex[%00%82%00%00%00%00a1]hex%%hex[%80%82%00%00%00%00a2]hex%%hex[%01%82%00%00%00%00b1]hex%%hex[%00%80%00%00%00%00]hex%%hex[%80%82%00%00%00%00b2]hex%%hex[%01%82%00%00%00%00c1]hex%%hex[%00%82%00%00%00%00c2]hex%%hex[%80%80%00%00%00%00]hex%%hex[%01%80%00%00%00%00]hex%%hex[%00%80%00%00%00%00]hex%%hex[%80%80%00%00%00%00]hex%%hex[%88%80%00%00%00%00]hex%
</protocol>
<stdout>
txt --- [0]
txt --- [2] a1
txt fin [2] a2
txt --- [2] b1
txt --- [0]
txt fin [2] b2
txt --- [2] c1
txt --- [2] c2
txt fin [0]
txt --- [0]
txt --- [0]
txt fin [0]
close [0]
</stdout>
# CURLE_OK
<errorcode>
0
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

77
tests/data/test2721 Normal file
View file

@ -0,0 +1,77 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Fragmented messages with interleaved pong
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# A TEXT/BINARY message fragmented into two frames each, with pongs in the middle
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%01%02t1]hex%%hex[%8a%02p1]hex%%hex[%80%02t2]hex%%hex[%02%02b1]hex%%hex[%8a%02p2]hex%%hex[%80%02b2]hex%%hex[%88%00]hex%
</data>
</reply>
<verify>
# Exact echo of reply data with additional masking
<protocol nonewline="yes">
%hex[%01%82%00%00%00%00t1]hex%%hex[%8a%82%00%00%00%00p1]hex%%hex[%80%82%00%00%00%00t2]hex%%hex[%02%82%00%00%00%00b1]hex%%hex[%8a%82%00%00%00%00p2]hex%%hex[%80%82%00%00%00%00b2]hex%%hex[%88%80%00%00%00%00]hex%
</protocol>
<stdout>
txt --- [2] t1
pong [2] p1
txt fin [2] t2
bin --- [2] b1
pong [2] p2
bin fin [2] b2
close [0]
</stdout>
# CURLE_OK
<errorcode>
0
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

73
tests/data/test2722 Normal file
View file

@ -0,0 +1,73 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid fragmented message without initial frame
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# First a valid BINARY frame
# Second a fragmented message with the first frame missing
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%82%01b]hex%%hex[%00%02m2]hex%%hex[%80%02m3]hex%
</data>
</reply>
<verify>
# Echo of reply data with additional masking up to the missing initial frame
<protocol nonewline="yes">
%hex[%82%81%00%00%00%00b]hex%
</protocol>
# Only frames up to the missing initial frame
<stdout>
bin fin [1] b
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

74
tests/data/test2723 Normal file
View file

@ -0,0 +1,74 @@
<testcase>
<info>
<keywords>
WebSockets
</keywords>
</info>
<client>
<name>
ws: Invalid fragmented message without final frame
</name>
<features>
Debug
ws
</features>
<server>
http
</server>
<tool>
lib2700
</tool>
<command>
ws://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
<setenv>
CURL_WS_FORCE_ZERO_MASK=1
</setenv>
</client>
<reply>
<servercmd>
upgrade
</servercmd>
# Full list of frames: see <verify.stdout> below
# First a fragmented TEXT message with the last frame missing
# Second a valid BINARY frame
<data nocheck="yes" nonewline="yes">
HTTP/1.1 101 Switching to WebSockets
Server: server/%TESTNUMBER
Upgrade: Websocket
Connection: Upgrade
Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs=
%hex[%01%02t1]hex%%hex[%00%02t2]hex%%hex[%82%01b]hex%
</data>
</reply>
<verify>
# Echo of reply data with additional masking up to the missing final frame
<protocol nonewline="yes">
%hex[%01%82%00%00%00%00t1]hex%%hex[%00%82%00%00%00%00t2]hex%
</protocol>
# Only frames up to the missing final frame
<stdout>
txt --- [2] t1
txt --- [2] t2
</stdout>
# CURLE_RECV_ERROR
<errorcode>
56
</errorcode>
# Strip HTTP header from <protocol>
<strip>
^GET /.*
^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .*
^\s*$
</strip>
</verify>
</testcase>

View file

@ -124,7 +124,7 @@ class TestWebsockets:
pytest.skip(f'example client not built: {client.name}')
url = f'ws://localhost:{env.ws_port}/'
r = client.run(args=[url, payload])
r.check_exit_code(56)
r.check_exit_code(100) # CURLE_TOO_LARGE
def test_20_04_data_small(self, env: Env, ws_echo):
client = LocalClient(env=env, name='ws-data')

View file

@ -75,10 +75,10 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq \
lib1945 lib1946 lib1947 lib1948 lib1955 lib1956 lib1957 lib1958 lib1959 \
lib1960 lib1964 \
lib1970 lib1971 lib1972 lib1973 lib1974 lib1975 lib1977 lib1978 \
lib2301 lib2302 lib2304 lib2305 lib2306 lib2308 lib2309 lib2310 \
lib2311 lib2312 \
lib2301 lib2302 lib2304 lib2306 lib2308 lib2309 \
lib2402 lib2404 lib2405 \
lib2502 \
lib2700 \
lib3010 lib3025 lib3026 lib3027 \
lib3100 lib3101 lib3102 lib3103 lib3104 lib3105 lib3207 lib3208
@ -700,9 +700,6 @@ lib2302_LDADD = $(TESTUTIL_LIBS)
lib2304_SOURCES = lib2304.c $(SUPPORTFILES)
lib2304_LDADD = $(TESTUTIL_LIBS)
lib2305_SOURCES = lib2305.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) $(MULTIBYTE)
lib2305_LDADD = $(TESTUTIL_LIBS)
lib2306_SOURCES = lib2306.c $(SUPPORTFILES)
lib2306_LDADD = $(TESTUTIL_LIBS)
@ -712,15 +709,6 @@ lib2308_LDADD = $(TESTUTIL_LIBS)
lib2309_SOURCES = lib2309.c $(SUPPORTFILES)
lib2309_LDADD = $(TESTUTIL_LIBS)
lib2310_SOURCES = lib2310.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) $(MULTIBYTE)
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)
@ -733,6 +721,9 @@ lib2405_LDADD = $(TESTUTIL_LIBS)
lib2502_SOURCES = lib2502.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) $(WARNLESS)
lib2502_LDADD = $(TESTUTIL_LIBS)
lib2700_SOURCES = lib2700.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) $(MULTIBYTE)
lib2700_LDADD = $(TESTUTIL_LIBS)
lib3010_SOURCES = lib3010.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib3010_LDADD = $(TESTUTIL_LIBS)

View file

@ -1,109 +0,0 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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"
#include "testtrace.h"
#include "memdebug.h"
#ifndef CURL_DISABLE_WEBSOCKETS
/* just close the connection */
static void websocket_close(CURL *curl)
{
size_t sent;
CURLcode result =
curl_ws_send(curl, "", 0, &sent, 0, CURLWS_CLOSE);
curl_mfprintf(stderr,
"ws: curl_ws_send returned %d, sent %d\n", result, (int)sent);
}
static void websocket(CURL *curl)
{
char buffer[256];
const struct curl_ws_frame *meta;
size_t nread;
size_t i = 0;
FILE *save = fopen(libtest_arg2, FOPEN_WRITETEXT);
if(!save)
return;
/* Three 4097-bytes frames are expected, 12291 bytes */
while(i < 12291) {
CURLcode result =
curl_ws_recv(curl, buffer, sizeof(buffer), &nread, &meta);
if(result) {
if(result == CURLE_AGAIN)
/* crude busy-loop */
continue;
fclose(save);
curl_mprintf("curl_ws_recv returned %d\n", result);
return;
}
curl_mprintf("%d: nread %zu Age %d Flags %x "
"Offset %" CURL_FORMAT_CURL_OFF_T " "
"Bytesleft %" CURL_FORMAT_CURL_OFF_T "\n",
(int)i,
nread, meta->age, meta->flags, meta->offset, meta->bytesleft);
i += meta->len;
fwrite(buffer, 1, nread, save);
}
fclose(save);
websocket_close(curl);
}
CURLcode test(char *URL)
{
CURL *curl;
CURLcode res = CURLE_OK;
global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, URL);
/* use the callback style */
curl_easy_setopt(curl, CURLOPT_USERAGENT, "websocket/2304");
libtest_debug_config.nohex = 1;
libtest_debug_config.tracetime = 1;
curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &libtest_debug_config);
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */
res = curl_easy_perform(curl);
curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", res);
if(res == CURLE_OK)
websocket(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return res;
}
#else
NO_SUPPORT_BUILT_IN
#endif

View file

@ -1,68 +0,0 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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"
#include "testtrace.h"
#include "memdebug.h"
#ifndef CURL_DISABLE_WEBSOCKETS
static size_t writecb(char *b, size_t size, size_t nitems, void *p)
{
(void)b;
(void)size;
(void)nitems;
(void)p;
return 0;
}
CURLcode test(char *URL)
{
CURL *curl;
CURLcode res = CURLE_OK;
global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, URL);
/* use the callback style */
curl_easy_setopt(curl, CURLOPT_USERAGENT, "webbie-sox/3");
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl);
res = curl_easy_perform(curl);
curl_mprintf("Returned %d, should be %d.\n", res, CURLE_RECV_ERROR);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return CURLE_OK;
}
#else
NO_SUPPORT_BUILT_IN
#endif

View file

@ -1,127 +0,0 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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"
#include "testtrace.h"
#include "memdebug.h"
#ifndef CURL_DISABLE_WEBSOCKETS
/* just close the connection */
static void websocket_close(CURL *curl)
{
size_t sent;
CURLcode result =
curl_ws_send(curl, "", 0, &sent, 0, CURLWS_CLOSE);
curl_mfprintf(stderr,
"ws: curl_ws_send returned %d, sent %zu\n", result, sent);
}
static void websocket_frame(CURL *curl, FILE *save, int expected_flags)
{
char buffer[256];
const struct curl_ws_frame *meta;
size_t nread;
size_t total_read = 0;
/* silence "unused parameter" warning */
(void)expected_flags;
/* Frames are expected to have 4097 bytes */
while(true) {
CURLcode result =
curl_ws_recv(curl, buffer, sizeof(buffer), &nread, &meta);
if(result) {
if(result == CURLE_AGAIN)
/* crude busy-loop */
continue;
curl_mprintf("curl_ws_recv returned %d\n", result);
return;
}
curl_mprintf("%d: nread %zu Age %d Flags %x "
"Offset %" CURL_FORMAT_CURL_OFF_T " "
"Bytesleft %" CURL_FORMAT_CURL_OFF_T "\n",
(int)total_read,
nread, meta->age, meta->flags, meta->offset, meta->bytesleft);
assert(meta->flags == expected_flags);
total_read += nread;
fwrite(buffer, 1, nread, save);
/* exit condition */
if(meta->bytesleft == 0) {
break;
}
}
assert(total_read == 4097);
}
static void websocket(CURL *curl)
{
FILE *save = fopen(libtest_arg2, FOPEN_WRITETEXT);
if(!save)
return;
/* Three frames are expected */
websocket_frame(curl, save, CURLWS_TEXT | CURLWS_CONT);
websocket_frame(curl, save, CURLWS_TEXT | CURLWS_CONT);
websocket_frame(curl, save, CURLWS_TEXT);
fclose(save);
websocket_close(curl);
}
CURLcode test(char *URL)
{
CURL *curl;
CURLcode res = CURLE_OK;
global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, URL);
/* use the callback style */
curl_easy_setopt(curl, CURLOPT_USERAGENT, "websocket/2311");
libtest_debug_config.nohex = 1;
libtest_debug_config.tracetime = 1;
curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &libtest_debug_config);
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */
res = curl_easy_perform(curl);
curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", res);
if(res == CURLE_OK)
websocket(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return res;
}
#else
NO_SUPPORT_BUILT_IN
#endif

View file

@ -1,102 +0,0 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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) {
curl_mfprintf(stderr, "write_cb received ping with %zd bytes\n",
size * nitems);
curl_mfprintf(stderr, "\n");
for(i = 0; i < size * nitems; i++) {
curl_mfprintf(stderr, "%02X%s", (int)b[i],
(i % 10 == 0 && i != 0) ? "\n" : " ");
}
curl_mfprintf(stderr, "\n");
curl_mfprintf(stderr, "write_cb sending pong response\n");
curl_ws_send(curl, b, size * nitems, &sent, 0, CURLWS_PONG);
curl_mfprintf(stderr, "write_cb closing websocket\n");
curl_ws_send(curl, NULL, 0, &sent, 0, CURLWS_CLOSE);
ping_check->pinged = 1;
}
else {
curl_mfprintf(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);
curl_mfprintf(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

254
tests/libtest/lib2700.c Normal file
View file

@ -0,0 +1,254 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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"
#include "testtrace.h"
#include "memdebug.h"
#ifndef CURL_DISABLE_WEBSOCKETS
static const char *descr_flags(int flags)
{
if(flags & CURLWS_TEXT)
return flags & CURLWS_CONT ? "txt ---" : "txt fin";
if(flags & CURLWS_BINARY)
return flags & CURLWS_CONT ? "bin ---" : "bin fin";
if(flags & CURLWS_PING)
return "ping";
if(flags & CURLWS_PONG)
return "pong";
if(flags & CURLWS_CLOSE)
return "close";
assert(false);
return "";
}
static CURLcode send_header(CURL *curl, int flags, size_t size)
{
CURLcode res = CURLE_OK;
size_t nsent;
retry:
res = curl_ws_send(curl, NULL, 0, &nsent, (curl_off_t)size,
flags | CURLWS_OFFSET);
if(res == CURLE_AGAIN) {
assert(nsent == 0);
goto retry;
}
if(res) {
curl_mfprintf(stderr, "%s:%d curl_ws_send() failed with code %d (%s)\n",
__FILE__, __LINE__, res, curl_easy_strerror(res));
assert(nsent == 0);
return res;
}
assert(nsent == 0);
return CURLE_OK;
}
static CURLcode recv_header(CURL *curl, int *flags, curl_off_t *offset,
curl_off_t *bytesleft)
{
CURLcode res = CURLE_OK;
size_t nread;
const struct curl_ws_frame *meta;
*flags = 0;
*offset = 0;
*bytesleft = 0;
retry:
res = curl_ws_recv(curl, NULL, 0, &nread, &meta);
if(res == CURLE_AGAIN) {
assert(nread == 0);
goto retry;
}
if(res) {
curl_mfprintf(stderr, "%s:%d curl_ws_recv() failed with code %d (%s)\n",
__FILE__, __LINE__, res, curl_easy_strerror(res));
assert(nread == 0);
return res;
}
assert(nread == 0);
assert(meta != NULL);
assert(meta->flags);
assert(meta->offset == 0);
*flags = meta->flags;
*offset = meta->offset;
*bytesleft = meta->bytesleft;
curl_mfprintf(stdout, "%s [%" FMT_OFF_T "]", descr_flags(meta->flags),
meta->bytesleft);
if(meta->bytesleft > 0)
curl_mfprintf(stdout, " ");
res = send_header(curl, meta->flags, (size_t)meta->bytesleft);
if(res)
return res;
return CURLE_OK;
}
static CURLcode send_chunk(CURL *curl, int flags, const char *buffer,
size_t size, size_t *offset)
{
CURLcode res = CURLE_OK;
size_t nsent;
retry:
res = curl_ws_send(curl, buffer + *offset, size - *offset, &nsent, 0,
flags);
if(res == CURLE_AGAIN) {
assert(nsent == 0);
goto retry;
}
if(res) {
curl_mfprintf(stderr, "%s:%d curl_ws_send() failed with code %d (%s)\n",
__FILE__, __LINE__, res, curl_easy_strerror(res));
assert(nsent == 0);
return res;
}
assert(nsent <= size - *offset);
*offset += nsent;
return CURLE_OK;
}
static CURLcode recv_chunk(CURL *curl, int flags, curl_off_t *offset,
curl_off_t *bytesleft)
{
CURLcode res = CURLE_OK;
char buffer[256];
size_t nread;
const struct curl_ws_frame *meta;
size_t sendoffset = 0;
retry:
res = curl_ws_recv(curl, buffer, sizeof(buffer), &nread, &meta);
if(res == CURLE_AGAIN) {
assert(nread == 0);
goto retry;
}
if(res) {
curl_mfprintf(stderr, "%s:%d curl_ws_recv() failed with code %d (%s)\n",
__FILE__, __LINE__, res, curl_easy_strerror(res));
assert(nread == 0);
return res;
}
assert(nread <= sizeof(buffer));
assert(meta != NULL);
assert(meta->flags == flags);
assert(meta->offset == *offset);
assert(meta->bytesleft == (*bytesleft - (curl_off_t)nread));
*offset += nread;
*bytesleft -= nread;
fwrite(buffer, 1, nread, stdout);
while(sendoffset < nread) {
res = send_chunk(curl, flags, buffer, nread, &sendoffset);
if(res)
return res;
}
return CURLE_OK;
}
static CURLcode recv_frame(CURL *curl, bool *stop)
{
CURLcode res = CURLE_OK;
int flags = 0;
curl_off_t offset = 0;
curl_off_t bytesleft = 0;
res = recv_header(curl, &flags, &offset, &bytesleft);
if(res)
return res;
while(bytesleft > 0) {
res = recv_chunk(curl, flags, &offset, &bytesleft);
if(res)
return res;
}
if(flags & CURLWS_CLOSE)
*stop = true;
curl_mfprintf(stdout, "\n");
return res;
}
CURLcode test(char *URL)
{
CURLcode res = CURLE_OK;
bool stop = false;
CURL *curl;
global_init(CURL_GLOBAL_ALL);
curl_global_trace("ws");
easy_init(curl);
easy_setopt(curl, CURLOPT_URL, URL);
easy_setopt(curl, CURLOPT_USERAGENT, "client/test2700");
libtest_debug_config.nohex = 1;
libtest_debug_config.tracetime = 1;
easy_setopt(curl, CURLOPT_DEBUGDATA, &libtest_debug_config);
easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb);
easy_setopt(curl, CURLOPT_VERBOSE, 1L);
easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L);
if(!getenv("LIB2700_AUTO_PONG"))
easy_setopt(curl, CURLOPT_WS_OPTIONS, (long)CURLWS_NOAUTOPONG);
res = curl_easy_perform(curl);
if(res) {
curl_mfprintf(stderr,
"%s:%d curl_easy_perform() failed with code %d (%s)\n",
__FILE__, __LINE__, res, curl_easy_strerror(res));
goto test_cleanup;
}
while(!stop) {
res = recv_frame(curl, &stop);
if(res)
goto test_cleanup;
}
test_cleanup:
curl_easy_cleanup(curl);
curl_global_cleanup();
return res;
}
#else
NO_SUPPORT_BUILT_IN
#endif