websocket.c (4687B)
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 /* <DESC> 25 * WebSocket using CONNECT_ONLY 26 * </DESC> 27 */ 28 #include <stdio.h> 29 #include <string.h> 30 #ifdef _WIN32 31 #include <winsock2.h> 32 #include <windows.h> 33 #define sleep(s) Sleep((DWORD)(s)) 34 #else 35 #include <unistd.h> 36 #endif 37 38 #include <curl/curl.h> 39 40 static CURLcode ping(CURL *curl, const char *send_payload) 41 { 42 CURLcode res = CURLE_OK; 43 const char *buf = send_payload; 44 size_t sent, blen = strlen(send_payload); 45 46 while(blen) { 47 res = curl_ws_send(curl, buf, blen, &sent, 0, CURLWS_PING); 48 if(!res) { 49 buf += sent; /* deduct what was sent */ 50 blen -= sent; 51 } 52 else if(res == CURLE_AGAIN) { /* blocked on sending */ 53 fprintf(stderr, "ws: sent PING blocked, waiting a second\n"); 54 sleep(1); /* either select() on socket or max timeout would 55 be good here. */ 56 } 57 else /* real error sending */ 58 break; 59 } 60 if(!res) 61 fprintf(stderr, "ws: sent PING with payload\n"); 62 return res; 63 } 64 65 static CURLcode recv_pong(CURL *curl, const char *expected_payload) 66 { 67 size_t rlen; 68 const struct curl_ws_frame *meta; 69 char buffer[256]; 70 CURLcode res; 71 72 retry: 73 res = curl_ws_recv(curl, buffer, sizeof(buffer), &rlen, &meta); 74 if(!res) { 75 /* on small PING content, this example assumes the complete 76 * PONG content arrives in one go. Larger frames will arrive 77 * in chunks, however. */ 78 if(meta->flags & CURLWS_PONG) { 79 int same = 0; 80 if(rlen == strlen(expected_payload)) { 81 if(!memcmp(expected_payload, buffer, rlen)) 82 same = 1; 83 } 84 fprintf(stderr, "ws: received PONG with %s payload back\n", 85 same ? "same" : "different"); 86 } 87 else if(meta->flags & CURLWS_TEXT) { 88 fprintf(stderr, "ws: received TEXT frame '%.*s'\n", (int)rlen, 89 buffer); 90 } 91 else if(meta->flags & CURLWS_BINARY) { 92 fprintf(stderr, "ws: received BINARY frame of %u bytes\n", (int)rlen); 93 } 94 else { 95 /* some other frame arrived. */ 96 fprintf(stderr, "ws: received frame of %u bytes rflags %x\n", (int)rlen, 97 meta->flags); 98 goto retry; 99 } 100 } 101 else if(res == CURLE_AGAIN) { /* blocked on receiving */ 102 fprintf(stderr, "ws: PONG not there yet, waiting a second\n"); 103 sleep(1); /* either select() on socket or max timeout would 104 be good here. */ 105 goto retry; 106 } 107 if(res) 108 fprintf(stderr, "ws: curl_ws_recv returned %u, received %u\n", 109 (unsigned int)res, (unsigned int)rlen); 110 return res; 111 } 112 113 /* close the connection */ 114 static void websocket_close(CURL *curl) 115 { 116 size_t sent; 117 (void)curl_ws_send(curl, "", 0, &sent, 0, CURLWS_CLOSE); 118 } 119 120 static CURLcode websocket(CURL *curl) 121 { 122 CURLcode res; 123 int i = 0; 124 do { 125 res = ping(curl, "foobar"); 126 if(res) 127 break; 128 res = recv_pong(curl, "foobar"); 129 if(res) 130 break; 131 sleep(1); 132 } while(i++ < 10); 133 websocket_close(curl); 134 return res; 135 } 136 137 int main(int argc, const char *argv[]) 138 { 139 CURL *curl; 140 CURLcode res; 141 142 curl = curl_easy_init(); 143 if(!curl) { 144 return 1; /* memory failure */ 145 } 146 if(argc == 2) 147 curl_easy_setopt(curl, CURLOPT_URL, argv[1]); 148 else 149 curl_easy_setopt(curl, CURLOPT_URL, "wss://example.com"); 150 151 curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */ 152 153 /* Perform the request, res gets the return code */ 154 res = curl_easy_perform(curl); 155 /* Check for errors */ 156 if(res != CURLE_OK) 157 fprintf(stderr, "curl_easy_perform() failed: %s\n", 158 curl_easy_strerror(res)); 159 else { 160 /* connected and ready */ 161 res = websocket(curl); 162 } 163 164 /* always cleanup */ 165 curl_easy_cleanup(curl); 166 return (int)res; 167 }