1/* 2 * lws-minimal-ws-client 3 * 4 * Written in 2010-2020 by Andy Green <andy@warmcat.com> 5 * 6 * This file is made available under the Creative Commons CC0 1.0 7 * Universal Public Domain Dedication. 8 * 9 * This demonstrates a ws client that connects by default to libwebsockets.org 10 * dumb increment ws server. 11 */ 12 13#include <libwebsockets.h> 14#include <string.h> 15#include <signal.h> 16 17/* 18 * This represents your object that "contains" the client connection and has 19 * the client connection bound to it 20 */ 21 22static struct my_conn { 23 lws_sorted_usec_list_t sul; /* schedule connection retry */ 24 struct lws *wsi; /* related wsi if any */ 25 uint16_t retry_count; /* count of consequetive retries */ 26} mco; 27 28static struct lws_context *context; 29static int interrupted, port = 443, ssl_connection = LCCSCF_USE_SSL; 30static const char *server_address = "libwebsockets.org", 31 *pro = "dumb-increment-protocol"; 32 33/* 34 * The retry and backoff policy we want to use for our client connections 35 */ 36 37static const uint32_t backoff_ms[] = { 1000, 2000, 3000, 4000, 5000 }; 38 39static const lws_retry_bo_t retry = { 40 .retry_ms_table = backoff_ms, 41 .retry_ms_table_count = LWS_ARRAY_SIZE(backoff_ms), 42 .conceal_count = LWS_ARRAY_SIZE(backoff_ms), 43 44 .secs_since_valid_ping = 3, /* force PINGs after secs idle */ 45 .secs_since_valid_hangup = 10, /* hangup after secs idle */ 46 47 .jitter_percent = 20, 48}; 49 50/* 51 * Scheduled sul callback that starts the connection attempt 52 */ 53 54static void 55connect_client(lws_sorted_usec_list_t *sul) 56{ 57 struct my_conn *mco = lws_container_of(sul, struct my_conn, sul); 58 struct lws_client_connect_info i; 59 60 memset(&i, 0, sizeof(i)); 61 62 i.context = context; 63 i.port = port; 64 i.address = server_address; 65 i.path = "/"; 66 i.host = i.address; 67 i.origin = i.address; 68 i.ssl_connection = ssl_connection; 69 i.protocol = pro; 70 i.local_protocol_name = "lws-minimal-client"; 71 i.pwsi = &mco->wsi; 72 i.retry_and_idle_policy = &retry; 73 i.userdata = mco; 74 75 if (!lws_client_connect_via_info(&i)) 76 /* 77 * Failed... schedule a retry... we can't use the _retry_wsi() 78 * convenience wrapper api here because no valid wsi at this 79 * point. 80 */ 81 if (lws_retry_sul_schedule(context, 0, sul, &retry, 82 connect_client, &mco->retry_count)) { 83 lwsl_err("%s: connection attempts exhausted\n", __func__); 84 interrupted = 1; 85 } 86} 87 88static int 89callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, 90 void *user, void *in, size_t len) 91{ 92 struct my_conn *mco = (struct my_conn *)user; 93 94 switch (reason) { 95 96 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 97 lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", 98 in ? (char *)in : "(null)"); 99 goto do_retry; 100 break; 101 102 case LWS_CALLBACK_CLIENT_RECEIVE: 103 lwsl_hexdump_notice(in, len); 104 break; 105 106 case LWS_CALLBACK_CLIENT_ESTABLISHED: 107 lwsl_user("%s: established\n", __func__); 108 break; 109 110 case LWS_CALLBACK_CLIENT_CLOSED: 111 goto do_retry; 112 113 default: 114 break; 115 } 116 117 return lws_callback_http_dummy(wsi, reason, user, in, len); 118 119do_retry: 120 /* 121 * retry the connection to keep it nailed up 122 * 123 * For this example, we try to conceal any problem for one set of 124 * backoff retries and then exit the app. 125 * 126 * If you set retry.conceal_count to be larger than the number of 127 * elements in the backoff table, it will never give up and keep 128 * retrying at the last backoff delay plus the random jitter amount. 129 */ 130 if (lws_retry_sul_schedule_retry_wsi(wsi, &mco->sul, connect_client, 131 &mco->retry_count)) { 132 lwsl_err("%s: connection attempts exhausted\n", __func__); 133 interrupted = 1; 134 } 135 136 return 0; 137} 138 139static const struct lws_protocols protocols[] = { 140 { "lws-minimal-client", callback_minimal, 0, 0, 0, NULL, 0 }, 141 LWS_PROTOCOL_LIST_TERM 142}; 143 144static void 145sigint_handler(int sig) 146{ 147 interrupted = 1; 148} 149 150int main(int argc, const char **argv) 151{ 152 struct lws_context_creation_info info; 153 const char *p; 154 int n = 0; 155 156 signal(SIGINT, sigint_handler); 157 memset(&info, 0, sizeof info); 158 lws_cmdline_option_handle_builtin(argc, argv, &info); 159 160 lwsl_user("LWS minimal ws client\n"); 161 162 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; 163 info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ 164 info.protocols = protocols; 165 166#if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL) 167 /* 168 * OpenSSL uses the system trust store. mbedTLS has to be told which 169 * CA to trust explicitly. 170 */ 171 info.client_ssl_ca_filepath = "./libwebsockets.org.cer"; 172#endif 173 174 if ((p = lws_cmdline_option(argc, argv, "--protocol"))) 175 pro = p; 176 177 if ((p = lws_cmdline_option(argc, argv, "-s"))) 178 server_address = p; 179 180 if ((p = lws_cmdline_option(argc, argv, "-p"))) 181 port = atoi(p); 182 183 if (lws_cmdline_option(argc, argv, "-n")) 184 ssl_connection &= ~LCCSCF_USE_SSL; 185 186 if (lws_cmdline_option(argc, argv, "-j")) 187 ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; 188 189 if (lws_cmdline_option(argc, argv, "-k")) 190 ssl_connection |= LCCSCF_ALLOW_INSECURE; 191 192 if (lws_cmdline_option(argc, argv, "-m")) 193 ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK; 194 195 if (lws_cmdline_option(argc, argv, "-e")) 196 ssl_connection |= LCCSCF_ALLOW_EXPIRED; 197 198 info.fd_limit_per_thread = 1 + 1 + 1; 199 200 context = lws_create_context(&info); 201 if (!context) { 202 lwsl_err("lws init failed\n"); 203 return 1; 204 } 205 206 /* schedule the first client connection attempt to happen immediately */ 207 lws_sul_schedule(context, 0, &mco.sul, connect_client, 1); 208 209 while (n >= 0 && !interrupted) 210 n = lws_service(context, 0); 211 212 lws_context_destroy(context); 213 lwsl_user("Completed\n"); 214 215 return 0; 216} 217