diff --git a/CMakeLists.txt b/CMakeLists.txt index 996a54511..20e739fd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,6 +278,7 @@ option(LWS_WITH_FANALYZER "Enable gcc -fanalyzer if compiler supports" OFF) option(LWS_HTTP_HEADERS_ALL "Override header reduction optimization and include all like older lws versions" OFF) option(LWS_WITH_SUL_DEBUGGING "Enable zombie lws_sul checking on object deletion" OFF) option(LWS_WITH_PLUGINS_API "Build generic lws_plugins apis (see LWS_WITH_PLUGINS to also build protocol plugins)" OFF) +option(LWS_WITH_CONMON "Collect introspectable connection latency stats on individual client connections" ON) if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") option(LWS_WITH_NETLINK "Monitor Netlink for Routing Table changes" ON) diff --git a/READMEs/README.lws_conmon.md b/READMEs/README.lws_conmon.md new file mode 100644 index 000000000..7e148c371 --- /dev/null +++ b/READMEs/README.lws_conmon.md @@ -0,0 +1,36 @@ +## `lws_conmon` apis + +`LWS_WITH_CONMON` build option enables `lws_conmon` apis for user code... these add +some staticistic and information to client connections that can use useful for devices +to introspect how the connection to their servers is actually performing. + +The public apis can be found in `libwebsockets/lws-conmon.h`. + +A struct is provided that describes + + - the peer sockaddr the wsi actually connected to, if any + + - a deep copy of the aggregate DNS results (struct addrinfo list) that the + client had access to for the peer + + - the number of us dns lookup took + + - the number of us the socket connection took + + - the number of us the tls link establishment took + + - the number of us from the transaction request to the first response, if + the protocol has a transaction concept + +Because the user code may want to hold on to the DNS list for longer than the +life of the wsi that originated it, the `lws_conmon_wsi_take()` api allows +the ownership of the allocated list to be transferred to the user code (as +well as copying data out into the user's struct so it no longer has any +dependency on wsi lifetime either). The DNS list copy in the struct must be +released at some point by calling `lws_conmon_release()`, but that +can be at any time afterwards. + +The lws-minimal-http-client example shows how user code can use the apis, build +lws with the `LWS_WITH_CONMON` cmake option and run with `--conmon` to get a +dump of the collected information. + diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index cd9aa2b3b..e60433fe2 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -127,7 +127,7 @@ #cmakedefine LWS_WITH_SYS_ASYNC_DNS #cmakedefine LWS_WITH_BORINGSSL #cmakedefine LWS_WITH_CGI -#cmakedefine LWS_WITH_SECURE_STREAMS_CPP +#cmakedefine LWS_WITH_CONMON #cmakedefine LWS_WITH_CUSTOM_HEADERS #cmakedefine LWS_WITH_DEPRECATED_LWS_DLL #cmakedefine LWS_WITH_DETAILED_LATENCY @@ -177,6 +177,7 @@ #cmakedefine LWS_WITH_RANGES #cmakedefine LWS_WITH_RFC6724 #cmakedefine LWS_WITH_SECURE_STREAMS +#cmakedefine LWS_WITH_SECURE_STREAMS_CPP #cmakedefine LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM #cmakedefine LWS_WITH_SECURE_STREAMS_PROXY_API #cmakedefine LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY diff --git a/include/libwebsockets.h b/include/libwebsockets.h index 63dfa4f32..6c989168f 100644 --- a/include/libwebsockets.h +++ b/include/libwebsockets.h @@ -585,6 +585,10 @@ struct lws; #include +#if defined(LWS_WITH_CONMON) +#include +#endif + #if defined(LWS_ROLE_MQTT) #include #endif diff --git a/include/libwebsockets/lws-client.h b/include/libwebsockets/lws-client.h index ee30e2cd8..10feed21d 100644 --- a/include/libwebsockets/lws-client.h +++ b/include/libwebsockets/lws-client.h @@ -90,6 +90,9 @@ enum lws_client_connect_ssl_connection_flags { LCCSCF_IP_LOW_COST = (1 << 27), /**< set the "minimize monetary cost" bit on the IP packets of this * connection */ + LCCSCF_CONMON = (1 << 28), + /**< If LWS_WITH_CONMON enabled for build, keeps a copy of the + * getaddrinfo results so they can be queried subsequently */ }; /** struct lws_client_connect_info - parameters to connect with when using diff --git a/include/libwebsockets/lws-conmon.h b/include/libwebsockets/lws-conmon.h new file mode 100644 index 000000000..fcee2f1cd --- /dev/null +++ b/include/libwebsockets/lws-conmon.h @@ -0,0 +1,98 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** \defgroup conmon Connection Latency information + * ## Connection Latency information + * + * When LWS_WITH_CONMON is enabled at build, collects detailed statistics + * about the client connection setup latency, available to the connection + * itself + */ +///@{ + +/* enough for 4191us, or just over an hour */ +typedef uint32_t lws_conmon_interval_us_t; + +/* + * Connection latency information... note that not all wsi actually make + * connections, for example h2 streams after the initial one will have 0 + * for everything except ciu_txn_resp. + */ + +struct lws_conmon { + lws_sockaddr46 peer46; + /**< The peer we actually connected to, if any. .peer46.sa4.sa_family + * is either 0 if invalid, or the AF_ */ + + struct addrinfo *dns_results_copy; + /**< NULL, or Allocated copy of dns results, owned by this object and + * freed when object destroyed. + * Only set if client flag LCCSCF_CONMON applied */ + + lws_conmon_interval_us_t ciu_dns; + /**< 0, or if a socket connection, us taken to acquire this DNS response + * + */ + lws_conmon_interval_us_t ciu_sockconn; + /**< 0, or if connection-based, the us interval between the socket + * connect() attempt that succeeded, and the connection setup */ + lws_conmon_interval_us_t ciu_tls; + /**< 0 if no tls, or us taken to establish the tls tunnel */ + lws_conmon_interval_us_t ciu_txn_resp; + /**< 0, or if the protocol supports transactions, the interval between + * sending the transaction request and starting to receive the resp */ +}; + +/** + * lws_conmon_wsi_take() - create a connection latency object from client wsi + * + * \param context: lws wsi + * \param dest: conmon struct to fill + * + * Copies wsi conmon data into the caller's struct. Passes ownership of + * any allocations in the addrinfo list to the caller, lws will not delete that + * any more on wsi close after this call. The caller must call + * lws_conmon_release() on the struct to destroy any addrinfo in the struct + * that is prepared by this eventually but it can defer it as long as it wants. + * + * Other than the addrinfo list, the contents of the returned object are + * completely selfcontained and don't point outside of the object itself, ie, + * everything else in there remains in scope while the object itself does. + */ +LWS_VISIBLE LWS_EXTERN void +lws_conmon_wsi_take(struct lws *wsi, struct lws_conmon *dest); + +/** + * lws_conmon_release() - free any allocations in the conmon struct + * + * \param conmon: pointer to conmon struct + * + * Destroys any allocations in the conmon struct so it can go out of scope. + * It doesn't free \p dest itself, it's designed to clean out a struct that + * is on the stack or embedded in another object. + */ +LWS_VISIBLE LWS_EXTERN void +lws_conmon_release(struct lws_conmon *conmon); + +///@} diff --git a/lib/core-net/CMakeLists.txt b/lib/core-net/CMakeLists.txt index 96c742ed7..fd5692733 100644 --- a/lib/core-net/CMakeLists.txt +++ b/lib/core-net/CMakeLists.txt @@ -70,6 +70,11 @@ if (LWS_WITH_CLIENT) core-net/client/connect4.c core-net/client/sort-dns.c ) + if (LWS_WITH_CONMON) + list(APPEND SOURCES + core-net/client/conmon.c + ) + endif() endif() if (LWS_WITH_SOCKS5 AND NOT LWS_WITHOUT_CLIENT) diff --git a/lib/core-net/client/conmon.c b/lib/core-net/client/conmon.c new file mode 100644 index 000000000..3a233e14b --- /dev/null +++ b/lib/core-net/client/conmon.c @@ -0,0 +1,151 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2019 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Client Connection Latency and DNS reporting + */ + +/* + * We want to allocate copies for and append DNS results that we don't already + * have. We take this approach because a) we may be getting duplicated results + * from multiple DNS servers, and b) we may be getting results stacatto over + * time. + * + * We capture DNS results from either getaddrinfo or ASYNC_DNS the same here, + * before they are sorted and filtered. + * + * Because this is relatively expensive, we only do it on client wsi that + * explicitly indicated that they want it with the LCCSCF_CONMON flag. + */ + +#include + +int +lws_conmon_append_copy_new_dns_results(struct lws *wsi, + const struct addrinfo *cai) +{ + if (!(wsi->flags & LCCSCF_CONMON)) + return 0; + + /* + * Let's go through the incoming guys, seeing if we already have them, + * or if we want to take a copy + */ + + while (cai) { + struct addrinfo *ai = wsi->conmon.dns_results_copy; + char skip = 0; + + /* do we already have this guy? */ + + while (ai) { + + if (ai->ai_family != cai->ai_family && + ai->ai_addrlen != cai->ai_addrlen && + ai->ai_protocol != cai->ai_protocol && + ai->ai_socktype != cai->ai_socktype && + /* either ipv4 or v6 address must match */ + ((ai->ai_family == AF_INET && + ((struct sockaddr_in *)ai->ai_addr)-> + sin_addr.s_addr == + ((struct sockaddr_in *)cai->ai_addr)-> + sin_addr.s_addr) +#if defined(LWS_WITH_IPV6) + || + (ai->ai_family == AF_INET6 && + !memcmp(((struct sockaddr_in6 *)ai->ai_addr)-> + sin6_addr.s6_addr, + ((struct sockaddr_in6 *)cai->ai_addr)-> + sin6_addr.s6_addr, 16)) +#endif + )) { + /* yes, we already got a copy then */ + skip = 1; + break; + } + + ai = ai->ai_next; + } + + if (!skip) { + /* + * No we don't already have a copy of this one, let's + * allocate and append it then + */ + size_t al = sizeof(struct addrinfo) + cai->ai_addrlen; + size_t cl = cai->ai_canonname ? + strlen(cai->ai_canonname) + 1 : 0; + + ai = lws_malloc(al + cl, __func__); + if (!ai) { + lwsl_warn("%s: OOM\n", __func__); + return 1; + } + *ai = *cai; + ai->ai_addr = (struct sockaddr *)&ai[1]; + memcpy(ai->ai_addr, cai->ai_addr, cai->ai_addrlen); + + if (cl) { + ai->ai_canonname = ((char *)ai->ai_addr) + + cai->ai_addrlen; + memcpy(ai->ai_canonname, cai->ai_canonname, cl + 1); + } + ai->ai_next = wsi->conmon.dns_results_copy; + wsi->conmon.dns_results_copy = ai; + } + + cai = cai->ai_next; + } + + return 0; +} + +void +lws_conmon_addrinfo_destroy(struct addrinfo *ai) +{ + while (ai) { + struct addrinfo *ai1 = ai->ai_next; + + lws_free(ai); + ai = ai1; + } +} + +void +lws_conmon_wsi_take(struct lws *wsi, struct lws_conmon *dest) +{ + memcpy(dest, &wsi->conmon, sizeof(*dest)); + dest->peer46 = wsi->sa46_peer; + + /* wsi no longer has to free it... */ + wsi->conmon.dns_results_copy = NULL; +} + +void +lws_conmon_release(struct lws_conmon *conmon) +{ + if (!conmon) + return; + + lws_conmon_addrinfo_destroy(conmon->dns_results_copy); + conmon->dns_results_copy = NULL; +} diff --git a/lib/core-net/client/connect2.c b/lib/core-net/client/connect2.c index 54bf139d4..a3ba542fe 100644 --- a/lib/core-net/client/connect2.c +++ b/lib/core-net/client/connect2.c @@ -57,9 +57,18 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result) hints.ai_family = PF_UNSPEC; } +#if defined(LWS_WITH_CONMON) + wsi->conmon_datum = lws_now_usecs(); +#endif + wsi->dns_reachability = 0; n = getaddrinfo(ads, NULL, &hints, result); +#if defined(LWS_WITH_CONMON) + wsi->conmon.ciu_dns = (lws_conmon_interval_us_t) + (lws_now_usecs() - wsi->conmon_datum); +#endif + /* * Which EAI_* are available and the meanings are highly platform- * dependent, even different linux distros differ. diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c index 311a4e79d..f35b08ac8 100644 --- a/lib/core-net/client/connect3.c +++ b/lib/core-net/client/connect3.c @@ -164,6 +164,12 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads, if (result) { lws_sul_cancel(&wsi->sul_connect_timeout); + +#if defined(LWS_WITH_CONMON) + /* append a copy from before the sorting */ + lws_conmon_append_copy_new_dns_results(wsi, result); +#endif + lws_sort_dns(wsi, result); #if defined(LWS_WITH_SYS_ASYNC_DNS) lws_async_dns_freeaddrinfo(&result); @@ -433,6 +439,11 @@ ads_known: wsi->socket_is_permanently_unusable = 0; m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa, (unsigned int)n); +#if defined(LWS_WITH_CONMON) + wsi->conmon_datum = lws_now_usecs(); + wsi->conmon.ciu_sockconn = 0; +#endif + if (m == -1) { /* * Since we're nonblocking, connect not having completed is not @@ -458,6 +469,11 @@ ads_known: * The connect() failed immediately... */ +#if defined(LWS_WITH_CONMON) + wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t) + (lws_now_usecs() - wsi->conmon_datum); +#endif + lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO); #if defined(_DEBUG) @@ -471,6 +487,8 @@ ads_known: sizeof(nads)); lwsl_info("%s: Connect failed: %s port %d\n", __func__, nads, port); + + wsi->sa46_peer.sa4.sin_family = 0; #if defined(LWS_WITH_UNIX_SOCK) } #endif @@ -514,6 +532,11 @@ conn_good: * The connection has happened */ +#if defined(LWS_WITH_CONMON) + wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t) + (lws_now_usecs() - wsi->conmon_datum); +#endif + #if !defined(LWS_PLAT_OPTEE) { socklen_t salen = sizeof(wsi->sa46_local); diff --git a/lib/core-net/close.c b/lib/core-net/close.c index 806d772b6..c58f6e3c3 100644 --- a/lib/core-net/close.c +++ b/lib/core-net/close.c @@ -46,6 +46,19 @@ __lws_reset_wsi(struct lws *wsi) lws_free_set_NULL(wsi->cli_hostname_copy); +#if defined(LWS_WITH_CONMON) + + if (wsi->conmon.dns_results_copy) { + lws_conmon_addrinfo_destroy(wsi->conmon.dns_results_copy); + wsi->conmon.dns_results_copy = NULL; + } + + wsi->conmon.ciu_dns = + wsi->conmon.ciu_sockconn = + wsi->conmon.ciu_tls = + wsi->conmon.ciu_txn_resp = 0; +#endif + /* * if we have wsi in our transaction queue, if we are closing we * must go through and close all those first diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 86094fb34..234831d1f 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -718,7 +718,12 @@ struct lws { #if defined(LWS_WITH_CLIENT) struct client_info_stash *stash; char *cli_hostname_copy; + +#if defined(LWS_WITH_CONMON) + struct lws_conmon conmon; + lws_usec_t conmon_datum; #endif +#endif /* WITH_CLIENT */ void *user_space; void *opaque_parent_data; @@ -945,6 +950,13 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller) void __lws_free_wsi(struct lws *wsi); +void +lws_conmon_addrinfo_destroy(struct addrinfo *ai); + +int +lws_conmon_append_copy_new_dns_results(struct lws *wsi, + const struct addrinfo *cai); + #if LWS_MAX_SMP > 1 static LWS_INLINE void diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index ba836e958..cac0278ad 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -1463,7 +1463,17 @@ lws_h2_parse_end_of_frame(struct lws *wsi) h2n->swsi->client_h2_alpn = 1; #if defined(LWS_WITH_CLIENT) h2n->swsi->flags = wsi->flags; +#if defined(LWS_WITH_CONMON) + /* sid1 needs to represent the connection experience + * ... we take over responsibility for the DNS list + * copy as well + */ + h2n->swsi->conmon = wsi->conmon; + h2n->swsi->conmon_datum = wsi->conmon_datum; + h2n->swsi->sa46_peer = wsi->sa46_peer; + wsi->conmon.dns_results_copy = NULL; #endif +#endif /* CLIENT */ h2n->swsi->a.protocol = wsi->a.protocol; if (h2n->swsi->user_space && diff --git a/lib/roles/http/client/client-http.c b/lib/roles/http/client/client-http.c index 1e6da7962..f00782b4a 100644 --- a/lib/roles/http/client/client-http.c +++ b/lib/roles/http/client/client-http.c @@ -570,6 +570,11 @@ lws_client_interpret_server_handshake(struct lws *wsi) lws_free_set_NULL(wsi->stash); +#if defined(LWS_WITH_CONMON) + wsi->conmon.ciu_txn_resp = (lws_conmon_interval_us_t) + (lws_now_usecs() - wsi->conmon_datum); +#endif + ah = wsi->http.ah; if (!wsi->do_ws) { /* we are being an http client... @@ -1206,6 +1211,9 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt) lws_callback_on_writable(wsi); lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_http_txn); +#if defined(LWS_WITH_CONMON) + wsi->conmon_datum = lws_now_usecs(); +#endif // puts(pkt); diff --git a/lib/tls/tls-client.c b/lib/tls/tls-client.c index e576f8cef..9b5003f12 100644 --- a/lib/tls/tls-client.c +++ b/lib/tls/tls-client.c @@ -35,6 +35,10 @@ lws_ssl_client_connect1(struct lws *wsi, char *errbuf, size_t len) return -1; case LWS_SSL_CAPABLE_DONE: lws_metrics_caliper_report(wsi->cal_conn, METRES_GO); +#if defined(LWS_WITH_CONMON) + wsi->conmon.ciu_tls = (lws_conmon_interval_us_t) + (lws_now_usecs() - wsi->conmon_datum); +#endif return 1; /* connected */ case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE: lws_callback_on_writable(wsi); @@ -80,6 +84,10 @@ lws_ssl_client_connect2(struct lws *wsi, char *errbuf, size_t len) } lws_metrics_caliper_report(wsi->cal_conn, METRES_GO); +#if defined(LWS_WITH_CONMON) + wsi->conmon.ciu_tls = (lws_conmon_interval_us_t) + (lws_now_usecs() - wsi->conmon_datum); +#endif return 1; } @@ -194,6 +202,9 @@ lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1) lws_metrics_caliper_report(wsi->cal_conn, METRES_GO); lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_conn_tls); +#if defined(LWS_WITH_CONMON) + wsi->conmon_datum = lws_now_usecs(); +#endif n = lws_ssl_client_connect1(wsi, (char *)wsi->a.context->pt[(int)wsi->tsi].serv_buf, wsi->a.context->pt_serv_buf_size); diff --git a/minimal-examples/http-client/minimal-http-client/minimal-http-client.c b/minimal-examples/http-client/minimal-http-client/minimal-http-client.c index aba159162..87d5a8e29 100644 --- a/minimal-examples/http-client/minimal-http-client/minimal-http-client.c +++ b/minimal-examples/http-client/minimal-http-client/minimal-http-client.c @@ -16,7 +16,7 @@ #include #include -static int interrupted, bad = 1, status; +static int interrupted, bad = 1, status, conmon; #if defined(LWS_WITH_HTTP2) static int long_poll; #endif @@ -28,6 +28,40 @@ static const lws_retry_bo_t retry = { .secs_since_valid_hangup = 10, }; +#if defined(LWS_WITH_CONMON) +void +dump_conmon_data(struct lws *wsi) +{ + const struct addrinfo *ai; + struct lws_conmon cm; + char ads[48]; + + lws_conmon_wsi_take(wsi, &cm); + + lws_sa46_write_numeric_address(&cm.peer46, ads, sizeof(ads)); + lwsl_notice("%s: peer %s, dns: %uus, sockconn: %uus, tls: %uus, txn_resp: %uus\n", + __func__, ads, + (unsigned int)cm.ciu_dns, + (unsigned int)cm.ciu_sockconn, + (unsigned int)cm.ciu_tls, + (unsigned int)cm.ciu_txn_resp); + + ai = cm.dns_results_copy; + while (ai) { + lws_sa46_write_numeric_address((lws_sockaddr46 *)ai->ai_addr, ads, sizeof(ads)); + lwsl_notice("%s: DNS %s\n", __func__, ads); + ai = ai->ai_next; + } + + /* + * This destroys the DNS list in the lws_conmon that we took + * responsibility for when we used lws_conmon_wsi_take() + */ + + lws_conmon_release(&cm); +} +#endif + static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) @@ -39,6 +73,12 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char *)in : "(null)"); interrupted = 1; + lws_cancel_service(lws_get_context(wsi)); + +#if defined(LWS_WITH_CONMON) + if (conmon) + dump_conmon_data(wsi); +#endif break; case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: @@ -128,6 +168,10 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, interrupted = 1; bad = status != 200; lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ +#if defined(LWS_WITH_CONMON) + if (conmon) + dump_conmon_data(wsi); +#endif break; default: @@ -234,6 +278,13 @@ system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, i.manual_initial_tx_credit); } +#if defined(LWS_WITH_CONMON) + if (lws_cmdline_option(a->argc, a->argv, "--conmon")) { + i.ssl_connection |= LCCSCF_CONMON; + conmon = 1; + } +#endif + /* the default validity check is 5m / 5m10s... -v = 3s / 10s */ if (lws_cmdline_option(a->argc, a->argv, "-v"))