ws_data.c (7427B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 #include "first.h" 25 26 #ifndef CURL_DISABLE_WEBSOCKETS 27 28 static CURLcode check_recv(const struct curl_ws_frame *frame, 29 size_t r_offset, size_t nread, size_t exp_len) 30 { 31 if(!frame) 32 return CURLE_OK; 33 34 if(frame->flags & CURLWS_CLOSE) { 35 curl_mfprintf(stderr, "recv_data: unexpected CLOSE frame from server, " 36 "got %ld bytes, offset=%ld, rflags %x\n", 37 (long)nread, (long)r_offset, frame->flags); 38 return CURLE_RECV_ERROR; 39 } 40 if(!r_offset && !(frame->flags & CURLWS_BINARY)) { 41 curl_mfprintf(stderr, "recv_data: wrong frame, got %ld bytes, offset=%ld, " 42 "rflags %x\n", 43 (long)nread, (long)r_offset, frame->flags); 44 return CURLE_RECV_ERROR; 45 } 46 if(frame->offset != (curl_off_t)r_offset) { 47 curl_mfprintf(stderr, "recv_data: frame offset, expected %ld, got %ld\n", 48 (long)r_offset, (long)frame->offset); 49 return CURLE_RECV_ERROR; 50 } 51 if(frame->bytesleft != (curl_off_t)(exp_len - r_offset - nread)) { 52 curl_mfprintf(stderr, "recv_data: frame bytesleft, " 53 "expected %ld, got %ld\n", 54 (long)(exp_len - r_offset - nread), (long)frame->bytesleft); 55 return CURLE_RECV_ERROR; 56 } 57 if(r_offset + nread > exp_len) { 58 curl_mfprintf(stderr, "recv_data: data length, expected %ld, now at %ld\n", 59 (long)exp_len, (long)(r_offset + nread)); 60 return CURLE_RECV_ERROR; 61 } 62 return CURLE_OK; 63 } 64 65 static CURLcode data_echo(CURL *curl, size_t count, 66 size_t plen_min, size_t plen_max) 67 { 68 CURLcode r = CURLE_OK; 69 const struct curl_ws_frame *frame; 70 size_t len; 71 char *send_buf = NULL, *recv_buf = NULL; 72 size_t i, scount = count, rcount = count; 73 int rblock, sblock; 74 75 send_buf = calloc(1, plen_max + 1); 76 recv_buf = calloc(1, plen_max + 1); 77 if(!send_buf || !recv_buf) { 78 r = CURLE_OUT_OF_MEMORY; 79 goto out; 80 } 81 82 for(i = 0; i < plen_max; ++i) { 83 send_buf[i] = (char)('0' + ((int)i % 10)); 84 } 85 86 for(len = plen_min; len <= plen_max; ++len) { 87 size_t nwritten, nread, slen = len, rlen = len; 88 char *sbuf = send_buf, *rbuf = recv_buf; 89 90 memset(recv_buf, 0, plen_max); 91 while(slen || rlen || scount || rcount) { 92 sblock = rblock = 1; 93 if(slen) { 94 r = curl_ws_send(curl, sbuf, slen, &nwritten, 0, CURLWS_BINARY); 95 sblock = (r == CURLE_AGAIN); 96 if(!r || (r == CURLE_AGAIN)) { 97 curl_mfprintf(stderr, "curl_ws_send(len=%ld) -> %d, %ld (%ld/%ld)\n", 98 (long)slen, r, (long)nwritten, 99 (long)(len - slen), (long)len); 100 sbuf += nwritten; 101 slen -= nwritten; 102 } 103 else 104 goto out; 105 } 106 if(!slen && scount) { /* go again? */ 107 scount--; 108 sbuf = send_buf; 109 slen = len; 110 } 111 112 if(rlen) { 113 size_t max_recv = (64 * 1024); 114 r = curl_ws_recv(curl, rbuf, (rlen > max_recv) ? max_recv : rlen, 115 &nread, &frame); 116 if(!r || (r == CURLE_AGAIN)) { 117 rblock = (r == CURLE_AGAIN); 118 curl_mfprintf(stderr, "curl_ws_recv(len=%ld) -> %d, %ld (%ld/%ld) " 119 "\n", 120 (long)rlen, r, (long)nread, (long)(len - rlen), 121 (long)len); 122 if(!r) { 123 r = check_recv(frame, len - rlen, nread, len); 124 if(r) 125 goto out; 126 } 127 rbuf += nread; 128 rlen -= nread; 129 } 130 else 131 goto out; 132 } 133 if(!rlen && rcount) { /* go again? */ 134 rcount--; 135 rbuf = recv_buf; 136 rlen = len; 137 } 138 139 if(rblock && sblock) { 140 curl_mfprintf(stderr, "EAGAIN, sleep, try again\n"); 141 curlx_wait_ms(100); 142 } 143 } 144 145 if(memcmp(send_buf, recv_buf, len)) { 146 curl_mfprintf(stderr, "recv_data: data differs\n"); 147 dump("expected:", (unsigned char *)send_buf, len, 0); 148 dump("received:", (unsigned char *)recv_buf, len, 0); 149 r = CURLE_RECV_ERROR; 150 goto out; 151 } 152 } 153 154 out: 155 if(!r) 156 websocket_close(curl); 157 free(send_buf); 158 free(recv_buf); 159 return r; 160 } 161 162 static void usage_ws_data(const char *msg) 163 { 164 if(msg) 165 curl_mfprintf(stderr, "%s\n", msg); 166 curl_mfprintf(stderr, 167 "usage: [options] url\n" 168 " -m number minimum frame size\n" 169 " -M number maximum frame size\n" 170 ); 171 } 172 173 #endif 174 175 static int test_ws_data(int argc, char *argv[]) 176 { 177 #ifndef CURL_DISABLE_WEBSOCKETS 178 CURL *curl; 179 CURLcode res = CURLE_OK; 180 const char *url; 181 size_t plen_min = 0, plen_max = 0, count = 1; 182 int ch; 183 184 while((ch = cgetopt(argc, argv, "c:hm:M:")) != -1) { 185 switch(ch) { 186 case 'h': 187 usage_ws_data(NULL); 188 res = CURLE_BAD_FUNCTION_ARGUMENT; 189 goto cleanup; 190 case 'c': 191 count = (size_t)strtol(coptarg, NULL, 10); 192 break; 193 case 'm': 194 plen_min = (size_t)strtol(coptarg, NULL, 10); 195 break; 196 case 'M': 197 plen_max = (size_t)strtol(coptarg, NULL, 10); 198 break; 199 default: 200 usage_ws_data("invalid option"); 201 res = CURLE_BAD_FUNCTION_ARGUMENT; 202 goto cleanup; 203 } 204 } 205 argc -= coptind; 206 argv += coptind; 207 208 if(!plen_max) 209 plen_max = plen_min; 210 211 if(plen_max < plen_min) { 212 curl_mfprintf(stderr, "maxlen must be >= minlen, got %ld-%ld\n", 213 (long)plen_min, (long)plen_max); 214 res = CURLE_BAD_FUNCTION_ARGUMENT; 215 goto cleanup; 216 } 217 218 if(argc != 1) { 219 usage_ws_data(NULL); 220 res = CURLE_BAD_FUNCTION_ARGUMENT; 221 goto cleanup; 222 } 223 url = argv[0]; 224 225 curl_global_init(CURL_GLOBAL_ALL); 226 227 curl = curl_easy_init(); 228 if(curl) { 229 curl_easy_setopt(curl, CURLOPT_URL, url); 230 231 /* use the callback style */ 232 curl_easy_setopt(curl, CURLOPT_USERAGENT, "ws-data"); 233 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); 234 curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */ 235 res = curl_easy_perform(curl); 236 curl_mfprintf(stderr, "curl_easy_perform() returned %u\n", (int)res); 237 if(res == CURLE_OK) 238 res = data_echo(curl, count, plen_min, plen_max); 239 240 /* always cleanup */ 241 curl_easy_cleanup(curl); 242 } 243 244 cleanup: 245 curl_global_cleanup(); 246 return (int)res; 247 248 #else /* !CURL_DISABLE_WEBSOCKETS */ 249 (void)argc; 250 (void)argv; 251 curl_mfprintf(stderr, "WebSockets not enabled in libcurl\n"); 252 return 1; 253 #endif /* CURL_DISABLE_WEBSOCKETS */ 254 }