diff --git a/.mailmap b/.mailmap index 000268adf2..a67f6e6b57 100644 --- a/.mailmap +++ b/.mailmap @@ -114,3 +114,4 @@ Aki Sakurai <75532970+AkiSakurai@users.noreply.github.com> Sinkevich Artem Andrew Kirillov Stephen Farrell +Calvin Ruocco diff --git a/docs/libcurl/libcurl-env-dbg.md b/docs/libcurl/libcurl-env-dbg.md index a5e977f302..ce4e3c006a 100644 --- a/docs/libcurl/libcurl-env-dbg.md +++ b/docs/libcurl/libcurl-env-dbg.md @@ -141,6 +141,11 @@ decoding. Used to simulate blocking sends after this chunk size for WebSocket connections. +## `CURL_WS_FORCE_ZERO_MASK` + +Used to set the bitmask of all sent WebSocket frames to zero. The value of the +environment variable does not matter. + ## `CURL_FORBID_REUSE` Used to set the CURLOPT_FORBID_REUSE flag on each transfer initiated diff --git a/lib/ws.c b/lib/ws.c index 61ab5019fd..fc02025f1d 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -145,35 +145,77 @@ static int ws_frame_firstbyte2flags(struct Curl_easy *data, unsigned char firstbyte, int cont_flags) { switch(firstbyte) { + /* 0x00 - intermediate TEXT/BINARY fragment */ case WSBIT_OPCODE_CONT: - /* continuation of a previous fragment: restore stored flags */ + if(!(cont_flags & CURLWS_CONT)) { + failf(data, "[WS] no ongoing fragmented message to resume"); + return 0; + } return cont_flags | CURLWS_CONT; + /* 0x80 - final TEXT/BIN fragment */ case (WSBIT_OPCODE_CONT | WSBIT_FIN): - /* continuation of a previous fragment: restore stored flags */ + if(!(cont_flags & CURLWS_CONT)) { + failf(data, "[WS] no ongoing fragmented message to resume"); + return 0; + } return cont_flags & ~CURLWS_CONT; + /* 0x01 - first TEXT fragment */ case WSBIT_OPCODE_TEXT: + if(cont_flags & CURLWS_CONT) { + failf(data, "[WS] fragmented message interrupted by new TEXT msg"); + return 0; + } return CURLWS_TEXT | CURLWS_CONT; + /* 0x81 - unfragmented TEXT msg */ case (WSBIT_OPCODE_TEXT | WSBIT_FIN): + if(cont_flags & CURLWS_CONT) { + failf(data, "[WS] fragmented message interrupted by new TEXT msg"); + return 0; + } return CURLWS_TEXT; + /* 0x02 - first BINARY fragment */ case WSBIT_OPCODE_BIN: + if(cont_flags & CURLWS_CONT) { + failf(data, "[WS] fragmented message interrupted by new BINARY msg"); + return 0; + } return CURLWS_BINARY | CURLWS_CONT; + /* 0x82 - unfragmented BINARY msg */ case (WSBIT_OPCODE_BIN | WSBIT_FIN): + if(cont_flags & CURLWS_CONT) { + failf(data, "[WS] fragmented message interrupted by new BINARY msg"); + return 0; + } return CURLWS_BINARY; + /* 0x08 - first CLOSE fragment */ + case WSBIT_OPCODE_CLOSE: + failf(data, "[WS] invalid fragmented CLOSE frame"); + return 0; + /* 0x88 - unfragmented CLOSE */ case (WSBIT_OPCODE_CLOSE | WSBIT_FIN): return CURLWS_CLOSE; + /* 0x09 - first PING fragment */ + case WSBIT_OPCODE_PING: + failf(data, "[WS] invalid fragmented PING frame"); + return 0; + /* 0x89 - unfragmented PING */ case (WSBIT_OPCODE_PING | WSBIT_FIN): return CURLWS_PING; + /* 0x0a - first PONG fragment */ + case WSBIT_OPCODE_PONG: + failf(data, "[WS] invalid fragmented PONG frame"); + return 0; + /* 0x8a - unfragmented PONG */ case (WSBIT_OPCODE_PONG | WSBIT_FIN): return CURLWS_PONG; + /* invalid first byte */ default: - if(firstbyte & WSBIT_RSV_MASK) { - failf(data, "WS: unknown reserved bit: %x", - firstbyte & WSBIT_RSV_MASK); - } - else { - failf(data, "WS: unknown opcode: %x", - firstbyte & WSBIT_OPCODE_MASK); - } + if(firstbyte & WSBIT_RSV_MASK) + /* any of the reserved bits 0x40/0x20/0x10 are set */ + failf(data, "[WS] invalid reserved bits: %02x", firstbyte); + else + /* any of the reserved opcodes 0x3-0x7 or 0xb-0xf is used */ + failf(data, "[WS] invalid opcode: %02x", firstbyte); return 0; } } @@ -186,20 +228,20 @@ static unsigned char ws_frame_flags2firstbyte(struct Curl_easy *data, switch(flags & ~CURLWS_OFFSET) { case 0: if(contfragment) { - infof(data, "WS: no flags given; interpreting as continuation " + infof(data, "[WS] no flags given; interpreting as continuation " "fragment for compatibility"); return (WSBIT_OPCODE_CONT | WSBIT_FIN); } - failf(data, "WS: no flags given"); + failf(data, "[WS] no flags given"); *err = CURLE_BAD_FUNCTION_ARGUMENT; return 0xff; case CURLWS_CONT: if(contfragment) { - infof(data, "WS: setting CURLWS_CONT flag without message type is " + infof(data, "[WS] setting CURLWS_CONT flag without message type is " "supported for compatibility but highly discouraged"); return WSBIT_OPCODE_CONT; } - failf(data, "WS: No ongoing fragmented message to continue"); + failf(data, "[WS] No ongoing fragmented message to continue"); *err = CURLE_BAD_FUNCTION_ARGUMENT; return 0xff; case CURLWS_TEXT: @@ -215,24 +257,24 @@ static unsigned char ws_frame_flags2firstbyte(struct Curl_easy *data, case CURLWS_CLOSE: return WSBIT_OPCODE_CLOSE | WSBIT_FIN; case (CURLWS_CLOSE | CURLWS_CONT): - failf(data, "WS: CLOSE frame must not be fragmented"); + failf(data, "[WS] CLOSE frame must not be fragmented"); *err = CURLE_BAD_FUNCTION_ARGUMENT; return 0xff; case CURLWS_PING: return WSBIT_OPCODE_PING | WSBIT_FIN; case (CURLWS_PING | CURLWS_CONT): - failf(data, "WS: PING frame must not be fragmented"); + failf(data, "[WS] PING frame must not be fragmented"); *err = CURLE_BAD_FUNCTION_ARGUMENT; return 0xff; case CURLWS_PONG: return WSBIT_OPCODE_PONG | WSBIT_FIN; case (CURLWS_PONG | CURLWS_CONT): - failf(data, "WS: PONG frame must not be fragmented"); + failf(data, "[WS] PONG frame must not be fragmented"); *err = CURLE_BAD_FUNCTION_ARGUMENT; return 0xff; default: - failf(data, "WS: unknown flags: %x", flags); - *err = CURLE_SEND_ERROR; + failf(data, "[WS] unknown flags: %x", flags); + *err = CURLE_BAD_FUNCTION_ARGUMENT; return 0xff; } } @@ -244,23 +286,23 @@ static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, case 0: break; case 1: - CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s]", msg, - ws_frame_name_of_op(dec->head[0]), - (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL"); + CURL_TRC_WS(data, "decoded %s [%s%s]", msg, + ws_frame_name_of_op(dec->head[0]), + (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL"); break; default: if(dec->head_len < dec->head_total) { - CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s](%d/%d)", msg, - ws_frame_name_of_op(dec->head[0]), - (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL", - dec->head_len, dec->head_total); + CURL_TRC_WS(data, "decoded %s [%s%s](%d/%d)", msg, + ws_frame_name_of_op(dec->head[0]), + (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL", + dec->head_len, dec->head_total); } else { - CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s payload=%" - FMT_OFF_T "/%" FMT_OFF_T "]", - msg, ws_frame_name_of_op(dec->head[0]), - (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL", - dec->payload_offset, dec->payload_len); + CURL_TRC_WS(data, "decoded %s [%s%s payload=%" + FMT_OFF_T "/%" FMT_OFF_T "]", + msg, ws_frame_name_of_op(dec->head[0]), + (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL", + dec->payload_offset, dec->payload_len); } break; } @@ -318,17 +360,15 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, dec->frame_flags = ws_frame_firstbyte2flags(data, dec->head[0], dec->cont_flags); if(!dec->frame_flags) { - failf(data, "WS: invalid first byte: %x", dec->head[0]); ws_dec_reset(dec); return CURLE_RECV_ERROR; } - if(dec->frame_flags & CURLWS_CONT) { + /* fragmentation only applies to data frames (text/binary); + * control frames (close/ping/pong) do not affect the CONT status */ + if(dec->frame_flags & (CURLWS_TEXT | CURLWS_BINARY)) { dec->cont_flags = dec->frame_flags; } - else { - dec->cont_flags = 0; - } dec->head_len = 1; /* ws_dec_info(dec, data, "seeing opcode"); */ @@ -341,10 +381,30 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, if(dec->head[1] & WSBIT_MASK) { /* A client MUST close a connection if it detects a masked frame. */ - failf(data, "WS: masked input frame"); + failf(data, "[WS] masked input frame"); ws_dec_reset(dec); return CURLE_RECV_ERROR; } + if(dec->frame_flags & CURLWS_PING && dec->head[1] > 125) { + /* The maximum valid size of PING frames is 125 bytes. + Accepting overlong pings would mean sending equivalent pongs! */ + failf(data, "[WS] received PING frame is too big"); + ws_dec_reset(dec); + return CURLE_RECV_ERROR; + } + if(dec->frame_flags & CURLWS_PONG && dec->head[1] > 125) { + /* The maximum valid size of PONG frames is 125 bytes. */ + failf(data, "[WS] received PONG frame is too big"); + ws_dec_reset(dec); + return CURLE_RECV_ERROR; + } + if(dec->frame_flags & CURLWS_CLOSE && dec->head[1] > 125) { + /* The maximum valid size of CLOSE frames is 125 bytes. */ + failf(data, "[WS] received CLOSE frame is too big"); + ws_dec_reset(dec); + return CURLE_RECV_ERROR; + } + /* How long is the frame head? */ if(dec->head[1] == 126) { dec->head_total = 4; @@ -379,7 +439,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, break; case 10: if(dec->head[2] > 127) { - failf(data, "WS: frame length longer than 64 signed not supported"); + failf(data, "[WS] frame length longer than 64 signed not supported"); return CURLE_RECV_ERROR; } dec->payload_len = ((curl_off_t)dec->head[2] << 56) | @@ -394,7 +454,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, default: /* this should never happen */ DEBUGASSERT(0); - failf(data, "WS: unexpected frame header length"); + failf(data, "[WS] unexpected frame header length"); return CURLE_RECV_ERROR; } @@ -430,8 +490,8 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, Curl_bufq_skip(inraw, (size_t)nwritten); dec->payload_offset += (curl_off_t)nwritten; remain = dec->payload_len - dec->payload_offset; - CURL_TRC_WRITE(data, "websocket, passed %zd bytes payload, %" - FMT_OFF_T " remain", nwritten, remain); + CURL_TRC_WS(data, "passed %zd bytes payload, %" + FMT_OFF_T " remain", nwritten, remain); } return remain ? CURLE_AGAIN : CURLE_OK; @@ -457,7 +517,7 @@ static CURLcode ws_dec_pass(struct ws_decoder *dec, result = ws_dec_read_head(dec, data, inraw); if(result) { if(result != CURLE_AGAIN) { - infof(data, "WS: decode error %d", (int)result); + infof(data, "[WS] decode error %d", (int)result); break; /* real error */ } /* incomplete ws frame head */ @@ -555,7 +615,7 @@ static ssize_t ws_cw_dec_next(const unsigned char *buf, size_t buflen, 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"); + infof(data, "[WS] auto-respond to PING with a PONG"); /* send back the exact same content as a PONG */ *err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG); if(*err) @@ -588,7 +648,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); if(!ws) { - failf(data, "WS: not a websocket transfer"); + failf(data, "[WS] not a websocket transfer"); return CURLE_FAILED_INIT; } @@ -597,7 +657,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, nwritten = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf, nbytes, &result); if(nwritten < 0) { - infof(data, "WS: error adding data to buffer %d", result); + infof(data, "[WS] error adding data to buffer %d", result); return result; } } @@ -613,17 +673,17 @@ static CURLcode ws_cw_write(struct Curl_easy *data, if(result == CURLE_AGAIN) { /* insufficient amount of data, keep it for later. * we pretend to have written all since we have a copy */ - CURL_TRC_WRITE(data, "websocket, buffered incomplete frame head"); + CURL_TRC_WS(data, "buffered incomplete frame head"); return CURLE_OK; } else if(result) { - infof(data, "WS: decode error %d", (int)result); + infof(data, "[WS] decode error %d", (int)result); return result; } } if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) { - infof(data, "WS: decode ending with %zd frame bytes remaining", + failf(data, "[WS] decode ending with %zd frame bytes remaining", Curl_bufq_len(&ctx->buf)); return CURLE_RECV_ERROR; } @@ -645,10 +705,11 @@ static const struct Curl_cwtype ws_cw_decode = { static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data, const char *msg) { - infof(data, "WS-ENC: %s [%s%s payload=%" FMT_OFF_T "/%" FMT_OFF_T "]", - msg, ws_frame_name_of_op(enc->firstbyte), - (enc->firstbyte & WSBIT_FIN) ? "" : " NON-FIN", - enc->payload_len - enc->payload_remain, enc->payload_len); + CURL_TRC_WS(data, "WS-ENC: %s [%s%s payload=%" + FMT_OFF_T "/%" FMT_OFF_T "]", + msg, ws_frame_name_of_op(enc->firstbyte), + (enc->firstbyte & WSBIT_FIN) ? "" : " NON-FIN", + enc->payload_len - enc->payload_remain, enc->payload_len); } static void ws_enc_reset(struct ws_encoder *enc) @@ -699,7 +760,7 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data, ssize_t n; if(payload_len < 0) { - failf(data, "WS: starting new frame with negative payload length %" + failf(data, "[WS] starting new frame with negative payload length %" FMT_OFF_T, payload_len); *err = CURLE_SEND_ERROR; return -1; @@ -707,7 +768,7 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data, if(enc->payload_remain > 0) { /* trying to write a new frame before the previous one is finished */ - failf(data, "WS: starting new frame with %zd bytes from last one " + failf(data, "[WS] starting new frame with %zd bytes from last one " "remaining to be sent", (ssize_t)enc->payload_remain); *err = CURLE_SEND_ERROR; return -1; @@ -715,7 +776,6 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data, firstbyte = ws_frame_flags2firstbyte(data, flags, enc->contfragment, err); if(*err) { - failf(data, "WS: provided flags not valid: %x", flags); return -1; } @@ -725,6 +785,25 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data, enc->contfragment = (flags & CURLWS_CONT) ? (bit)TRUE : (bit)FALSE; } + if(flags & CURLWS_PING && payload_len > 125) { + /* The maximum valid size of PING frames is 125 bytes. */ + failf(data, "[WS] given PING frame is too big"); + *err = CURLE_TOO_LARGE; + return -1; + } + if(flags & CURLWS_PONG && payload_len > 125) { + /* The maximum valid size of PONG frames is 125 bytes. */ + failf(data, "[WS] given PONG frame is too big"); + *err = CURLE_TOO_LARGE; + return -1; + } + if(flags & CURLWS_CLOSE && payload_len > 125) { + /* The maximum valid size of CLOSE frames is 125 bytes. */ + failf(data, "[WS] given CLOSE frame is too big"); + *err = CURLE_TOO_LARGE; + return -1; + } + head[0] = enc->firstbyte = firstbyte; if(payload_len > 65535) { head[1] = 127 | WSBIT_MASK; @@ -952,7 +1031,14 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, sizeof(ws->enc.mask)); if(result) return result; - infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x", + +#ifdef DEBUGBUILD + if(getenv("CURL_WS_FORCE_ZERO_MASK")) + /* force the bit mask to 0x00000000, effectively disabling masking */ + memset(ws->enc.mask, 0, sizeof(ws->enc.mask)); +#endif + + infof(data, "[WS] Received 101, switch to WebSocket; mask %02x%02x%02x%02x", ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]); /* Install our client writer that decodes WS frames payload */ @@ -976,7 +1062,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, nread, &result); if(nwritten < 0) return result; - infof(data, "%zu bytes websocket payload", nread); + CURL_TRC_WS(data, "%zu bytes payload", nread); } else { /* !connect_only */ /* And pass any additional data to the writers */ @@ -1025,7 +1111,7 @@ static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen, 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"); + infof(ctx->data, "[WS] auto-respond to PING with a PONG"); /* send back the exact same content as a PONG */ *err = curl_ws_send(ctx->data, buf, buflen, &bytes, 0, CURLWS_PONG); if(*err) @@ -1079,19 +1165,19 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *d, void *buffer, if(!conn) { /* Unhappy hack with lifetimes of transfers and connection */ if(!data->set.connect_only) { - failf(data, "CONNECT_ONLY is required"); + failf(data, "[WS] CONNECT_ONLY is required"); return CURLE_UNSUPPORTED_PROTOCOL; } Curl_getconnectinfo(data, &conn); if(!conn) { - failf(data, "connection not found"); + failf(data, "[WS] connection not found"); return CURLE_BAD_FUNCTION_ARGUMENT; } } ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN); if(!ws) { - failf(data, "connection is not setup for websocket"); + failf(data, "[WS] connection is not setup for websocket"); return CURLE_BAD_FUNCTION_ARGUMENT; } @@ -1112,7 +1198,7 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *d, void *buffer, } else if(n == 0) { /* connection closed */ - infof(data, "connection expectedly closed?"); + infof(data, "[WS] connection expectedly closed?"); return CURLE_GOT_NOTHING; } CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network", @@ -1196,11 +1282,11 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, return result; } else if(result) { - failf(data, "WS: flush, write error %d", result); + failf(data, "[WS] flush, write error %d", result); return result; } else { - infof(data, "WS: flushed %zu bytes", n); + CURL_TRC_WS(data, "flushed %zu bytes", n); Curl_bufq_skip(&ws->sendbuf, n); } } @@ -1232,7 +1318,7 @@ static CURLcode ws_send_raw_blocking(CURL *d, struct websocket *ws, buflen); left_ms = Curl_timeleft(data, NULL, FALSE); if(left_ms < 0) { - failf(data, "Timeout waiting for socket becoming writable"); + failf(data, "[WS] Timeout waiting for socket becoming writable"); return CURLE_SEND_ERROR; } @@ -1242,7 +1328,7 @@ static CURLcode ws_send_raw_blocking(CURL *d, struct websocket *ws, ev = Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, sock, left_ms ? left_ms : 500); if(ev < 0) { - failf(data, "Error while waiting for socket becoming writable"); + failf(data, "[WS] Error while waiting for socket becoming writable"); return CURLE_SEND_ERROR; } } @@ -1258,7 +1344,7 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); if(!ws) { - failf(data, "Not a websocket transfer"); + failf(data, "[WS] Not a websocket transfer"); return CURLE_SEND_ERROR; } if(!buflen) @@ -1308,13 +1394,13 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg, goto out; } if(!data->conn) { - failf(data, "No associated connection"); + failf(data, "[WS] No associated connection"); result = CURLE_SEND_ERROR; goto out; } ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); if(!ws) { - failf(data, "Not a websocket transfer"); + failf(data, "[WS] Not a websocket transfer"); result = CURLE_SEND_ERROR; goto out; } @@ -1327,7 +1413,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg, goto out; if(fragsize || flags) { - failf(data, "ws_send, raw mode: fragsize and flags cannot be non-zero"); + failf(data, "[WS] fragsize and flags must be zero in raw mode"); return CURLE_BAD_FUNCTION_ARGUMENT; } result = ws_send_raw(data, buffer, buflen, sent); @@ -1342,7 +1428,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg, if(buflen < ws->sendbuf_payload) { /* We have been called with LESS buffer data than before. This * is not how it's supposed too work. */ - failf(data, "curl_ws_send() called with smaller 'buflen' than " + failf(data, "[WS] curl_ws_send() called with smaller 'buflen' than " "bytes already buffered in previous call, %zu vs %zu", buflen, ws->sendbuf_payload); result = CURLE_BAD_FUNCTION_ARGUMENT; @@ -1351,7 +1437,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg, if((curl_off_t)buflen > (ws->enc.payload_remain + (curl_off_t)ws->sendbuf_payload)) { /* too large buflen beyond payload length of frame */ - infof(data, "WS: unaligned frame size (sending %zu instead of %" + failf(data, "[WS] unaligned frame size (sending %zu instead of %" FMT_OFF_T ")", buflen, ws->enc.payload_remain + ws->sendbuf_payload); result = CURLE_BAD_FUNCTION_ARGUMENT; @@ -1392,7 +1478,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg, /* flush, blocking when in callback */ result = ws_flush(data, ws, Curl_is_in_callback(data)); - if(!result) { + if(!result && ws->sendbuf_payload > 0) { *sent += ws->sendbuf_payload; buffer += ws->sendbuf_payload; buflen -= ws->sendbuf_payload; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 63cde06b02..1b07400d85 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -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 \ diff --git a/tests/data/test2304 b/tests/data/test2304 index 8ade3e7718..7a0324c50f 100644 --- a/tests/data/test2304 +++ b/tests/data/test2304 @@ -6,17 +6,16 @@ WebSockets # -# Sends a PING + a 5 byte hello TEXT +# Close connection during websocket upgrade -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= + # allow upgrade @@ -53,7 +52,7 @@ ws://%HOSTIP:%HTTPPORT/%TESTNUMBER GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT -User-Agent: websocket/2304 +User-Agent: websocket/%TESTNUMBER Accept: */* Upgrade: websocket Connection: Upgrade diff --git a/tests/data/test2305 b/tests/data/test2305 deleted file mode 100644 index 773b5df3a5..0000000000 --- a/tests/data/test2305 +++ /dev/null @@ -1,59 +0,0 @@ - - - -WebSockets - - - -# -# Sends three 4097 bytes TEXT frames, as one single message - - -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]% - -# allow upgrade - -upgrade - - - -# -# Client-side - -# require debug for the forced CURL_ENTROPY - -Debug -ws - - -http - - -WebSocket curl_ws_recv() loop reading three larger frames - - -lib%TESTNUMBER - - -ws://%HOSTIP:%HTTPPORT/%TESTNUMBER %LOGDIR/save%TESTNUMBER - - - -# - - -%repeat[256 x helothisisdaniel]% -%repeat[256 x helothisisdaniel]% -%repeat[256 x helothisisdaniel]% - - - diff --git a/tests/data/test2307 b/tests/data/test2307 deleted file mode 100644 index b51b08db22..0000000000 --- a/tests/data/test2307 +++ /dev/null @@ -1,70 +0,0 @@ - - - -WebSockets - - - -# -# Sends a PING with overlong payload - - -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% - -# allow upgrade - -upgrade - - - -# -# Client-side - -# require debug for the forced CURL_ENTROPY - -Debug -ws - - -http - - -WebSockets, overlong PING payload - - -lib2302 - - -ws://%HOSTIP:%HTTPPORT/%TESTNUMBER - - - -# -# PONG with no data and the 32 bit mask -# - - -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== - - - -# 56 == CURLE_RECV_ERROR - -56 - - - diff --git a/tests/data/test2310 b/tests/data/test2310 deleted file mode 100644 index a5338c5e72..0000000000 --- a/tests/data/test2310 +++ /dev/null @@ -1,68 +0,0 @@ - - - -WebSockets - - - -# -# Sends a PING + a TEXT with RSV1 set - - -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% - -# allow upgrade - -upgrade - - - -# -# Client-side - -# require debug for the forced CURL_ENTROPY - -Debug -ws - - -http - - -WebSockets unknown reserved bit set in frame header - - -lib%TESTNUMBER - - -ws://%HOSTIP:%HTTPPORT/%TESTNUMBER - - - -# -# PONG with no data and the 32 bit mask -# - - -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% - - -Returned 56, should be 56. - - - diff --git a/tests/data/test2311 b/tests/data/test2311 deleted file mode 100644 index 9ca25ebc02..0000000000 --- a/tests/data/test2311 +++ /dev/null @@ -1,59 +0,0 @@ - - - -WebSockets - - - -# -# Sends three 4097 bytes TEXT frames, as fragments of one 12291 bytes message - - -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]% - -# allow upgrade - -upgrade - - - -# -# Client-side - -# require debug for the forced CURL_ENTROPY - -Debug -ws - - -http - - -WebSocket curl_ws_recv() read fragmented message - - -lib%TESTNUMBER - - -ws://%HOSTIP:%HTTPPORT/%TESTNUMBER %LOGDIR/save%TESTNUMBER - - - -# - - -%repeat[256 x helothisisdaniel]% -%repeat[256 x helothisisdaniel]% -%repeat[256 x helothisisdaniel]% - - - diff --git a/tests/data/test2312 b/tests/data/test2312 deleted file mode 100644 index 964381de6e..0000000000 --- a/tests/data/test2312 +++ /dev/null @@ -1,66 +0,0 @@ - - - -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/data/test2700 b/tests/data/test2700 new file mode 100644 index 0000000000..e9c5863bee --- /dev/null +++ b/tests/data/test2700 @@ -0,0 +1,76 @@ + + + +WebSockets + + + + + +ws: Frame types + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# A TEXT/BINARY/PING/PONG/CLOSE message with payload + +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% + + + + + +# Exact echo of reply data with additional masking + +%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% + + +# PING is handled by lib and never given to application + +txt fin [3] txt +bin fin [3] bin +ping [4] ping +pong [4] pong +close [7] %hex[%03%e8]hex%close + + +# CURLE_OK + +0 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2701 b/tests/data/test2701 new file mode 100644 index 0000000000..3f810a6a0c --- /dev/null +++ b/tests/data/test2701 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid opcode 0x3 + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# An empty frame with the reserved opcode 0x3 + +HTTP/1.1 101 Switching to WebSockets +Server: server/%TESTNUMBER +Upgrade: Websocket +Connection: Upgrade +Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs= + +%hex[%83%00]hex% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2702 b/tests/data/test2702 new file mode 100644 index 0000000000..1c309a01b3 --- /dev/null +++ b/tests/data/test2702 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid opcode 0xB + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# An empty frame with the reserved opcode 0xB + +HTTP/1.1 101 Switching to WebSockets +Server: server/%TESTNUMBER +Upgrade: Websocket +Connection: Upgrade +Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs= + +%hex[%8b%00]hex% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2703 b/tests/data/test2703 new file mode 100644 index 0000000000..627c2bce80 --- /dev/null +++ b/tests/data/test2703 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid reserved bit RSV1 + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# An empty frame with the RSV1 bit set + +HTTP/1.1 101 Switching to WebSockets +Server: server/%TESTNUMBER +Upgrade: Websocket +Connection: Upgrade +Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs= + +%hex[%C1%00]hex% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2704 b/tests/data/test2704 new file mode 100644 index 0000000000..d00983a246 --- /dev/null +++ b/tests/data/test2704 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid reserved bit RSV2 + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# An empty frame with the RSV2 bit set + +HTTP/1.1 101 Switching to WebSockets +Server: server/%TESTNUMBER +Upgrade: Websocket +Connection: Upgrade +Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs= + +%hex[%A1%00]hex% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2705 b/tests/data/test2705 new file mode 100644 index 0000000000..e94b9c4a7e --- /dev/null +++ b/tests/data/test2705 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid reserved bit RSV3 + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# An empty frame with the RSV3 bit set + +HTTP/1.1 101 Switching to WebSockets +Server: server/%TESTNUMBER +Upgrade: Websocket +Connection: Upgrade +Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs= + +%hex[%91%00]hex% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2706 b/tests/data/test2706 new file mode 100644 index 0000000000..174dc44404 --- /dev/null +++ b/tests/data/test2706 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid masked server message + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# An empty frame with masking + +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% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2707 b/tests/data/test2707 new file mode 100644 index 0000000000..c87a73f5bb --- /dev/null +++ b/tests/data/test2707 @@ -0,0 +1,84 @@ + + + +WebSockets + + + + + +ws: Peculiar frame sizes + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see 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 + +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% + + + + + +# Exact echo of reply data with additional masking + +%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% + + + +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] + + +# CURLE_OK + +0 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2708 b/tests/data/test2708 new file mode 100644 index 0000000000..fd0ee0707f --- /dev/null +++ b/tests/data/test2708 @@ -0,0 +1,75 @@ + + + +WebSockets + + + + + +ws: Automatic PONG + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 +LIB2700_AUTO_PONG=1 + + + + + +upgrade + + +# PING "test" +# CLOSE + +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% + + + + + +# PONG "test" (payload MUST equal ping payload) +# see https://datatracker.ietf.org/doc/html/rfc6455#section-5.2 +# CLOSE + +%hex[%8A%84%00%00%00%00test%88%80%00%00%00%00]hex% + + +# PING is handled by lib and never given to application + +close [0] + + +# CURLE_OK + +0 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2709 b/tests/data/test2709 new file mode 100644 index 0000000000..654f4bf62c --- /dev/null +++ b/tests/data/test2709 @@ -0,0 +1,73 @@ + + + +WebSockets + + + + + +ws: No automatic PONG + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# PING "test" +# CLOSE + +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% + + + + + +# Exact echo of reply data with additional masking + +%hex[%89%84%00%00%00%00test]hex%%hex[%88%80%00%00%00%00]hex% + + +# PING is given to application + +ping [4] test +close [0] + + +# CURLE_OK + +0 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2710 b/tests/data/test2710 new file mode 100644 index 0000000000..5501104e99 --- /dev/null +++ b/tests/data/test2710 @@ -0,0 +1,73 @@ + + + +WebSockets + + + + + +ws: Unsolicited PONG + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# An unsolicited PONG with and without payload + +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% + + + + + +# Exact echo of reply data with additional masking + +%hex[%8a%80%00%00%00%00]hex%%hex[%8a%84%00%00%00%00pong]hex%%hex[%88%80%00%00%00%00]hex% + + + +pong [0] +pong [4] pong +close [0] + + +# CURLE_OK + +0 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2711 b/tests/data/test2711 new file mode 100644 index 0000000000..31b40b596e --- /dev/null +++ b/tests/data/test2711 @@ -0,0 +1,74 @@ + + + +WebSockets + + + + + +ws: Empty PING/PONG/CLOSE + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# A PING/PONG/CLOSE message without payload + +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% + + + + + +# Exact echo of reply data with additional masking + +%hex[%89%80%00%00%00%00]hex%%hex[%8a%80%00%00%00%00]hex%%hex[%88%80%00%00%00%00]hex% + + +# PING is handled by lib and never given to application + +ping [0] +pong [0] +close [0] + + +# CURLE_OK + +0 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2712 b/tests/data/test2712 new file mode 100644 index 0000000000..d1182c37a0 --- /dev/null +++ b/tests/data/test2712 @@ -0,0 +1,74 @@ + + + +WebSockets + + + + + +ws: Max sized PING/PONG/CLOSE + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# A PING/PONG/CLOSE with 125 bytes payload each + +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 _]% + + + + + +# Exact echo of reply data with additional masking + +%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 _]% + + +# PING is handled by lib and never given to application + +ping [125] %repeat[125 x _]% +pong [125] %repeat[125 x _]% +close [125] %hex[%03%e8]hex%%repeat[123 x _]% + + +# CURLE_OK + +0 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2713 b/tests/data/test2713 new file mode 100644 index 0000000000..55188e8514 --- /dev/null +++ b/tests/data/test2713 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid oversized PING + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# A 126 byte long PING + +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 _]% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2714 b/tests/data/test2714 new file mode 100644 index 0000000000..f47c96bda7 --- /dev/null +++ b/tests/data/test2714 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid oversized PONG + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# A 126 byte long PONG + +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 _]% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2715 b/tests/data/test2715 new file mode 100644 index 0000000000..a83e3cc188 --- /dev/null +++ b/tests/data/test2715 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid oversized CLOSE + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# A 126 byte long CLOSE + +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 _]% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2716 b/tests/data/test2716 new file mode 100644 index 0000000000..9c0183ce5c --- /dev/null +++ b/tests/data/test2716 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid fragmented PING + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# A fragmented PING + +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% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2717 b/tests/data/test2717 new file mode 100644 index 0000000000..faa9a83ae0 --- /dev/null +++ b/tests/data/test2717 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid fragmented PONG + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# A fragmented PONG + +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% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2718 b/tests/data/test2718 new file mode 100644 index 0000000000..5a9c773d09 --- /dev/null +++ b/tests/data/test2718 @@ -0,0 +1,72 @@ + + + +WebSockets + + + + + +ws: Invalid fragmented CLOSE + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# A fragmented CLOSE + +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% + + + + + +# No frames + + + + +# No frames + + + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2719 b/tests/data/test2719 new file mode 100644 index 0000000000..15f9ecc9d9 --- /dev/null +++ b/tests/data/test2719 @@ -0,0 +1,81 @@ + + + +WebSockets + + + + + +ws: Fragmented messages + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# Fragmented TEXT/BINARY messages, each with 2/3 fragments + +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% + + + + + +# Exact echo of reply data with additional masking + +%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% + + + +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] + + +# CURLE_OK + +0 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2720 b/tests/data/test2720 new file mode 100644 index 0000000000..1f4622632b --- /dev/null +++ b/tests/data/test2720 @@ -0,0 +1,86 @@ + + + +WebSockets + + + + + +ws: Fragmented messages with empty fragments + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see 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 + +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% + + + + + +# Exact echo of reply data with additional masking + +%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% + + + +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] + + +# CURLE_OK + +0 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2721 b/tests/data/test2721 new file mode 100644 index 0000000000..2c3fcd855e --- /dev/null +++ b/tests/data/test2721 @@ -0,0 +1,77 @@ + + + +WebSockets + + + + + +ws: Fragmented messages with interleaved pong + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# A TEXT/BINARY message fragmented into two frames each, with pongs in the middle + +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% + + + + + +# Exact echo of reply data with additional masking + +%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% + + + +txt --- [2] t1 +pong [2] p1 +txt fin [2] t2 +bin --- [2] b1 +pong [2] p2 +bin fin [2] b2 +close [0] + + +# CURLE_OK + +0 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2722 b/tests/data/test2722 new file mode 100644 index 0000000000..0dd1bbb59f --- /dev/null +++ b/tests/data/test2722 @@ -0,0 +1,73 @@ + + + +WebSockets + + + + + +ws: Invalid fragmented message without initial frame + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# First a valid BINARY frame +# Second a fragmented message with the first frame missing + +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% + + + + + +# Echo of reply data with additional masking up to the missing initial frame + +%hex[%82%81%00%00%00%00b]hex% + + +# Only frames up to the missing initial frame + +bin fin [1] b + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/data/test2723 b/tests/data/test2723 new file mode 100644 index 0000000000..7dd2609fcf --- /dev/null +++ b/tests/data/test2723 @@ -0,0 +1,74 @@ + + + +WebSockets + + + + + +ws: Invalid fragmented message without final frame + + +Debug +ws + + +http + + +lib2700 + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + +CURL_WS_FORCE_ZERO_MASK=1 + + + + + +upgrade + + +# Full list of frames: see below +# First a fragmented TEXT message with the last frame missing +# Second a valid BINARY frame + +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% + + + + + +# Echo of reply data with additional masking up to the missing final frame + +%hex[%01%82%00%00%00%00t1]hex%%hex[%00%82%00%00%00%00t2]hex% + + +# Only frames up to the missing final frame + +txt --- [2] t1 +txt --- [2] t2 + + +# CURLE_RECV_ERROR + +56 + + +# Strip HTTP header from + +^GET /.* +^(Host|User-Agent|Accept|Upgrade|Connection|Sec-WebSocket-(Version|Key)): .* +^\s*$ + + + diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index 306f418ad5..d78d64ac4f 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -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') diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 002e7ab547..d130d5158d 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -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) diff --git a/tests/libtest/lib2305.c b/tests/libtest/lib2305.c deleted file mode 100644 index fc712ac166..0000000000 --- a/tests/libtest/lib2305.c +++ /dev/null @@ -1,109 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * 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" -#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 diff --git a/tests/libtest/lib2310.c b/tests/libtest/lib2310.c deleted file mode 100644 index f5fe7dcca9..0000000000 --- a/tests/libtest/lib2310.c +++ /dev/null @@ -1,68 +0,0 @@ -/*************************************************************************** -* _ _ ____ _ - * 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" -#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 diff --git a/tests/libtest/lib2311.c b/tests/libtest/lib2311.c deleted file mode 100644 index 428b637e29..0000000000 --- a/tests/libtest/lib2311.c +++ /dev/null @@ -1,127 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * 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" -#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 diff --git a/tests/libtest/lib2312.c b/tests/libtest/lib2312.c deleted file mode 100644 index 7f1b085d4d..0000000000 --- a/tests/libtest/lib2312.c +++ /dev/null @@ -1,102 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * 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) { - 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 diff --git a/tests/libtest/lib2700.c b/tests/libtest/lib2700.c new file mode 100644 index 0000000000..ecec6809c9 --- /dev/null +++ b/tests/libtest/lib2700.c @@ -0,0 +1,254 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" +#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