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
22 static 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
28 static struct lws_context *context;
29 static int interrupted, port = 443, ssl_connection = LCCSCF_USE_SSL;
30 static 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
37 static const uint32_t backoff_ms[] = { 1000, 2000, 3000, 4000, 5000 };
38
39 static 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
54 static void
connect_client(lws_sorted_usec_list_t * sul)55 connect_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
88 static int
callback_minimal(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)89 callback_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
119 do_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
139 static const struct lws_protocols protocols[] = {
140 { "lws-minimal-client", callback_minimal, 0, 0, 0, NULL, 0 },
141 LWS_PROTOCOL_LIST_TERM
142 };
143
144 static void
sigint_handler(int sig)145 sigint_handler(int sig)
146 {
147 interrupted = 1;
148 }
149
main(int argc,const char ** argv)150 int 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