Asterisk - The Open Source Telephony Project GIT-master-70eff7f
Loading...
Searching...
No Matches
res_websocket_client.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2025, Sangoma Technologies Corporation
5 *
6 * George Joseph <gjoseph@sangoma.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*** MODULEINFO
20 <support_level>core</support_level>
21 ***/
22
23/*** DOCUMENTATION
24 <configInfo name="res_websocket_client" language="en_US">
25 <synopsis>Websocket Client Configuration</synopsis>
26 <configFile name="websocket_client.conf">
27 <configObject name="websocket_client">
28 <since>
29 <version>20.15.0</version>
30 <version>21.10.0</version>
31 <version>22.5.0</version>
32 </since>
33 <synopsis>Websocket Client Configuration</synopsis>
34 <see-also>
35 <ref type="link">/Configuration/Channel-Drivers/WebSocket/</ref>
36 <ref type="link">/Configuration/Interfaces/Asterisk-REST-Interface-ARI/ARI-Outbound-Websockets/</ref>
37 </see-also>
38 <description>
39 <para>
40 These config objects are currently shared by the following Asterisk capabilities:
41 </para>
42 <enumlist>
43 <enum name="chan_websocket"><para>The WebSocket channel driver.</para></enum>
44 <enum name="res_ari"><para>ARI Outbound WebSockets.</para></enum>
45 </enumlist>
46 <para>
47 They may have more specific information or restrictions on the parameters below.
48 </para>
49 <example title="websocket_client.conf">
50;
51; A connection for use by chan_websocket
52[media_connection1]
53type = websocket_client
54uri = ws://localhost:8787
55protocols = media
56username = media_username
57password = media_password
58connection_type = per_call_config
59connection_timeout = 500
60reconnect_interval = 500
61reconnect_attempts = 5
62tls_enabled = no
63;
64; A TLS connection for use by ARI Outbound Websocket
65[ari_connection1]
66type = websocket_client
67uri = wss://localhost:8765
68protocols = ari
69username = some_username
70password = some_password
71connection_type = persistent
72connection_timeout = 500
73reconnect_interval = 500
74reconnect_attempts = 5
75tls_enabled = yes
76ca_list_file = /etc/pki/tls/cert.pem
77verify_server_cert = no
78verify_server_hostname = no
79 </example>
80 </description>
81 <configOption name="type">
82 <since>
83 <version>20.15.0</version>
84 <version>21.10.0</version>
85 <version>22.5.0</version>
86 </since>
87 <synopsis>Must be "websocket_client".</synopsis>
88 </configOption>
89 <configOption name="uri">
90 <since>
91 <version>20.15.0</version>
92 <version>21.10.0</version>
93 <version>22.5.0</version>
94 </since>
95 <synopsis>Full URI to remote server.</synopsis>
96 </configOption>
97 <configOption name="protocols">
98 <since>
99 <version>20.15.0</version>
100 <version>21.10.0</version>
101 <version>22.5.0</version>
102 </since>
103 <synopsis>Comma separated list of protocols acceptable to the server.</synopsis>
104 </configOption>
105 <configOption name="username">
106 <since>
107 <version>20.15.0</version>
108 <version>21.10.0</version>
109 <version>22.5.0</version>
110 </since>
111 <synopsis>Server authentication username if required.</synopsis>
112 </configOption>
113 <configOption name="password">
114 <since>
115 <version>20.15.0</version>
116 <version>21.10.0</version>
117 <version>22.5.0</version>
118 </since>
119 <synopsis>Server authentication password if required.</synopsis>
120 </configOption>
121 <configOption name="connection_type">
122 <since>
123 <version>20.15.0</version>
124 <version>21.10.0</version>
125 <version>22.5.0</version>
126 </since>
127 <synopsis>Single persistent connection or per-call configuration.</synopsis>
128 <description>
129 <enumlist>
130 <enum name="persistent"><para>Single persistent connection for all calls.</para></enum>
131 <enum name="per_call_config"><para>New connection for each call to the Stasis() dialplan app.</para></enum>
132 </enumlist>
133 </description>
134 </configOption>
135 <configOption name="connection_timeout">
136 <since>
137 <version>20.15.0</version>
138 <version>21.10.0</version>
139 <version>22.5.0</version>
140 </since>
141 <synopsis>Connection timeout (ms).</synopsis>
142 </configOption>
143 <configOption name="reconnect_attempts">
144 <since>
145 <version>20.15.0</version>
146 <version>21.10.0</version>
147 <version>22.5.0</version>
148 </since>
149 <synopsis>On failure, how many times should reconnection be attempted?</synopsis>
150 <description>
151 <para>
152 For per_call connections, this is the number of
153 (re)connection attempts to make before returning an
154 and terminating the call. Persistent connections
155 always retry forever but this setting will control
156 how often failure messages are logged.
157 </para>
158 </description>
159 </configOption>
160 <configOption name="reconnect_interval">
161 <since>
162 <version>20.15.0</version>
163 <version>21.10.0</version>
164 <version>22.5.0</version>
165 </since>
166 <synopsis>How often should reconnection be attempted (ms)?</synopsis>
167 </configOption>
168 <configOption name="tls_enabled">
169 <since>
170 <version>20.15.0</version>
171 <version>21.10.0</version>
172 <version>22.5.0</version>
173 </since>
174 <synopsis>Enable TLS</synopsis>
175 </configOption>
176 <configOption name="ca_list_file">
177 <since>
178 <version>20.15.0</version>
179 <version>21.10.0</version>
180 <version>22.5.0</version>
181 </since>
182 <synopsis>File containing the server's CA certificate. (optional)</synopsis>
183 </configOption>
184 <configOption name="ca_list_path">
185 <since>
186 <version>20.15.0</version>
187 <version>21.10.0</version>
188 <version>22.5.0</version>
189 </since>
190 <synopsis>Path to a directory containing one or more hashed CA certificates. (optional)</synopsis>
191 </configOption>
192 <configOption name="cert_file">
193 <since>
194 <version>20.15.0</version>
195 <version>21.10.0</version>
196 <version>22.5.0</version>
197 </since>
198 <synopsis>File containing a client certificate. (optional)</synopsis>
199 </configOption>
200 <configOption name="priv_key_file">
201 <since>
202 <version>20.15.0</version>
203 <version>21.10.0</version>
204 <version>22.5.0</version>
205 </since>
206 <synopsis>File containing the client's private key. (optional)</synopsis>
207 </configOption>
208 <configOption name="verify_server_cert">
209 <since>
210 <version>20.15.0</version>
211 <version>21.10.0</version>
212 <version>22.5.0</version>
213 </since>
214 <synopsis>If set to true, verify the server's certificate. (optional)</synopsis>
215 </configOption>
216 <configOption name="verify_server_hostname">
217 <since>
218 <version>20.15.0</version>
219 <version>21.10.0</version>
220 <version>22.5.0</version>
221 </since>
222 <synopsis>If set to true, verify that the server's hostname matches the common name in it's certificate. (optional)</synopsis>
223 </configOption>
224 <configOption name="proxy_host">
225 <since>
226 <version>20.21.0</version>
227 <version>22.11.0</version>
228 <version>23.5.0</version>
229 </since>
230 <synopsis>Proxy host including port for outbound proxy if required. (optional)</synopsis>
231 <description>
232 <para>
233 If an outbound proxy is required to reach the websocket server,
234 specify a host in the form <literal>&lt;host&gt;:&lt;port7gt;</literal>.
235 Currently only http (non-TLS) proxies are supported although the tunnelled
236 connection to the websocket server can have TLS enabled.
237 </para>
238 </description>
239 </configOption>
240 <configOption name="proxy_username">
241 <since>
242 <version>20.21.0</version>
243 <version>22.11.0</version>
244 <version>23.5.0</version>
245 </since>
246 <synopsis>Proxy authentication username if required. (optional)</synopsis>
247 </configOption>
248 <configOption name="proxy_password">
249 <since>
250 <version>20.21.0</version>
251 <version>22.11.0</version>
252 <version>23.5.0</version>
253 </since>
254 <synopsis>Proxy authentication password if required. (optional)</synopsis>
255 </configOption>
256 <configOption name="enable_tcp_keepalives">
257 <since>
258 <version>20.21.0</version>
259 <version>22.11.0</version>
260 <version>23.5.0</version>
261 </since>
262 <synopsis>Enable TCP Keepalives. (optional)</synopsis>
263 </configOption>
264 <configOption name="tcp_keepalive_time">
265 <since>
266 <version>20.21.0</version>
267 <version>22.11.0</version>
268 <version>23.5.0</version>
269 </since>
270 <synopsis>Start sending keepalives when no data has been sent for this many seconds. (optional)</synopsis>
271 </configOption>
272 <configOption name="tcp_keepalive_interval">
273 <since>
274 <version>20.21.0</version>
275 <version>22.11.0</version>
276 <version>23.5.0</version>
277 </since>
278 <synopsis>Send keepalives at this interval in seconds. (optional)</synopsis>
279 <description>
280 <para>
281 If a reply isn't received by the time the next keepalive is due
282 to be sent, it's considered missed so this option also controls
283 how long it takes to detect a failure.
284 </para>
285 </description>
286 </configOption>
287 <configOption name="tcp_keepalive_probes">
288 <since>
289 <version>20.21.0</version>
290 <version>22.11.0</version>
291 <version>23.5.0</version>
292 </since>
293 <synopsis>Close the connection after this many missed replies. (optional)</synopsis>
294 <description>
295 <para>
296 If a reply isn't received by the time the next keepalive is due
297 to be sent, it's considered missed. The time to detect a failure
298 is therefore between (probes * interval) and
299 ((probes + 1) * interval) seconds. If the connection closes
300 and reconnect_interval reconnect_attempts are set, a new connection
301 will be attempted using those parameters.
302 </para>
303 </description>
304 </configOption>
305 <configOption name="enable_pingpongs">
306 <since>
307 <version>20.21.0</version>
308 <version>22.11.0</version>
309 <version>23.5.0</version>
310 </since>
311 <synopsis>Enable WebSocket PING/PONGs.. (optional)</synopsis>
312 </configOption>
313 <configOption name="pingpong_interval">
314 <since>
315 <version>20.21.0</version>
316 <version>22.11.0</version>
317 <version>23.5.0</version>
318 </since>
319 <synopsis>Send WebSocket PINGs at this interval in seconds. (optional)</synopsis>
320 <description>
321 <para>
322 If a reply isn't received by the time the next PING is due
323 to be sent, it's considered missed so this option also controls
324 how long it takes to detect a failure.
325 </para>
326 </description>
327 </configOption>
328 <configOption name="pingpong_probes">
329 <since>
330 <version>20.21.0</version>
331 <version>22.11.0</version>
332 <version>23.5.0</version>
333 </since>
334 <synopsis>Close the connection after this many missed PONG replies. (optional)</synopsis>
335 <description>
336 <para>
337 If a reply isn't received by the time the next PING is due
338 to be sent, it's considered missepingd. The time to detect a failure
339 is therefore between (probes * interval) and
340 ((probes + 1) * interval) seconds. If the connection closes
341 and reconnect_interval reconnect_attempts are set, a new connection
342 will be attempted using those parameters.
343 </para>
344 </description>
345 </configOption>
346 </configObject>
347 </configFile>
348 </configInfo>
349***/
350
351
352#include "asterisk.h"
353
354#include "asterisk/module.h"
355#include "asterisk/astobj2.h"
356#include "asterisk/strings.h"
357#include "asterisk/vector.h"
359
360static struct ast_sorcery *sorcery = NULL;
361
363 const char *uri_params)
364{
365 ast_string_field_set(wc, uri_params, uri_params);
366}
367
369 void *lock_obj, const char *display_name, enum ast_websocket_result *result)
370{
371 int reconnect_counter = wc->reconnect_attempts;
372 char *uri = NULL;
373
374 if (ast_strlen_zero(display_name)) {
375 display_name = ast_sorcery_object_get_id(wc);
376 }
377
378 if (!ast_strlen_zero(wc->uri_params)) {
379 /*
380 * If the configured URI doesn't already contain parameters, we append the
381 * new ones to the URI path component with '?'. If it does, we append the
382 * new ones to the existing ones with a '&'.
383 */
384 char sep = '?';
385 uri = ast_alloca(strlen(wc->uri) + strlen(wc->uri_params) + 2);
386 if (strchr(wc->uri, '?')) {
387 sep = '&';
388 }
389 sprintf(uri, "%s%c%s", wc->uri, sep, wc->uri_params); /*Safe */
390 }
391
392 while (1) {
393 struct ast_websocket *astws = NULL;
395 .uri = S_OR(uri, wc->uri),
396 .protocols = wc->protocols,
397 .username = wc->username,
398 .password = wc->password,
399 .timeout = wc->connect_timeout,
400 .suppress_connection_msgs = 1,
401 .proxy_host = wc->proxy_host,
402 .proxy_username = wc->proxy_username,
403 .proxy_password = wc->proxy_password,
405 .tcp_keepalive_time = wc->tcp_keepalive_time,
406 .tcp_keepalive_interval = wc->tcp_keepalive_interval,
407 .tcp_keepalive_probes = wc->tcp_keepalive_probes,
408 .pingpongs = wc->pingpongs,
409 .pingpong_interval = wc->pingpong_interval,
410 .pingpong_probes = wc->pingpong_probes,
411 .tls_cfg = NULL,
412 };
413
414 if (lock_obj) {
415 ao2_lock(lock_obj);
416 }
417
418 if (wc->tls_enabled) {
419 /*
420 * tls_cfg and its contents are freed automatically
421 * by res_http_websocket when the connection ends.
422 * We create it even if tls is not enabled to we can
423 * suppress connection error messages and print our own.
424 */
425 options.tls_cfg = ast_calloc(1, sizeof(*options.tls_cfg));
426 if (!options.tls_cfg) {
427 if (lock_obj) {
428 ao2_unlock(lock_obj);
429 }
430 return NULL;
431 }
432 /* TLS options */
433 options.tls_cfg->enabled = wc->tls_enabled;
434 options.tls_cfg->cafile = ast_strdup(wc->ca_list_file);
435 options.tls_cfg->capath = ast_strdup(wc->ca_list_path);
436 options.tls_cfg->certfile = ast_strdup(wc->cert_file);
437 options.tls_cfg->pvtfile = ast_strdup(wc->priv_key_file);
440 }
441
443 if (astws && *result == WS_OK) {
444 if (lock_obj) {
445 ao2_unlock(lock_obj);
446 }
447 return astws;
448 }
449
450 reconnect_counter--;
451 if (reconnect_counter <= 0) {
454 "%s: Websocket connection to %s failed after %d tries: %s%s%s%s. Retrying in %d ms.\n",
455 display_name,
456 wc->uri,
459 errno ? " (" : "",
460 errno ? strerror(errno) : "",
461 errno ? ")" : "",
463 );
464 } else {
466 "%s: Websocket connection to %s failed after %d tries: %s%s%s%s. Hanging up after exhausting retries.\n",
467 display_name,
468 wc->uri,
471 errno ? " (" : "",
472 errno ? strerror(errno) : "",
473 errno ? ")" : ""
474 );
475 }
476 break;
477 }
478
479 if (lock_obj) {
480 ao2_unlock(lock_obj);
481 }
482 usleep(wc->reconnect_interval * 1000);
483 }
484
485 if (lock_obj) {
486 ao2_unlock(lock_obj);
487 }
488
489 return NULL;
490}
491
492
493
494static void wc_dtor(void *obj)
495{
496 struct ast_websocket_client *wc = obj;
497
498 ast_debug(3, "%s: Disposing of websocket client config\n",
501}
502
503static void *wc_alloc(const char *id)
504{
505 struct ast_websocket_client *wc = NULL;
506
507 wc = ast_sorcery_generic_alloc(sizeof(*wc), wc_dtor);
508 if (!wc) {
509 return NULL;
510 }
511
512 if (ast_string_field_init(wc, 1024) != 0) {
513 ao2_cleanup(wc);
514 return NULL;
515 }
516
517 if (ast_string_field_init_extended(wc, uri_params) != 0) {
518 ao2_cleanup(wc);
519 return NULL;
520 }
521
522 if (ast_string_field_init_extended(wc, proxy_host) != 0) {
523 ao2_cleanup(wc);
524 return NULL;
525 }
526
527 if (ast_string_field_init_extended(wc, proxy_username) != 0) {
528 ao2_cleanup(wc);
529 return NULL;
530 }
531
532 if (ast_string_field_init_extended(wc, proxy_password) != 0) {
533 ao2_cleanup(wc);
534 return NULL;
535 }
536
537 ast_debug(2, "%s: Allocated websocket client config\n", id);
538 return wc;
539}
540
542 struct ast_variable *var, void *obj)
543{
544 struct ast_websocket_client *ws = obj;
545
546 if (strcasecmp(var->value, "persistent") == 0) {
548 } else if (strcasecmp(var->value, "per_call_config") == 0) {
550 } else {
551 return -1;
552 }
553
554 return 0;
555}
556
557static int websocket_client_connection_type_to_str(const void *obj, const intptr_t *args, char **buf)
558{
559 const struct ast_websocket_client *wc = obj;
560
562 *buf = ast_strdup("persistent");
564 *buf = ast_strdup("per_call_config");
565 } else {
566 return -1;
567 }
568
569 return 0;
570}
571
572/*
573 * Can't use INT_MIN because it's an expression
574 * and macro substitutions using stringify can't
575 * handle that.
576 */
577#define DEFAULT_RECONNECT_ATTEMPTS -2147483648
578
579static int wc_apply(const struct ast_sorcery *sorcery, void *obj)
580{
581 struct ast_websocket_client *wc = obj;
582 const char *id = ast_sorcery_object_get_id(wc);
583 int res = 0;
584
585 ast_debug(3, "%s: Applying config\n", id);
586
587 if (ast_strlen_zero(wc->uri)) {
588 ast_log(LOG_WARNING, "%s: Websocket client missing uri\n", id);
589 res = -1;
590 }
591
592 if (!ast_strlen_zero(wc->proxy_host)) {
593 char *host = NULL;
594 char *port = NULL;
595 char *s = ast_strdupa(wc->proxy_host);
596 if (!ast_sockaddr_split_hostport(s, &host, &port, PARSE_PORT_REQUIRE)) {
597 ast_log(LOG_WARNING, "%s: proxy_host '%s' is missing a port\n", id, wc->proxy_host);
598 res = -1;
599 }
600 }
601
602 if (wc->tcp_keepalives) {
604 ast_log(LOG_WARNING, "%s: tcp_keepalive_time, tcp_keepalive_interval and tcp_keepalive_probes must all be non-zero\n", id);
605 res = -1;
606 }
607 }
608
609 if (wc->pingpongs) {
610 if (!wc->pingpong_interval || !wc->pingpong_probes) {
611 ast_log(LOG_WARNING, "%s: pingpong_interval and pingpong_probes must be non-zero\n", id);
612 res = -1;
613 }
614 }
615 if (res != 0) {
616 ast_log(LOG_WARNING, "%s: Websocket client configuration failed\n", id);
617 } else {
618 ast_debug(3, "%s: Websocket client configuration succeeded\n", id);
619
622 wc->reconnect_attempts = INT_MAX;
623 } else {
624 wc->reconnect_attempts = 4;
625 }
626 }
627 }
628
629 return res;
630}
631
633{
634 if (!sorcery) {
635 return NULL;
636 }
637
638 return ast_sorcery_retrieve_by_fields(sorcery, "websocket_client",
640}
641
643{
644 if (!sorcery) {
645 return NULL;
646 }
647
648 return ast_sorcery_retrieve_by_id(sorcery, "websocket_client", id);
649}
650
652 struct ast_websocket_client *old_wc,
653 struct ast_websocket_client *new_wc)
654{
656 const char *new_id = ast_sorcery_object_get_id(new_wc);
657 RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
658 struct ast_variable *v = NULL;
659 int res = 0;
660 int changes_found = 0;
661
662 ast_debug(2, "%s: Detecting changes\n", new_id);
663
664 res = ast_sorcery_diff(sorcery, old_wc, new_wc, &changes);
665 if (res != 0) {
666 ast_log(LOG_WARNING, "%s: Failed to create changeset\n", new_id);
668 }
669
670 for (v = changes; v; v = v->next) {
671 changes_found = 1;
672 ast_debug(2, "%s: %s changed to %s\n", new_id, v->name, v->value);
673 if (ast_strings_equal(v->name, "connection_type")) {
675 } else if (ast_strings_equal(v->name, "uri")) {
676 changed |= AST_WS_CLIENT_FIELD_URI;
677 } else if (ast_strings_equal(v->name, "protocols")) {
679 } else if (ast_strings_equal(v->name, "username")) {
681 } else if (ast_strings_equal(v->name, "password")) {
683 } else if (ast_strings_equal(v->name, "tls_enabled")) {
685 } else if (ast_strings_equal(v->name, "ca_list_file")) {
687 } else if (ast_strings_equal(v->name, "ca_list_path")) {
689 } else if (ast_strings_equal(v->name, "cert_file")) {
691 } else if (ast_strings_equal(v->name, "priv_key_file")) {
693 } else if (ast_strings_equal(v->name, "reconnect_interval")) {
695 } else if (ast_strings_equal(v->name, "reconnect_attempts")) {
697 } else if (ast_strings_equal(v->name, "connection_timeout")) {
699 } else if (ast_strings_equal(v->name, "verify_server_cert")) {
701 } else if (ast_strings_equal(v->name, "verify_server_hostname")) {
703 } else if (ast_strings_equal(v->name, "proxy_host")) {
705 } else if (ast_strings_equal(v->name, "proxy_username")) {
707 } else if (ast_strings_equal(v->name, "proxy_password")) {
709 } else if (ast_strings_equal(v->name, "enable_tcp_keepalives")) {
711 } else if (ast_strings_equal(v->name, "tcp_keepalive_time")) {
713 } else if (ast_strings_equal(v->name, "tcp_keepalive_interval")) {
715 } else if (ast_strings_equal(v->name, "tcp_keepalive_probes")) {
717 } else if (ast_strings_equal(v->name, "enable_pingpongs")) {
719 } else if (ast_strings_equal(v->name, "pingpong_interval")) {
721 } else if (ast_strings_equal(v->name, "pingpong_probes")) {
723 } else {
724 ast_debug(2, "%s: Unknown change %s\n", new_id, v->name);
725 }
726 }
727
728 if (!changes_found) {
729 ast_debug(2, "%s: No changes found %p %p\n", new_id,
730 old_wc,new_wc);
731 }
732 return changed;
733
734}
735
737{
738 if (!sorcery || !callbacks) {
739 return -1;
740 }
741
742 if (ast_sorcery_observer_add(sorcery, "websocket_client", callbacks)) {
743 ast_log(LOG_ERROR, "Failed to register websocket client observers\n");
744 return -1;
745 }
746
747 return 0;
748}
749
751{
752 if (!sorcery || !callbacks) {
753 return;
754 }
755
756 ast_sorcery_observer_remove(sorcery, "websocket_client", callbacks);
757}
758
759
760static int load_module(void)
761{
762 ast_debug(2, "Initializing Websocket Client Configuration\n");
764 if (!sorcery) {
765 ast_log(LOG_ERROR, "Failed to open sorcery\n");
766 return -1;
767 }
768
769 ast_sorcery_apply_default(sorcery, "websocket_client", "config",
770 "websocket_client.conf,criteria=type=websocket_client");
771
772 if (ast_sorcery_object_register(sorcery, "websocket_client", wc_alloc,
773 NULL, wc_apply)) {
774 ast_log(LOG_ERROR, "Failed to register websocket_client object with sorcery\n");
776 sorcery = NULL;
777 return -1;
778 }
779
780 ast_sorcery_object_field_register(sorcery, "websocket_client", "type", "", OPT_NOOP_T, 0, 0);
781 ast_sorcery_register_cust(websocket_client, connection_type, "");
786 ast_sorcery_register_sf(websocket_client, ast_websocket_client, ca_list_file, ca_list_file, "");
787 ast_sorcery_register_sf(websocket_client, ast_websocket_client, ca_list_path, ca_list_path, "");
789 ast_sorcery_register_sf(websocket_client, ast_websocket_client, priv_key_file, priv_key_file, "");
790 ast_sorcery_register_bool(websocket_client, ast_websocket_client, tls_enabled, tls_enabled, "no");
791 ast_sorcery_register_bool(websocket_client, ast_websocket_client, verify_server_cert, verify_server_cert, "yes");
792 ast_sorcery_register_bool(websocket_client, ast_websocket_client, verify_server_hostname, verify_server_hostname, "yes");
793 ast_sorcery_register_int(websocket_client, ast_websocket_client, connection_timeout, connect_timeout, 500);
794 ast_sorcery_register_int(websocket_client, ast_websocket_client, reconnect_attempts, reconnect_attempts, 4);
795 ast_sorcery_register_int(websocket_client, ast_websocket_client, reconnect_interval, reconnect_interval, 500);
797 ast_sorcery_register_sf(websocket_client, ast_websocket_client, proxy_username, proxy_username, "");
798 ast_sorcery_register_sf(websocket_client, ast_websocket_client, proxy_password, proxy_password, "");
799 ast_sorcery_register_bool(websocket_client, ast_websocket_client, enable_tcp_keepalives, tcp_keepalives, "no");
800 ast_sorcery_register_uint(websocket_client, ast_websocket_client, tcp_keepalive_time, tcp_keepalive_time, 20);
801 ast_sorcery_register_uint(websocket_client, ast_websocket_client, tcp_keepalive_interval, tcp_keepalive_interval, 20);
802 ast_sorcery_register_uint(websocket_client, ast_websocket_client, tcp_keepalive_probes, tcp_keepalive_probes, 3);
803 ast_sorcery_register_bool(websocket_client, ast_websocket_client, enable_pingpongs, pingpongs, "no");
804 ast_sorcery_register_uint(websocket_client, ast_websocket_client, pingpong_interval, pingpong_interval, 20);
805 ast_sorcery_register_uint(websocket_client, ast_websocket_client, pingpong_probes, pingpong_probes, 3);
806
808
809 return 0;
810}
811
812static int reload_module(void)
813{
814 ast_debug(2, "Reloading Websocket Client Configuration\n");
816
817 return 0;
818}
819
821{
822 ast_debug(2, "Reloading Websocket Client Configuration\n");
823 if (sorcery) {
825 }
826
827 return 0;
828}
829
830static int unload_module(void)
831{
832 ast_debug(2, "Unloading Websocket Client Configuration\n");
833 if (sorcery) {
835 sorcery = NULL;
836 }
837 return 0;
838}
839
841 .support_level = AST_MODULE_SUPPORT_CORE,
842 .load = load_module,
843 .unload = unload_module,
845 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
846 .requires = "res_http_websocket",
#define var
Definition ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition astmm.h:288
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_log
Definition astobj2.c:42
#define ao2_cleanup(obj)
Definition astobj2.h:1934
#define ao2_unlock(a)
Definition astobj2.h:729
#define ao2_lock(a)
Definition astobj2.h:717
static PGresult * result
Definition cel_pgsql.c:84
@ OPT_NOOP_T
Type for a default handler that should do nothing.
char buf[BUFSIZE]
Definition eagi_proxy.c:66
ast_websocket_result
Result code for a websocket client.
@ WS_OK
struct ast_websocket *AST_OPTIONAL_API_NAME() ast_websocket_client_create_with_options(struct ast_websocket_client_options *options, enum ast_websocket_result *result)
Create, and connect, a websocket client using given options.
@ AST_WS_TYPE_CLIENT_PER_CALL_CONFIG
@ AST_WS_TYPE_CLIENT_PERSISTENT
const char *AST_OPTIONAL_API_NAME() ast_websocket_result_to_str(enum ast_websocket_result result)
Convert a websocket result code to a string.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_WARNING
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition module.h:340
@ AST_MODULE_SUPPORT_CORE
Definition module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
Splits a string into its host and port components.
Definition netsock2.c:164
static int reload(void)
struct @509 callbacks
static struct @522 args
static int websocket_client_connection_type_to_str(const void *obj, const intptr_t *args, char **buf)
void ast_websocket_client_add_uri_params(struct ast_websocket_client *wc, const char *uri_params)
Add additional parameters to the URI.
struct ao2_container * ast_websocket_client_retrieve_all(void)
Retrieve a container of all websocket client objects.
int ast_websocket_client_reload(void)
Force res_websocket_client to reload its configuration.
static void * wc_alloc(const char *id)
enum ast_ws_client_fields ast_websocket_client_get_field_diff(struct ast_websocket_client *old_wc, struct ast_websocket_client *new_wc)
Detect changes between two websocket client configurations.
static int wc_apply(const struct ast_sorcery *sorcery, void *obj)
struct ast_websocket_client * ast_websocket_client_retrieve_by_id(const char *id)
Retrieve a websocket client object by ID.
static int reload_module(void)
static struct ast_sorcery * sorcery
static int websocket_client_connection_type_from_str(const struct aco_option *opt, struct ast_variable *var, void *obj)
static int load_module(void)
#define DEFAULT_RECONNECT_ATTEMPTS
void ast_websocket_client_observer_remove(const struct ast_sorcery_observer *callbacks)
Remove sorcery observers for websocket client events.
int ast_websocket_client_observer_add(const struct ast_sorcery_observer *callbacks)
Add sorcery observers for websocket client events.
static int unload_module(void)
static void wc_dtor(void *obj)
struct ast_websocket * ast_websocket_client_connect(struct ast_websocket_client *wc, void *lock_obj, const char *display_name, enum ast_websocket_result *result)
Connect to a websocket server using the configured authentication, retry and TLS options.
#define NULL
Definition resample.c:96
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
Definition sorcery.h:1500
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition sorcery.c:2381
void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Remove an observer from a specific object type.
Definition sorcery.c:2487
#define ast_sorcery_register_cust(object, option, def_value)
Register a custom field within an object.
Definition sorcery.h:1767
@ AST_RETRIEVE_FLAG_MULTIPLE
Return all matching objects.
Definition sorcery.h:120
@ AST_RETRIEVE_FLAG_ALL
Perform no matching, return all objects.
Definition sorcery.h:123
void ast_sorcery_load(const struct ast_sorcery *sorcery)
Inform any wizards to load persistent objects.
Definition sorcery.c:1441
#define ast_sorcery_register_uint(object, structure, option, field, def_value)
Register an unsigned int field as type OPT_UINT_T within an object.
Definition sorcery.h:1728
int ast_sorcery_diff(const struct ast_sorcery *sorcery, const void *original, const void *modified, struct ast_variable **changes)
Create a changeset of two objects.
Definition sorcery.c:1869
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition sorcery.c:1917
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition sorcery.h:837
void ast_sorcery_reload(const struct ast_sorcery *sorcery)
Inform any wizards to reload persistent objects.
Definition sorcery.c:1472
int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Add an observer to a specific object type.
Definition sorcery.c:2455
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition sorcery.c:1792
#define ast_sorcery_register_int(object, structure, option, field, def_value)
Register an int field as type OPT_INT_T within an object.
Definition sorcery.h:1710
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition sorcery.h:955
#define ast_sorcery_apply_default(sorcery, type, name, data)
Definition sorcery.h:476
#define ast_sorcery_register_bool(object, structure, option, field, def_value)
Register a boolean field as type OPT_YESNO_T within an object.
Definition sorcery.h:1684
#define ast_sorcery_open()
Open a new sorcery structure.
Definition sorcery.h:406
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition sorcery.c:1961
#define ast_sorcery_register_sf(object, structure, option, field, def_value)
Register a stringfield field as type OPT_STRINGFIELD_T within an object.
Definition sorcery.h:1746
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_string_field_init_extended(x, field)
Initialize an extended string field.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
String manipulation functions.
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition strings.c:238
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
Generic container type.
Interface for a sorcery object type observer.
Definition sorcery.h:332
Full structure for sorcery.
Definition sorcery.c:231
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Options used for a websocket client.
unsigned int pingpong_interval
unsigned int reconnect_interval
const ast_string_field uri
enum ast_websocket_type connection_type
const ast_string_field ca_list_path
const ast_string_field cert_file
const ast_string_field password
const ast_string_field username
const ast_string_field ca_list_file
unsigned int tcp_keepalive_time
const ast_string_field protocols
unsigned int tcp_keepalive_interval
unsigned int tcp_keepalive_probes
const ast_string_field priv_key_file
unsigned int reconnect_attempts
Structure definition for session.
@ AST_SSL_DONT_VERIFY_SERVER
Definition tcptls.h:69
@ AST_SSL_IGNORE_COMMON_NAME
Definition tcptls.h:71
static struct test_options options
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981
#define ast_set2_flag(p, value, flag)
Definition utils.h:95
Vector container support.
ast_ws_client_fields
@ AST_WS_CLIENT_FIELD_RECONNECT_INTERVAL
@ AST_WS_CLIENT_FIELD_NONE
@ AST_WS_CLIENT_FIELD_URI
@ AST_WS_CLIENT_FIELD_PROTOCOLS
@ AST_WS_CLIENT_FIELD_TCP_KEEPALIVES
@ AST_WS_CLIENT_FIELD_CA_LIST_PATH
@ AST_WS_CLIENT_FIELD_TLS_ENABLED
@ AST_WS_CLIENT_FIELD_PROXY_PASSWORD
@ AST_WS_CLIENT_FIELD_TCP_KEEPALIVE_TIME
@ AST_WS_CLIENT_FIELD_CA_LIST_FILE
@ AST_WS_CLIENT_FIELD_PINGPONG_PROBES
@ AST_WS_CLIENT_FIELD_USERNAME
@ AST_WS_CLIENT_FIELD_VERIFY_SERVER_CERT
@ AST_WS_CLIENT_FIELD_PASSWORD
@ AST_WS_CLIENT_FIELD_PROXY_USERNAME
@ AST_WS_CLIENT_FIELD_CONNECTION_TIMEOUT
@ AST_WS_CLIENT_FIELD_PINGPONG_INTERVAL
@ AST_WS_CLIENT_FIELD_RECONNECT_ATTEMPTS
@ AST_WS_CLIENT_FIELD_CONNECTION_TYPE
@ AST_WS_CLIENT_FIELD_TCP_KEEPALIVE_PROBES
@ AST_WS_CLIENT_FIELD_VERIFY_SERVER_HOSTNAME
@ AST_WS_CLIENT_FIELD_PINGPONGS
@ AST_WS_CLIENT_FIELD_TCP_KEEPALIVE_INTERVAL
@ AST_WS_CLIENT_FIELD_CERT_FILE
@ AST_WS_CLIENT_FIELD_PRIV_KEY_FILE
@ AST_WS_CLIENT_FIELD_PROXY_HOST