Imported Upstream version 3.2
[platform/upstream/libwebsockets.git] / minimal-examples / ws-client / minimal-ws-client-ping / minimal-ws-client-ping.c
1 /*
2  * lws-minimal-ws-client-ping
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 sends pings from time to time and
10  * shows when it receives the PONG
11  */
12
13 #include <libwebsockets.h>
14 #include <string.h>
15 #include <signal.h>
16 #include <pthread.h>
17
18 static struct lws_context *context;
19 static struct lws *client_wsi;
20 static int interrupted, zero_length_ping, port = 443,
21            ssl_connection = LCCSCF_USE_SSL;
22 static const char *server_address = "libwebsockets.org", *pro = "lws-mirror-protocol";
23
24 struct pss {
25         int send_a_ping;
26 };
27
28 static int
29 connect_client(void)
30 {
31         struct lws_client_connect_info i;
32
33         memset(&i, 0, sizeof(i));
34
35         i.context = context;
36         i.port = port;
37         i.address = server_address;
38         i.path = "/";
39         i.host = i.address;
40         i.origin = i.address;
41         i.ssl_connection = ssl_connection;
42         i.protocol = pro;
43         i.local_protocol_name = "lws-ping-test";
44         i.pwsi = &client_wsi;
45
46         return !lws_client_connect_via_info(&i);
47 }
48
49 static int
50 callback_minimal_broker(struct lws *wsi, enum lws_callback_reasons reason,
51                         void *user, void *in, size_t len)
52 {
53         struct pss *pss = (struct pss *)user;
54         int n;
55
56         switch (reason) {
57
58         case LWS_CALLBACK_PROTOCOL_INIT:
59                 goto try;
60
61         case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
62                 lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
63                          in ? (char *)in : "(null)");
64                 client_wsi = NULL;
65                 lws_timed_callback_vh_protocol(lws_get_vhost(wsi),
66                                 lws_get_protocol(wsi), LWS_CALLBACK_USER, 1);
67                 break;
68
69         /* --- client callbacks --- */
70
71         case LWS_CALLBACK_CLIENT_ESTABLISHED:
72                 lwsl_user("%s: established\n", __func__);
73                 lws_set_timer_usecs(wsi, 5 * LWS_USEC_PER_SEC);
74                 break;
75
76         case LWS_CALLBACK_CLIENT_WRITEABLE:
77                 if (pss->send_a_ping) {
78                         uint8_t ping[LWS_PRE + 125];
79                         int m;
80
81                         pss->send_a_ping = 0;
82                         n = 0;
83                         if (!zero_length_ping)
84                                 n = lws_snprintf((char *)ping + LWS_PRE, 125,
85                                         "ping body!");
86
87                         lwsl_user("Sending PING %d...\n", n);
88
89                         m = lws_write(wsi, ping + LWS_PRE, n, LWS_WRITE_PING);
90                         if (m < n) {
91                                 lwsl_err("sending ping failed: %d\n", m);
92
93                                 return -1;
94                         }
95                         
96                         lws_callback_on_writable(wsi);
97                 }
98                 break;
99
100         case LWS_CALLBACK_WS_CLIENT_DROP_PROTOCOL:
101                 client_wsi = NULL;
102                 lws_timed_callback_vh_protocol(lws_get_vhost(wsi),
103                                                lws_get_protocol(wsi),
104                                                LWS_CALLBACK_USER, 1);
105                 break;
106
107         case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
108                 lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE_PONG\n");
109                 lwsl_hexdump_notice(in, len);
110                 break;
111
112         case LWS_CALLBACK_TIMER:
113                 /* we want to send a ws PING every few seconds */
114                 pss->send_a_ping = 1;
115                 lws_callback_on_writable(wsi);
116                 lws_set_timer_usecs(wsi, 5 * LWS_USEC_PER_SEC);
117                 break;
118
119         /* rate-limited client connect retries */
120
121         case LWS_CALLBACK_USER:
122                 lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__);
123 try:
124                 if (connect_client())
125                         lws_timed_callback_vh_protocol(lws_get_vhost(wsi),
126                                                        lws_get_protocol(wsi),
127                                                        LWS_CALLBACK_USER, 1);
128                 break;
129
130         default:
131                 break;
132         }
133
134         return lws_callback_http_dummy(wsi, reason, user, in, len);
135 }
136
137 static const struct lws_protocols protocols[] = {
138         {
139                 "lws-ping-test",
140                 callback_minimal_broker,
141                 sizeof(struct pss),
142                 0,
143         },
144         { NULL, NULL, 0, 0 }
145 };
146
147 static void
148 sigint_handler(int sig)
149 {
150         interrupted = 1;
151 }
152
153 int main(int argc, const char **argv)
154 {
155         struct lws_context_creation_info info;
156         const char *p;
157         int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
158                         /* for LLL_ verbosity above NOTICE to be built into lws,
159                          * lws must have been configured and built with
160                          * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
161                         /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
162                         /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
163                         /* | LLL_DEBUG */;
164
165         signal(SIGINT, sigint_handler);
166
167         if ((p = lws_cmdline_option(argc, argv, "-d")))
168                 logs = atoi(p);
169
170         lws_set_log_level(logs, NULL);
171         lwsl_user("LWS minimal ws client PING\n");
172
173         memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
174         info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
175         info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
176         info.protocols = protocols;
177 #if defined(LWS_WITH_MBEDTLS)
178         /*
179          * OpenSSL uses the system trust store.  mbedTLS has to be told which
180          * CA to trust explicitly.
181          */
182         info.client_ssl_ca_filepath = "./libwebsockets.org.cer";
183 #endif
184
185         if (lws_cmdline_option(argc, argv, "-z"))
186                 zero_length_ping = 1;
187
188         if ((p = lws_cmdline_option(argc, argv, "--protocol")))
189                 pro = p;
190
191         if ((p = lws_cmdline_option(argc, argv, "--server"))) {
192                 server_address = p;
193                 pro = "lws-minimal";
194                 ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
195         }
196
197         if ((p = lws_cmdline_option(argc, argv, "--port")))
198                 port = atoi(p);
199
200         /*
201          * since we know this lws context is only ever going to be used with
202          * one client wsis / fds / sockets at a time, let lws know it doesn't
203          * have to use the default allocations for fd tables up to ulimit -n.
204          * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
205          * will use.
206          */
207         info.fd_limit_per_thread = 1 + 1 + 1;
208
209         context = lws_create_context(&info);
210         if (!context) {
211                 lwsl_err("lws init failed\n");
212                 return 1;
213         }
214
215         while (n >= 0 && !interrupted)
216                 n = lws_service(context, 0);
217
218         lws_context_destroy(context);
219         lwsl_user("Completed\n");
220
221         return 0;
222 }