Imported Upstream version 3.2.0
[platform/upstream/libwebsockets.git] / minimal-examples / ws-client / minimal-ws-client-spam / minimal-ws-client-spam.c
1 /*
2  * lws-minimal-ws-client-spam
3  *
4  * Written in 2010-2019 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 makes continuous mass ws connections
10  * asynchronously
11  */
12
13 #include <libwebsockets.h>
14 #include <string.h>
15 #include <signal.h>
16 #include <pthread.h>
17
18 enum {
19         CLIENT_IDLE,
20         CLIENT_CONNECTING,
21         CLIENT_AWAITING_SEND,
22 };
23
24 struct client {
25         struct lws *wsi;
26         int index;
27         int state;
28 };
29
30 static struct lws_context *context;
31 static struct client clients[200];
32 static int interrupted, port = 443, ssl_connection = LCCSCF_USE_SSL;
33 static const char *server_address = "libwebsockets.org",
34                   *pro = "lws-mirror-protocol";
35 static int concurrent = 3, conn, tries, est, errors, closed, sent, limit = 15;
36
37 struct pss {
38         int conn;
39 };
40
41 static int
42 connect_client(int idx)
43 {
44         struct lws_client_connect_info i;
45
46         if (tries == limit) {
47                 lwsl_user("Reached limit... finishing\n");
48                 return 0;
49         }
50
51         memset(&i, 0, sizeof(i));
52
53         i.context = context;
54         i.port = port;
55         i.address = server_address;
56         i.path = "/";
57         i.host = i.address;
58         i.origin = i.address;
59         i.ssl_connection = ssl_connection;
60         i.protocol = pro;
61         i.local_protocol_name = pro;
62         i.pwsi = &clients[idx].wsi;
63
64         clients[idx].state = CLIENT_CONNECTING;
65         tries++;
66
67         if (!lws_client_connect_via_info(&i)) {
68                 clients[idx].wsi = NULL;
69                 clients[idx].state = CLIENT_IDLE;
70
71                 return 1;
72         }
73
74         return 0;
75 }
76
77 static int
78 callback_minimal_spam(struct lws *wsi, enum lws_callback_reasons reason,
79                         void *user, void *in, size_t len)
80 {
81         struct pss *pss = (struct pss *)user;
82         uint8_t ping[LWS_PRE + 125];
83         int n, m;
84
85         switch (reason) {
86
87         case LWS_CALLBACK_PROTOCOL_INIT:
88                 for (n = 0; n < concurrent; n++) {
89                         clients[n].index = n;
90                         connect_client(n);
91                 }
92                 break;
93
94         case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
95                 errors++;
96                 lwsl_err("CLIENT_CONNECTION_ERROR: %s (try %d, est %d, closed %d, err %d)\n",
97                          in ? (char *)in : "(null)", tries, est, closed, errors);
98                 for (n = 0; n < concurrent; n++) {
99                         if (clients[n].wsi == wsi) {
100                                 clients[n].wsi = NULL;
101                                 clients[n].state = CLIENT_IDLE;
102                                 connect_client(n);
103                                 break;
104                         }
105                 }
106                 if (tries == closed + errors)
107                         interrupted = 1;
108                 break;
109
110         /* --- client callbacks --- */
111
112         case LWS_CALLBACK_CLIENT_ESTABLISHED:
113                 lwsl_user("%s: established (try %d, est %d, closed %d, err %d)\n",
114                                 __func__, tries, est, closed, errors);
115                 est++;
116                 pss->conn = conn++;
117                 lws_callback_on_writable(wsi);
118                 break;
119
120         case LWS_CALLBACK_CLIENT_CLOSED:
121                 closed++;
122                 if (tries == closed + errors)
123                         interrupted = 1;
124                 if (tries == limit) {
125                         lwsl_user("%s: leaving CLOSED (try %d, est %d, sent %d, closed %d, err %d)\n",
126                                         __func__, tries, est, sent, closed, errors);
127                         break;
128                 }
129
130                 for (n = 0; n < concurrent; n++) {
131                         if (clients[n].wsi == wsi) {
132                                 connect_client(n);
133                                 lwsl_user("%s: reopening (try %d, est %d, closed %d, err %d)\n",
134                                                 __func__, tries, est, closed, errors);
135                                 break;
136                         }
137                 }
138                 if (n == concurrent)
139                         lwsl_user("CLOSED: can't find client wsi\n");
140                 break;
141
142         case LWS_CALLBACK_CLIENT_WRITEABLE:
143                 n = lws_snprintf((char *)ping + LWS_PRE, sizeof(ping) - LWS_PRE,
144                                           "hello %d", pss->conn);
145
146                 m = lws_write(wsi, ping + LWS_PRE, n, LWS_WRITE_TEXT);
147                 if (m < n) {
148                         lwsl_err("sending ping failed: %d\n", m);
149
150                         return -1;
151                 }
152                 lws_set_timeout(wsi, PENDING_TIMEOUT_USER_OK, LWS_TO_KILL_ASYNC);
153                 break;
154
155         default:
156                 break;
157         }
158
159         return lws_callback_http_dummy(wsi, reason, user, in, len);
160 }
161
162 static const struct lws_protocols protocols[] = {
163         {
164                 "lws-spam-test",
165                 callback_minimal_spam,
166                 sizeof(struct pss),
167                 0,
168         },
169         { NULL, NULL, 0, 0 }
170 };
171
172 static void
173 sigint_handler(int sig)
174 {
175         interrupted = 1;
176 }
177
178 int main(int argc, const char **argv)
179 {
180         struct lws_context_creation_info info;
181         const char *p;
182         int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
183                         /* for LLL_ verbosity above NOTICE to be built into lws,
184                          * lws must have been configured and built with
185                          * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
186                         /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
187                         /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
188                         /* | LLL_DEBUG */;
189
190         signal(SIGINT, sigint_handler);
191
192         if ((p = lws_cmdline_option(argc, argv, "-d")))
193                 logs = atoi(p);
194
195         lws_set_log_level(logs, NULL);
196         lwsl_user("LWS minimal ws client SPAM\n");
197
198         memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
199         info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
200         info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
201         info.protocols = protocols;
202 #if defined(LWS_WITH_MBEDTLS)
203         /*
204          * OpenSSL uses the system trust store.  mbedTLS has to be told which
205          * CA to trust explicitly.
206          */
207         info.client_ssl_ca_filepath = "./libwebsockets.org.cer";
208 #endif
209
210         if ((p = lws_cmdline_option(argc, argv, "--server"))) {
211                 server_address = p;
212                 ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
213         }
214
215         if ((p = lws_cmdline_option(argc, argv, "--port")))
216                 port = atoi(p);
217
218         if ((p = lws_cmdline_option(argc, argv, "-l")))
219                 limit = atoi(p);
220
221         if ((p = lws_cmdline_option(argc, argv, "-c")))
222                 concurrent = atoi(p);
223
224         if (lws_cmdline_option(argc, argv, "-n")) {
225                 ssl_connection = 0;
226                 info.options = 0;
227         }
228
229         if (concurrent < 0 ||
230             concurrent > (int)LWS_ARRAY_SIZE(clients)) {
231                 lwsl_err("%s: -c %d larger than max concurrency %d\n", __func__,
232                                 concurrent, (int)LWS_ARRAY_SIZE(clients));
233
234                 return 1;
235         }
236
237         /*
238          * since we know this lws context is only ever going to be used with
239          * one client wsis / fds / sockets at a time, let lws know it doesn't
240          * have to use the default allocations for fd tables up to ulimit -n.
241          * It will just allocate for 1 internal and n (+ 1 http2 nwsi) that we
242          * will use.
243          */
244         info.fd_limit_per_thread = 1 + concurrent + 1;
245
246         context = lws_create_context(&info);
247         if (!context) {
248                 lwsl_err("lws init failed\n");
249                 return 1;
250         }
251
252         while (n >= 0 && !interrupted)
253                 n = lws_service(context, 0);
254
255         lws_context_destroy(context);
256
257         if (tries == limit && closed == tries) {
258                 lwsl_user("Completed\n");
259                 return 0;
260         }
261
262         lwsl_err("Failed\n");
263
264         return 1;
265 }