1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

lws_validity: unified connection validity tracking

Refactor everything around ping / pong handling in ws and h2, so there
is instead a protocol-independent validity lws_sul tracking how long it
has been since the last exchange that confirms the operation of the
network connection in both directions.

Clean out periodic role callback and replace the last two role users
with discrete lws_sul for each pt.
This commit is contained in:
Andy Green 2019-09-18 13:09:32 +01:00
parent 503bb8f8c9
commit f9f6bb66fe
41 changed files with 559 additions and 219 deletions

View file

@ -16,6 +16,20 @@ various scenarios, CC0-licensed (public domain) for cut-and-paste, allow you to
News
----
## Connection Validity tracking
Lws now allows you to apply a policy for how long a network connection may go
without seeing something on it that confirms it's still valid in the sense of
passing traffic cohernetly both ways. There's a global policy in the context
which defaults to 5m before it produces a PING if possible, and 5m10 before
the connection will be hung up, user code can override this in the context,
vhost (for server) and client connection info (for client).
An api `lws_validity_confirmed(wsi)` is provided so user code can indicate
that it observed traffic that must mean the connection is passing traffic in
both directions to and from the peer. In the absence of these confirmations
lws will generate PINGs and take PONGs as the indication of validity.
## Async DNS support
Master now provides optional Asynchronous (ie, nonblocking) DNS resolving. Enable

View file

@ -0,0 +1,98 @@
# `lws_retry_bo_t` client connection management
This struct sets the policy for delays between retries, and for
how long a connection may be 'idle' before it first tries to
ping / pong on it to confirm it's up, or drops the connection
if still idle.
## Retry rate limiting
You can define a table of ms-resolution delays indexed by which
connection attempt number is ongoing, this is pointed to by
`.retry_ms_table` with `.retry_ms_table_count` containing the
count of table entries.
`.conceal_count` is the number of retries that should be allowed
before informing the parent that the connection has failed. If it's
greater than the number of entries in the table, the last entry is
reused for the additional attempts.
`.jitter_percent` controls how much additional random delay is
added to the actual interval to be used... this stops a lot of
devices all synchronizing when they try to connect after a single
trigger event and DDoS-ing the server.
The struct and apis are provided for user implementations, lws does
not offer reconnection itself.
## Connection validity management
Lws has a sophisticated idea of connection validity and the need to
reconfirm that a connection is still operable if proof of validity
has not been seen for some time. It concerns itself only with network
connections rather than streams, for example, it only checks h2
network connections rather than the individual streams inside (which
is consistent with h2 PING frames only working at the network stream
level itself).
Connections may fail in a variety of ways, these include that no traffic
at all is passing, or, eg, incoming traffic may be received but no
outbound traffic is making it to the network, and vice versa. In the
case that tx is not failing at any point but just isn't getting sent,
endpoints can potentially kid themselves that since "they are sending"
and they are seeing RX, the combination means the connection is valid.
This can potentially continue for a long time if the peer is not
performing keepalives.
"Connection validity" is proven when one side sends something and later
receives a response that can only have been generated by the peer
receiving what was just sent. This can happen for some kinds of user
transactions on any stream using the connection, or by sending PING /
PONG protocol packets where the PONG is only returned for a received PING.
To ensure that the generated traffic is only sent when necessary, user
code can report for any stream that it has observed a transaction amounting
to a proof of connection validity using an api. This resets the timer for
the associated network connection before the validity is considered
expired.
`.secs_since_valid_ping` in the retry struct sets the number of seconds since
the last validity after which lws will issue a protocol-specific PING of some
kind on the connection. `.secs_since_valid_hangup` specifies how long lws
will allow the connection to go without a confirmation of validity before
simply hanging up on it.
## Defaults
The context defaults to having a 5m valid ping interval and 5m10s hangup interval,
ie, it'll send a ping at 5m idle if the protocol supports it, and if no response
validating the connection arrives in another 10s, hang up the connection.
User code can set this in the context creation info and can individually set the
retry policy per vhost for server connections. Client connections can set it
per connection in the client creation info `.retry_and_idle_policy`.
## Checking for h2 and ws
Check using paired minimal examples with the -v flag on one or both sides to get a
small validity check period set of 3s / 10s
Also give, eg, -d1039 to see info level debug logging
### h2
```
$ lws-minimal-http-server-h2-long-poll -v
$ lws-minimal-http-client -l -v
```
### ws
```
$ lws-minimal-ws-server-h2 -s -v
$ lws-minimal-ws-client-ping -n --server 127.0.0.1 --port 7681 -v
```

View file

@ -529,6 +529,7 @@ struct lws_tokens;
struct lws_vhost;
struct lws;
#include <libwebsockets/lws-retry.h>
#include <libwebsockets/lws-system.h>
#include <libwebsockets/lws-detailed-latency.h>
#include <libwebsockets/lws-ws-close.h>
@ -565,7 +566,6 @@ struct lws;
#include <libwebsockets/lws-lwsac.h>
#include <libwebsockets/lws-fts.h>
#include <libwebsockets/lws-diskcache.h>
#include <libwebsockets/lws-retry.h>
#include <libwebsockets/lws-sequencer.h>
#include <libwebsockets/abstract/abstract.h>

View file

@ -133,6 +133,11 @@ struct lws_client_connect_info {
* an lws_seq_t.
*/
const lws_retry_bo_t *retry_and_idle_policy;
/**< optional retry and idle policy to apply to this connection.
* Currently only the idle parts are applied to the connection.
*/
/* Add new things just above here ---^
* This is part of the ABI, don't needlessly break compatibility
*

View file

@ -688,6 +688,10 @@ struct lws_context_creation_info {
* collected for each read and write */
const char *detailed_latency_filepath;
/**< CONTEXT: NULL, or filepath to put latency data into */
const lws_retry_bo_t *retry_and_idle_policy;
/**< VHOST: optional retry and idle policy to apply to this vhost.
* Currently only the idle parts are applied to the connections.
*/
/* Add new things just above here ---^
* This is part of the ABI, don't needlessly break compatibility

View file

@ -23,10 +23,12 @@
*/
typedef struct lws_retry_bo {
const uint32_t *retry_ms_table; /* base delay in ms */
uint16_t retry_ms_table_count; /* entries in table */
uint16_t conceal_count; /* max retries to conceal */
uint8_t jitter_percent; /* % additional random jitter */
const uint32_t *retry_ms_table; /* base delay in ms */
uint16_t retry_ms_table_count; /* entries in table */
uint16_t conceal_count; /* max retries to conceal */
uint16_t secs_since_valid_ping; /* idle before PING issued */
uint16_t secs_since_valid_hangup; /* idle before hangup conn */
uint8_t jitter_percent; /* % additional random jitter */
} lws_retry_bo_t;
/**

View file

@ -229,4 +229,26 @@ LWS_VISIBLE LWS_EXTERN void
lws_sul_schedule(struct lws_context *context, int tsi,
lws_sorted_usec_list_t *sul, sul_cb_t cb, lws_usec_t us);
/*
* lws_validity_confirmed() - reset the validity timer for a network connection
*
* \param wsi: the connection that saw traffic proving the connection valid
*
* Network connections are subject to intervals defined by the context, the
* vhost if server connections, or the client connect info if a client
* connection. If the connection goes longer than the specified time since
* last observing traffic that can only happen if traffic is passing in both
* directions, then lws will try to create a PING transaction on the network
* connection.
*
* If the connection reaches the specified `.secs_since_valid_hangup` time
* still without any proof of validity, the connection will be closed.
*
* If the PONG comes, or user code observes traffic that satisfies the proof
* that both directions are passing traffic to the peer and calls this api,
* the connection validity timer is reset and the scheme repeats.
*/
LWS_VISIBLE LWS_EXTERN void
lws_validity_confirmed(struct lws *wsi);
///@}

View file

@ -71,6 +71,7 @@ lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi)
new_wsi->context = vhost->context;
new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
new_wsi->retry_policy = vhost->retry_policy;
#if defined(LWS_WITH_DETAILED_LATENCY)
if (vhost->context->detailed_latency_cb)

View file

@ -60,6 +60,10 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
wsi->context = i->context;
wsi->desc.sockfd = LWS_SOCK_INVALID;
wsi->seq = i->seq;
if (i->retry_and_idle_policy)
wsi->retry_policy = i->retry_and_idle_policy;
else
wsi->retry_policy = &i->context->default_retry;
#if defined(LWS_WITH_DETAILED_LATENCY)
if (i->context->detailed_latency_cb)

View file

@ -354,6 +354,9 @@ struct lws_context_per_thread {
#if defined(LWS_PLAT_UNIX)
lws_sorted_usec_list_t sul_plat;
#endif
#if defined(LWS_ROLE_CGI)
lws_sorted_usec_list_t sul_cgi;
#endif
#if defined(LWS_WITH_STATS)
uint64_t lws_stats[LWSSTATS_SIZE];
int updated;
@ -508,6 +511,8 @@ struct lws_vhost {
struct lws_context *context;
struct lws_vhost *vhost_next;
const lws_retry_bo_t *retry_policy;
struct lws *lserv_wsi;
const char *name;
const char *iface;
@ -584,7 +589,6 @@ struct lws {
#endif
#if defined(LWS_ROLE_WS)
struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */
lws_sorted_usec_list_t sul_ping;
#endif
#if defined(LWS_ROLE_DBUS)
struct _lws_dbus_mode_related dbus;
@ -606,6 +610,8 @@ struct lws {
lws_sorted_usec_list_t sul_timeout;
lws_sorted_usec_list_t sul_hrtimer;
lws_sorted_usec_list_t sul_validity;
struct lws_dll2 dll_buflist; /* guys with pending rxflow */
struct lws_dll2 same_vh_protocol;
#if defined(LWS_WITH_SYS_ASYNC_DNS)
@ -627,6 +633,7 @@ struct lws {
const struct lws_role_ops *role_ops;
const struct lws_protocols *protocol;
struct lws_sequencer *seq; /* associated sequencer if any */
const lws_retry_bo_t *retry_policy;
#if defined(LWS_WITH_THREADPOOL)
struct lws_threadpool_task *tp_task;
@ -710,6 +717,7 @@ struct lws {
unsigned int proxied_ws_parent:1;
unsigned int do_bind:1;
unsigned int oom4:1;
unsigned int validity_hup:1;
unsigned int could_have_pending:1; /* detect back-to-back writes */
unsigned int outer_will_close:1;
@ -961,9 +969,6 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d);
LWS_VISIBLE LWS_EXTERN int
lws_plat_plugins_destroy(struct lws_context * context);
LWS_EXTERN void
lws_restart_ws_ping_pong_timer(struct lws *wsi);
struct lws *
lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd);
@ -1197,6 +1202,11 @@ lws_threadpool_tsi_context(struct lws_context *context, int tsi);
void
__lws_wsi_remove_from_sul(struct lws *wsi);
void
lws_validity_confirmed(struct lws *wsi);
void
_lws_validity_confirmed_role(struct lws *wsi);
int
lws_seq_pt_init(struct lws_context_per_thread *pt);

View file

@ -475,6 +475,12 @@ lws_create_vhost(struct lws_context *context,
#if !defined(LWS_PLAT_FREERTOS) && !defined(OPTEE_TA) && !defined(WIN32)
vh->bind_iface = info->bind_iface;
#endif
/* apply the context default lws_retry */
if (info->retry_and_idle_policy)
vh->retry_policy = info->retry_and_idle_policy;
else
vh->retry_policy = &context->default_retry;
/*
* let's figure out how many protocols the user is handing us, using the

View file

@ -35,9 +35,7 @@ __lws_wsi_remove_from_sul(struct lws *wsi)
// lws_dll2_describe(&pt->pt_sul_owner, "pre-remove");
lws_dll2_remove(&wsi->sul_timeout.list);
lws_dll2_remove(&wsi->sul_hrtimer.list);
#if defined(LWS_ROLE_WS)
lws_dll2_remove(&wsi->sul_ping.list);
#endif
lws_dll2_remove(&wsi->sul_validity.list);
// lws_dll2_describe(&pt->pt_sul_owner, "post-remove");
}
@ -227,7 +225,7 @@ lws_sul_timed_callback_vh_protocol_cb(lws_sorted_usec_list_t *sul)
__lws_timed_callback_remove(tvp->vhost, tvp);
}
LWS_VISIBLE LWS_EXTERN int
int
lws_timed_callback_vh_protocol_us(struct lws_vhost *vh,
const struct lws_protocols *prot, int reason,
lws_usec_t us)
@ -267,7 +265,7 @@ lws_timed_callback_vh_protocol_us(struct lws_vhost *vh,
return 0;
}
LWS_VISIBLE LWS_EXTERN int
int
lws_timed_callback_vh_protocol(struct lws_vhost *vh,
const struct lws_protocols *prot, int reason,
int secs)
@ -275,3 +273,84 @@ lws_timed_callback_vh_protocol(struct lws_vhost *vh,
return lws_timed_callback_vh_protocol_us(vh, prot, reason,
((lws_usec_t)secs) * LWS_US_PER_SEC);
}
static void
lws_validity_cb(lws_sorted_usec_list_t *sul)
{
struct lws *wsi = lws_container_of(sul, struct lws, sul_validity);
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
const lws_retry_bo_t *rbo = wsi->retry_policy;
/* one of either the ping or hangup validity threshold was crossed */
if (wsi->validity_hup) {
lwsl_info("%s: wsi %p: validity too old\n", __func__, wsi);
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"validity timeout");
return;
}
/* schedule a protocol-dependent ping */
lwsl_info("%s: wsi %p: scheduling validity check\n", __func__, wsi);
if (wsi->role_ops && wsi->role_ops->issue_keepalive)
wsi->role_ops->issue_keepalive(wsi, 0);
/*
* We arrange to come back here after the additional ping to hangup time
* and do the hangup, unless we get validated (by, eg, a PONG) and
* reset the timer
*/
assert(rbo->secs_since_valid_hangup > rbo->secs_since_valid_ping);
wsi->validity_hup = 1;
__lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_validity,
((uint64_t)rbo->secs_since_valid_hangup -
rbo->secs_since_valid_ping) * LWS_US_PER_SEC);
}
/*
* The role calls this back to actually confirm validity on a particular wsi
* (which may not be the original wsi)
*/
void
_lws_validity_confirmed_role(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
const lws_retry_bo_t *rbo = wsi->retry_policy;
if (!rbo || !rbo->secs_since_valid_hangup)
return;
wsi->validity_hup = 0;
wsi->sul_validity.cb = lws_validity_cb;
wsi->validity_hup = rbo->secs_since_valid_ping >=
rbo->secs_since_valid_hangup;
lwsl_info("%s: wsi %p: setting validity timer %ds (hup %d)\n",
__func__, wsi,
wsi->validity_hup ? rbo->secs_since_valid_hangup :
rbo->secs_since_valid_ping,
wsi->validity_hup);
__lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_validity,
((uint64_t)(wsi->validity_hup ?
rbo->secs_since_valid_hangup :
rbo->secs_since_valid_ping)) * LWS_US_PER_SEC);
}
void
lws_validity_confirmed(struct lws *wsi)
{
/*
* This may be a stream inside a muxed network connection... leave it
* to the role to figure out who actually needs to understand their
* validity was confirmed.
*/
if (wsi->role_ops && wsi->role_ops->issue_keepalive)
wsi->role_ops->issue_keepalive(wsi, 1);
}

View file

@ -28,9 +28,13 @@
#define LWS_BUILD_HASH "unknown-build-hash"
#endif
static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH;
#if defined(LWS_WITH_NETWORK)
/* in ms */
static uint32_t default_backoff_table[] = { 1000, 3000, 9000, 17000 };
#endif
/**
* lws_get_library_version: get version and git hash library built from
*
@ -164,11 +168,6 @@ lws_create_context(const struct lws_context_creation_info *info)
#endif
#endif
#if defined(LWS_ROLE_H2)
role_ops_h2.init_context(context, info);
#endif
#if LWS_MAX_SMP > 1
lws_mutex_refcount_init(&context->mr);
#endif
@ -373,6 +372,15 @@ lws_create_context(const struct lws_context_creation_info *info)
context->count_threads;
#if defined(LWS_WITH_NETWORK)
context->default_retry.retry_ms_table = default_backoff_table;
context->default_retry.conceal_count =
context->default_retry.retry_ms_table_count =
LWS_ARRAY_SIZE(default_backoff_table);
context->default_retry.jitter_percent = 20;
context->default_retry.secs_since_valid_ping = 300;
context->default_retry.secs_since_valid_hangup = 310;
/*
* Allocate the per-thread storage for scratchpad buffers,
* and header data pool
@ -405,6 +413,12 @@ lws_create_context(const struct lws_context_creation_info *info)
#if defined(LWS_WITH_SEQUENCER)
lws_seq_pt_init(&context->pt[n]);
#endif
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) {
if (ar->pt_init_destroy)
ar->pt_init_destroy(context, info,
&context->pt[n], 0);
} LWS_FOR_EVERY_AVAILABLE_ROLE_END;
}
lwsl_info(" Threads: %d each %d fds\n", context->count_threads,
@ -634,6 +648,10 @@ lws_context_destroy3(struct lws_context *context)
#if defined(LWS_WITH_SEQUENCER)
lws_seq_destroy_all_on_pt(pt);
#endif
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) {
if (ar->pt_init_destroy)
ar->pt_init_destroy(context, NULL, pt, 1);
} LWS_FOR_EVERY_AVAILABLE_ROLE_END;
if (context->event_loop_ops->destroy_pt)
context->event_loop_ops->destroy_pt(context, n);

View file

@ -274,6 +274,7 @@ struct lws_context {
#if defined(LWS_WITH_NETWORK)
struct lws_context_per_thread pt[LWS_MAX_SMP];
lws_retry_bo_t default_retry;
#if defined(LWS_WITH_HTTP2)
struct http2_settings set;

View file

@ -44,9 +44,6 @@ lws_sul_plat_unix(lws_sorted_usec_list_t *sul)
struct lws_context_per_thread *pt =
lws_container_of(sul, struct lws_context_per_thread, sul_plat);
struct lws_context *context = pt->context;
#if defined(LWS_ROLE_CGI) || defined(LWS_ROLE_DBUS)
time_t now = time(NULL);
#endif
#if !defined(LWS_NO_DAEMONIZE)
/* if our parent went down, don't linger around */
@ -83,13 +80,6 @@ lws_sul_plat_unix(lws_sorted_usec_list_t *sul)
lws_context_unlock(context);
#endif
#if defined(LWS_ROLE_CGI)
role_ops_cgi.periodic_checks(context, 0, now);
#endif
#if defined(LWS_ROLE_DBUS)
role_ops_dbus.periodic_checks(context, 0, now);
#endif
__lws_sul_insert(&pt->pt_sul_owner, &pt->sul_plat, 30 * LWS_US_PER_SEC);
}
#endif

View file

@ -1064,7 +1064,7 @@ handled:
return 0;
}
LWS_EXTERN int
int
lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
{
struct lws_cgi **pcgi, *cgi = NULL;

View file

@ -68,16 +68,6 @@ rops_handle_POLLOUT_cgi(struct lws *wsi)
return LWS_HP_RET_USER_SERVICE;
}
static int
rops_periodic_checks_cgi(struct lws_context *context, int tsi, time_t now)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
lws_cgi_kill_terminated(pt);
return 0;
}
static int
rops_destroy_role_cgi(struct lws *wsi)
{
@ -94,14 +84,43 @@ rops_destroy_role_cgi(struct lws *wsi)
return 0;
}
static void
lws_cgi_sul_cb(lws_sorted_usec_list_t *sul)
{
struct lws_context_per_thread *pt = lws_container_of(sul,
struct lws_context_per_thread, sul_cgi);
lws_cgi_kill_terminated(pt);
__lws_sul_insert(&pt->pt_sul_owner, &pt->sul_cgi,
3 * LWS_US_PER_SEC);
}
static int
rops_pt_init_destroy_cgi(struct lws_context *context,
const struct lws_context_creation_info *info,
struct lws_context_per_thread *pt, int destroy)
{
if (!destroy) {
pt->sul_cgi.cb = lws_cgi_sul_cb;
__lws_sul_insert(&pt->pt_sul_owner, &pt->sul_cgi,
3 * LWS_US_PER_SEC);
} else
lws_dll2_remove(&pt->sul_cgi.list);
return 0;
}
struct lws_role_ops role_ops_cgi = {
/* role name */ "cgi",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* pt_init_destroy */ rops_pt_init_destroy_cgi,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ rops_periodic_checks_cgi,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_cgi,
/* handle_POLLOUT */ rops_handle_POLLOUT_cgi,
@ -117,6 +136,7 @@ struct lws_role_ops role_ops_cgi = {
/* destroy_role */ rops_destroy_role_cgi,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
/* issue_keepalive */ NULL,
/* adoption_cb clnt, srv */ { 0, 0 },
/* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },

View file

@ -472,23 +472,18 @@ rops_handle_POLLIN_dbus(struct lws_context_per_thread *pt, struct lws *wsi,
return LWS_HPI_RET_HANDLED;
}
static int
rops_periodic_checks_dbus(struct lws_context *context, int tsi, time_t now)
static void
lws_dbus_sul_cb(lws_sorted_usec_list_t *sul)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
/*
* locking shouldn't be needed here, because periodic_checks is called
* from the tsi-specific service thread context, and only the same
* service thread can modify stuff on the same pt.
*/
struct lws_context_per_thread *pt = lws_container_of(sul,
struct lws_context_per_thread, dbus.sul);
lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx,
lws_dll2_get_head(&pt->dbus.timer_list_owner)) {
struct lws_role_dbus_timer *r = lws_container_of(rdt,
struct lws_role_dbus_timer, timer_list);
if (now > r->fire) {
if (time(NULL) > r->fire) {
lwsl_notice("%s: firing timer\n", __func__);
dbus_timeout_handle(r->data);
lws_dll2_remove(rdt);
@ -496,6 +491,24 @@ rops_periodic_checks_dbus(struct lws_context *context, int tsi, time_t now)
}
} lws_end_foreach_dll_safe(rdt, nx);
__lws_sul_insert(&pt->pt_sul_owner, &pt->dbus.sul,
3 * LWS_US_PER_SEC);
}
static int
rops_pt_init_destroy_dbus(struct lws_context *context,
const struct lws_context_creation_info *info,
struct lws_context_per_thread *pt, int destroy)
{
if (!destroy) {
pt->dbus.sul.cb = lws_dbus_sul_cb;
__lws_sul_insert(&pt->pt_sul_owner, &pt->dbus.sul,
3 * LWS_US_PER_SEC);
} else
lws_dll2_remove(&pt->dbus.sul.list);
return 0;
}
@ -503,10 +516,9 @@ struct lws_role_ops role_ops_dbus = {
/* role name */ "dbus",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* pt_init_destroy */ rops_pt_init_destroy_dbus,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ rops_periodic_checks_dbus,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_dbus,
/* handle_POLLOUT */ NULL,
@ -522,6 +534,7 @@ struct lws_role_ops role_ops_dbus = {
/* destroy_role */ NULL,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
/* issue_keepalive */ NULL,
/* adoption_cb clnt, srv */ { 0, 0 },
/* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },

View file

@ -38,6 +38,7 @@ struct lws_role_dbus_timer {
struct lws_pt_role_dbus {
struct lws_dll2_owner timer_list_owner;
lws_sorted_usec_list_t sul;
};
struct _lws_dbus_mode_related {

View file

@ -1105,24 +1105,23 @@ rops_close_kill_connection_h1(struct lws *wsi, enum lws_close_status reason)
}
int
rops_init_context_h1(struct lws_context *context,
const struct lws_context_creation_info *info)
rops_pt_init_destroy_h1(struct lws_context *context,
const struct lws_context_creation_info *info,
struct lws_context_per_thread *pt, int destroy)
{
/*
* We only want to do this once... we will do it if no h2 support
* otherwise let h2 ops do it.
*/
#if !defined(LWS_ROLE_H2) && defined(LWS_WITH_SERVER)
int n;
for (n = 0; n < context->count_threads; n++) {
struct lws_context_per_thread *pt = &context->pt[n];
if (!destroy) {
pt->sul_ah_lifecheck.cb = lws_sul_http_ah_lifecheck;
__lws_sul_insert(&pt->pt_sul_owner, &pt->sul_ah_lifecheck,
30 * LWS_US_PER_SEC);
}
} else
lws_dll2_remove(&pt->sul_ah_lifecheck.list);
#endif
return 0;
@ -1132,10 +1131,9 @@ struct lws_role_ops role_ops_h1 = {
/* role name */ "h1",
/* alpn id */ "http/1.1",
/* check_upgrades */ NULL,
/* init_context */ rops_init_context_h1,
/* pt_init_destroy */ rops_pt_init_destroy_h1,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_h1,
/* handle_POLLOUT */ rops_handle_POLLOUT_h1,
@ -1159,6 +1157,7 @@ struct lws_role_ops role_ops_h1 = {
#else
NULL,
#endif
/* issue_keepalive */ NULL,
/* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED,
LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED },
/* rx_cb clnt, srv */ { LWS_CALLBACK_RECEIVE_CLIENT_HTTP,

View file

@ -120,7 +120,7 @@ lws_h2_dump_settings(struct http2_settings *set)
}
#endif
static struct lws_h2_protocol_send *
struct lws_h2_protocol_send *
lws_h2_new_pps(enum lws_h2_protocol_send_type type)
{
struct lws_h2_protocol_send *pps = lws_malloc(sizeof(*pps), "pps");
@ -212,6 +212,9 @@ lws_wsi_server_new(struct lws_vhost *vh, struct lws *parent_wsi,
wsi->vhost->conn_stats.h2_subs++;
#endif
/* get the ball rolling */
lws_validity_confirmed(wsi);
lwsl_info("%s: %p new ch %p, sid %d, usersp=%p, tx cr %d, "
"peer_credit %d (nwsi tx_cr %d)\n",
__func__, parent_wsi, wsi, sid, wsi->user_space,
@ -727,17 +730,27 @@ int lws_h2_do_pps_send(struct lws *wsi)
break;
}
break;
/*
* h2 only has PING... ACK = 0 = ping, ACK = 1 = pong
*/
case LWS_H2_PPS_PING:
case LWS_H2_PPS_PONG:
lwsl_debug("sending PONG\n");
if (pps->type == LWS_H2_PPS_PING)
lwsl_info("sending PING\n");
else {
lwsl_info("sending PONG\n");
flags = LWS_H2_FLAG_SETTINGS_ACK;
}
memcpy(&set[LWS_PRE], pps->u.ping.ping_payload, 8);
n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_PING,
LWS_H2_FLAG_SETTINGS_ACK,
n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_PING, flags,
LWS_H2_STREAM_ID_MASTER, 8,
&set[LWS_PRE]);
if (n != 8) {
lwsl_info("send %d %d\n", n, m);
if (n != 8)
goto bail;
}
break;
case LWS_H2_PPS_GOAWAY:
@ -1599,6 +1612,7 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
case LWS_H2_FRAME_TYPE_PING:
if (h2n->flags & LWS_H2_FLAG_SETTINGS_ACK) { // ack
lws_validity_confirmed(wsi);
} else {/* they're sending us a ping request */
struct lws_h2_protocol_send *pps =
lws_h2_new_pps(LWS_H2_PPS_PONG);
@ -1737,6 +1751,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
lwsl_info("http2: %p: established\n", wsi);
lwsi_set_state(wsi, LRS_H2_AWAIT_SETTINGS);
lws_validity_confirmed(wsi);
h2n->count = 0;
wsi->h2.tx_cr = 65535;
@ -2339,6 +2354,8 @@ lws_h2_ws_handshake(struct lws *wsi)
(void *)hit->cgienv, 0))
return 1;
lws_validity_confirmed(wsi);
return 0;
}

View file

@ -538,33 +538,32 @@ rops_init_vhost_h2(struct lws_vhost *vh,
return 0;
}
static int
rops_init_context_h2(struct lws_context *context,
const struct lws_context_creation_info *info)
int
rops_pt_init_destroy_h2(struct lws_context *context,
const struct lws_context_creation_info *info,
struct lws_context_per_thread *pt, int destroy)
{
int n;
context->set = lws_h2_stock_settings;
#if defined(LWS_WITH_SERVER)
/*
* We only want to do this once... we will do it if we are built
* otherwise h1 ops will do it (or nobody if no http at all)
*/
for (n = 0; n < context->count_threads; n++) {
struct lws_context_per_thread *pt = &context->pt[n];
#if !defined(LWS_ROLE_H2) && defined(LWS_WITH_SERVER)
if (!destroy) {
pt->sul_ah_lifecheck.cb = lws_sul_http_ah_lifecheck;
__lws_sul_insert(&pt->pt_sul_owner, &pt->sul_ah_lifecheck,
30 * LWS_US_PER_SEC);
}
} else
lws_dll2_remove(&pt->sul_ah_lifecheck.list);
#endif
return 0;
}
static lws_fileofs_t
rops_tx_credit_h2(struct lws *wsi)
{
@ -1242,14 +1241,51 @@ rops_alpn_negotiated_h2(struct lws *wsi, const char *alpn)
return 0;
}
static int
rops_issue_keepalive_h2(struct lws *wsi, int isvalid)
{
struct lws *nwsi = lws_get_network_wsi(wsi);
struct lws_h2_protocol_send *pps;
uint64_t us = lws_now_usecs();
if (isvalid) {
_lws_validity_confirmed_role(nwsi);
return 0;
}
/*
* We can only send these frames on the network connection itself...
* we shouldn't be tracking validity on anything else
*/
assert(wsi == nwsi);
pps = lws_h2_new_pps(LWS_H2_PPS_PING);
if (!pps)
return 1;
/*
* The peer is defined to copy us back the unchanged payload in another
* PING frame this time with ACK set. So by sending that out with the
* current time, it's an interesting opportunity to learn the effective
* RTT on the link when the PONG comes in, plus or minus the time to
* schedule the PPS.
*/
memcpy(pps->u.ping.ping_payload, &us, 8);
lws_pps_schedule(nwsi, pps);
return 0;
}
struct lws_role_ops role_ops_h2 = {
/* role name */ "h2",
/* alpn id */ "h2",
/* check_upgrades */ rops_check_upgrades_h2,
/* init_context */ rops_init_context_h2,
/* pt_init_destroy */ rops_pt_init_destroy_h2,
/* init_vhost */ rops_init_vhost_h2,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_h2,
/* handle_POLLOUT */ rops_handle_POLLOUT_h2,
@ -1265,6 +1301,7 @@ struct lws_role_ops role_ops_h2 = {
/* destroy_role */ rops_destroy_role_h2,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
/* issue_keepalive */ rops_issue_keepalive_h2,
/* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED,
LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED },
/* rx cb clnt, srv */ { LWS_CALLBACK_RECEIVE_CLIENT_HTTP,

View file

@ -211,6 +211,7 @@ enum lws_h2_protocol_send_type {
LWS_PPS_NONE,
LWS_H2_PPS_MY_SETTINGS,
LWS_H2_PPS_ACK_SETTINGS,
LWS_H2_PPS_PING,
LWS_H2_PPS_PONG,
LWS_H2_PPS_GOAWAY,
LWS_H2_PPS_RST_STREAM,
@ -406,3 +407,5 @@ int
lws_handle_POLLOUT_event_h2(struct lws *wsi);
int
lws_read_h2(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
struct lws_h2_protocol_send *
lws_h2_new_pps(enum lws_h2_protocol_send_type type);

View file

@ -176,10 +176,9 @@ struct lws_role_ops role_ops_listen = {
/* role name */ "listen",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* pt_init_destroy */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_listen,
/* handle_POLLOUT */ rops_handle_POLLOUT_listen,
@ -195,6 +194,7 @@ struct lws_role_ops role_ops_listen = {
/* destroy_role */ NULL,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
/* issue_keepalive */ NULL,
/* adoption_cb clnt, srv */ { 0, 0 },
/* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },

View file

@ -72,10 +72,9 @@ struct lws_role_ops role_ops_pipe = {
/* role name */ "pipe",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* pt_init_destroy */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_pipe,
/* handle_POLLOUT */ NULL,
@ -91,6 +90,7 @@ struct lws_role_ops role_ops_pipe = {
/* destroy_role */ NULL,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
/* issue_keepalive */ NULL,
/* adoption_cb clnt, srv */ { 0, 0 },
/* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },

View file

@ -180,16 +180,14 @@ struct lws_role_ops {
*/
int (*check_upgrades)(struct lws *wsi);
/* role-specific context init during context creation */
int (*init_context)(struct lws_context *context,
const struct lws_context_creation_info *info);
int (*pt_init_destroy)(struct lws_context *context,
const struct lws_context_creation_info *info,
struct lws_context_per_thread *pt, int destroy);
/* role-specific per-vhost init during vhost creation */
int (*init_vhost)(struct lws_vhost *vh,
const struct lws_context_creation_info *info);
/* role-specific per-vhost destructor during vhost destroy */
int (*destroy_vhost)(struct lws_vhost *vh);
/* generic 1Hz callback for the role itself */
int (*periodic_checks)(struct lws_context *context, int tsi,
time_t now);
/* chance for the role to force POLLIN without network activity */
int (*service_flag_pending)(struct lws_context *context, int tsi);
/* an fd using this role has POLLIN signalled */
@ -233,6 +231,9 @@ struct lws_role_ops {
* case ret 0 = OK, 1 = fail, wsi needs freeing, -1 = fail, wsi freed */
int (*client_bind)(struct lws *wsi,
const struct lws_client_connect_info *i);
/* isvalid = 0: request a role-specific keepalive (PING etc)
* = 1: reset any related validity timer */
int (*issue_keepalive)(struct lws *wsi, int isvalid);
/*
* the callback reasons for adoption for client, server

View file

@ -81,10 +81,9 @@ struct lws_role_ops role_ops_raw_file = {
/* role name */ "raw-file",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* pt_init_destroy */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_raw_file,
/* handle_POLLOUT */ NULL,
@ -100,6 +99,7 @@ struct lws_role_ops role_ops_raw_file = {
/* destroy_role */ NULL,
/* adoption_bind */ rops_adoption_bind_raw_file,
/* client_bind */ NULL,
/* issue_keepalive */ NULL,
/* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_ADOPT_FILE,
LWS_CALLBACK_RAW_ADOPT_FILE },
/* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX_FILE,

View file

@ -188,10 +188,9 @@ struct lws_role_ops role_ops_raw_proxy = {
/* role name */ "raw-proxy",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* pt_init_destroy */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_raw_proxy,
/* handle_POLLOUT */ rops_handle_POLLOUT_raw_proxy,
@ -207,6 +206,7 @@ struct lws_role_ops role_ops_raw_proxy = {
/* destroy_role */ NULL,
/* adoption_bind */ rops_adoption_bind_raw_proxy,
/* client_bind */ rops_client_bind_raw_proxy,
/* issue_keepalive */ NULL,
/* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_ADOPT,
LWS_CALLBACK_RAW_PROXY_SRV_ADOPT },
/* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_RX,

View file

@ -219,10 +219,9 @@ struct lws_role_ops role_ops_raw_skt = {
/* role name */ "raw-skt",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* pt_init_destroy */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_raw_skt,
/* handle_POLLOUT */ NULL,
@ -246,6 +245,7 @@ struct lws_role_ops role_ops_raw_skt = {
#else
NULL,
#endif
/* issue_keepalive */ NULL,
/* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_CONNECTED,
LWS_CALLBACK_RAW_ADOPT },
/* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX,

View file

@ -31,7 +31,6 @@
int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
struct lws_ext_pm_deflate_rx_ebufs pmdrx;
unsigned short close_code;
@ -488,20 +487,7 @@ ping_drop:
lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
wsi->ws->rx_ubuf_head);
if (wsi->ws->await_pong) {
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
wsi->ws->await_pong = 0;
/*
* prepare to send the ping again if nothing
* sent to countermand it
*/
__lws_sul_insert(&pt->pt_sul_owner,
&wsi->sul_ping,
(lws_usec_t)wsi->context->ws_ping_pong_interval *
LWS_USEC_PER_SEC);
}
lws_validity_confirmed(wsi);
/* issue it */
callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
break;

View file

@ -234,7 +234,6 @@ lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1)
int
lws_client_ws_upgrade(struct lws *wsi, const char **cce)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
struct lws_context *context = wsi->context;
struct lws_tokenize ts;
int n, len, okay = 0;
@ -242,6 +241,7 @@ lws_client_ws_upgrade(struct lws *wsi, const char **cce)
char *p, buf[64];
const char *pc;
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char *sb = (char *)&pt->serv_buf[0];
const struct lws_ext_options *opts;
const struct lws_extension *ext;
@ -630,15 +630,8 @@ check_accept:
/* free up his parsing allocations */
lws_header_table_detach(wsi, 0);
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED,
&role_ops_ws);
if (wsi->context->ws_ping_pong_interval && !wsi->http2_substream ) {
wsi->sul_ping.cb = lws_sul_wsping_cb;
__lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_ping,
(lws_usec_t)wsi->context->ws_ping_pong_interval *
LWS_USEC_PER_SEC);
}
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED, &role_ops_ws);
lws_validity_confirmed(wsi);
wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;

View file

@ -34,7 +34,6 @@
int
lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int callback_action = LWS_CALLBACK_RECEIVE;
struct lws_ext_pm_deflate_rx_ebufs pmdrx;
unsigned short close_code;
@ -550,22 +549,7 @@ ping_drop:
lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
wsi->ws->rx_ubuf_head);
if (wsi->ws->await_pong) {
lwsl_info("received expected PONG on wsi %p\n",
wsi);
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
wsi->ws->await_pong = 0;
/*
* prepare to send the ping again if nothing
* sent to countermand it
*/
__lws_sul_insert(&pt->pt_sul_owner,
&wsi->sul_ping,
(lws_usec_t)wsi->context->ws_ping_pong_interval *
LWS_USEC_PER_SEC);
}
lws_validity_confirmed(wsi);
/* issue it */
callback_action = LWS_CALLBACK_RECEIVE_PONG;
@ -839,52 +823,13 @@ lws_0405_frame_mask_generate(struct lws *wsi)
return 0;
}
void
lws_sul_wsping_cb(lws_sorted_usec_list_t *sul)
{
struct lws *wsi = lws_container_of(sul, struct lws, sul_ping);
/*
* The sul_ping timer came up... either it's time to send a PING
* (!wsi->ws->send_check_ping), or we didn't get the PONG in time
* (wsi->ws->send_check_ping)
*/
if (!wsi->ws->send_check_ping) {
lwsl_info("%s: req pp on wsi %p\n", __func__, wsi);
wsi->ws->send_check_ping = 1;
lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING,
wsi->context->timeout_secs);
lws_callback_on_writable(wsi);
return;
}
if (wsi->ws->await_pong) {
/* it didn't return the PONG in time */
lwsl_info("%s: wsi %p: failed to send PONG\n", __func__, wsi);
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"PONG timeout");
}
}
int
lws_server_init_wsi_for_ws(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int n;
lwsi_set_state(wsi, LRS_ESTABLISHED);
if (wsi->context->ws_ping_pong_interval && !wsi->http2_substream ) {
wsi->sul_ping.cb = lws_sul_wsping_cb;
__lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_ping,
(lws_usec_t)wsi->context->ws_ping_pong_interval *
LWS_USEC_PER_SEC);
}
/*
* create the frame buffer for this connection according to the
* size mentioned in the protocol definition. If 0 there, use
@ -925,6 +870,7 @@ lws_server_init_wsi_for_ws(struct lws *wsi)
wsi->h2_stream_carries_ws))
return 1;
lws_validity_confirmed(wsi);
lwsl_debug("ws established\n");
return 0;
@ -1302,7 +1248,6 @@ drain:
int rops_handle_POLLOUT_ws(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int write_type = LWS_WRITE_PONG;
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_ext_pm_deflate_rx_ebufs pmdrx;
@ -1380,24 +1325,17 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
}
if (!wsi->socket_is_permanently_unusable &&
wsi->ws->send_check_ping && wsi->context->ws_ping_pong_interval) {
wsi->ws->send_check_ping) {
lwsl_info("%s: issuing ping on wsi %p: %s %s h2: %d\n", __func__, wsi,
wsi->role_ops->name, wsi->protocol->name,
wsi->http2_substream);
wsi->ws->send_check_ping = 0;
wsi->ws->await_pong = 1;
n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
0, LWS_WRITE_PING);
if (n < 0)
return LWS_HP_RET_BAIL_DIE;
/* give it a few seconds to respond with the PONG */
__lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_ping,
(lws_usec_t)wsi->context->timeout_secs *
LWS_USEC_PER_SEC);
return LWS_HP_RET_BAIL_OK;
}
@ -1634,8 +1572,8 @@ static int
rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol *wp)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
enum lws_write_protocol wpt;
#endif
struct lws_ext_pm_deflate_rx_ebufs pmdrx;
@ -1682,13 +1620,7 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
// assert(0);
}
#endif
/* reset the ping wait */
if (wsi->context->ws_ping_pong_interval) {
wsi->sul_ping.cb = lws_sul_wsping_cb;
__lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_ping,
(lws_usec_t)wsi->context->ws_ping_pong_interval *
LWS_USEC_PER_SEC);
}
if (((*wp) & 0x1f) == LWS_WRITE_HTTP ||
((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL ||
((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION ||
@ -1980,7 +1912,6 @@ send_raw:
static int
rops_close_kill_connection_ws(struct lws *wsi, enum lws_close_status reason)
{
lws_dll2_remove(&wsi->sul_ping.list);
/* deal with ws encapsulation in h2 */
#if defined(LWS_WITH_HTTP2)
if (wsi->http2_substream && wsi->h2_stream_carries_ws)
@ -2087,14 +2018,41 @@ rops_destroy_role_ws(struct lws *wsi)
return 0;
}
static int
rops_issue_keepalive_ws(struct lws *wsi, int isvalid)
{
uint64_t us;
#if defined(LWS_WITH_HTTP2)
if (lwsi_role_h2_ENCAPSULATION(wsi)) {
/* we know then that it has an h2 parent */
struct lws *enc = role_ops_h2.encapsulation_parent(wsi);
assert(enc);
if (enc->role_ops->issue_keepalive(wsi, isvalid))
return 1;
}
#endif
if (isvalid)
_lws_validity_confirmed_role(wsi);
else {
us = lws_now_usecs();
memcpy(&wsi->ws->ping_payload_buf[LWS_PRE], &us, 8);
wsi->ws->send_check_ping = 1;
lws_callback_on_writable(wsi);
}
return 0;
}
struct lws_role_ops role_ops_ws = {
/* role name */ "ws",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* pt_init_destroy */ NULL,
/* init_vhost */ rops_init_vhost_ws,
/* destroy_vhost */ rops_destroy_vhost_ws,
/* periodic_checks */ NULL,
/* service_flag_pending */ rops_service_flag_pending_ws,
/* handle_POLLIN */ rops_handle_POLLIN_ws,
/* handle_POLLOUT */ rops_handle_POLLOUT_ws,
@ -2110,6 +2068,7 @@ struct lws_role_ops role_ops_ws = {
/* destroy_role */ rops_destroy_role_ws,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
/* issue_keepalive */ rops_issue_keepalive_ws,
/* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED,
LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED },
/* rx_cb clnt, srv */ { LWS_CALLBACK_CLIENT_RECEIVE,

View file

@ -104,23 +104,6 @@ struct _lws_websocket_related {
/* Also used for close content... control opcode == < 128 */
uint8_t ping_payload_buf[128 - 3 + LWS_PRE];
uint8_t mask[4];
size_t rx_packet_length;
uint32_t rx_ubuf_head;
uint32_t rx_ubuf_alloc;
uint8_t ping_payload_len;
uint8_t mask_idx;
uint8_t opcode;
uint8_t rsv;
uint8_t rsv_first_msg;
/* zero if no info, or length including 2-byte close code */
uint8_t close_in_ping_buffer_len;
uint8_t utf8;
uint8_t stashed_write_type;
uint8_t tx_draining_stashed_wp;
uint8_t ietf_spec_revision;
unsigned int final:1;
unsigned int frame_is_binary:1;
@ -138,13 +121,31 @@ struct _lws_websocket_related {
unsigned int send_check_ping:1;
unsigned int first_fragment:1;
unsigned int peer_has_sent_close:1;
unsigned int await_pong;
#if !defined(LWS_WITHOUT_EXTENSIONS)
unsigned int extension_data_pending:1;
unsigned int rx_draining_ext:1;
unsigned int tx_draining_ext:1;
unsigned int pmd_trailer_application:1;
#endif
uint8_t mask[4];
size_t rx_packet_length;
uint32_t rx_ubuf_head;
uint32_t rx_ubuf_alloc;
uint8_t ping_payload_len;
uint8_t mask_idx;
uint8_t opcode;
uint8_t rsv;
uint8_t rsv_first_msg;
/* zero if no info, or length including 2-byte close code */
uint8_t close_in_ping_buffer_len;
uint8_t utf8;
uint8_t stashed_write_type;
uint8_t tx_draining_stashed_wp;
uint8_t ietf_spec_revision;
#if !defined(LWS_WITHOUT_EXTENSIONS)
uint8_t count_act_ext;
#endif
};

View file

@ -21,6 +21,7 @@ Commandline option|Meaning
-j|Apply tls option LCCSCF_ALLOW_SELFSIGNED
-m|Apply tls option LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK
-e|Apply tls option LCCSCF_ALLOW_EXPIRED
-v|Connection validity use 3s / 10s instead of default 5m / 5m10s
```
$ ./lws-minimal-http-client

View file

@ -22,6 +22,11 @@ static int long_poll;
#endif
static struct lws *client_wsi;
static const lws_retry_bo_t retry = {
.secs_since_valid_ping = 3,
.secs_since_valid_hangup = 10,
};
static int
callback_http(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
@ -207,6 +212,11 @@ int main(int argc, const char **argv)
if (lws_cmdline_option(argc, argv, "-e"))
i.ssl_connection |= LCCSCF_ALLOW_EXPIRED;
/* the default validity check is 5m / 5m10s... -v = 3s / 10s */
if (lws_cmdline_option(argc, argv, "-v"))
i.retry_and_idle_policy = &retry;
if ((p = lws_cmdline_option(argc, argv, "--server")))
i.address = p;

View file

@ -5,6 +5,13 @@
```
$ cmake . && make
```
## Commandline Options
Option|Meaning
---|---
-d|Set logging verbosity
-s|Serve using TLS selfsigned cert (ie, connect to it with https://...)
-v|Connection validity use 3s / 10s instead of default 5m / 5m10s
## usage

View file

@ -33,6 +33,11 @@ struct pss {
char pending;
};
static const lws_retry_bo_t retry = {
.secs_since_valid_ping = 5,
.secs_since_valid_hangup = 10,
};
static void
sul_cb(lws_sorted_usec_list_t *sul)
{
@ -132,6 +137,11 @@ int main(int argc, const char **argv)
LWS_SERVER_OPTION_VH_H2_HALF_CLOSED_LONG_POLL |
LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
/* the default validity check is 5m / 5m10s... -v = 5s / 10s */
if (lws_cmdline_option(argc, argv, "-v"))
info.retry_and_idle_policy = &retry;
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");

View file

@ -19,6 +19,8 @@ Option|Meaning
--port|Use a specific port instead of 443, eg `--port 7681`
-z|Send zero-length pings for testing
--protocol|Use a specific ws subprotocol rather than lws-mirror-protocol, eg, `--protocol myprotocol`
-v|Connection validity use 3s / 10s instead of default 5m / 5m10s
## usage

View file

@ -17,7 +17,7 @@
static struct lws_context *context;
static struct lws *client_wsi;
static int interrupted, zero_length_ping, port = 443,
static int interrupted, zero_length_ping, port = 443, quick_pings, no_user_ping,
ssl_connection = LCCSCF_USE_SSL;
static const char *server_address = "libwebsockets.org", *pro = "lws-mirror-protocol";
@ -25,6 +25,11 @@ struct pss {
int send_a_ping;
};
static const lws_retry_bo_t retry = {
.secs_since_valid_ping = 3,
.secs_since_valid_hangup = 10,
};
static int
connect_client(void)
{
@ -42,6 +47,8 @@ connect_client(void)
i.protocol = pro;
i.local_protocol_name = "lws-ping-test";
i.pwsi = &client_wsi;
if (quick_pings)
i.retry_and_idle_policy = &retry;
return !lws_client_connect_via_info(&i);
}
@ -110,10 +117,12 @@ callback_minimal_broker(struct lws *wsi, enum lws_callback_reasons reason,
break;
case LWS_CALLBACK_TIMER:
if (no_user_ping)
break;
/* we want to send a ws PING every few seconds */
pss->send_a_ping = 1;
lws_callback_on_writable(wsi);
lws_set_timer_usecs(wsi, 5 * LWS_USEC_PER_SEC);
lws_set_timer_usecs(wsi, 10 * LWS_USEC_PER_SEC);
break;
/* rate-limited client connect retries */
@ -185,6 +194,9 @@ int main(int argc, const char **argv)
if (lws_cmdline_option(argc, argv, "-z"))
zero_length_ping = 1;
if (lws_cmdline_option(argc, argv, "-n"))
no_user_ping = 1;
if ((p = lws_cmdline_option(argc, argv, "--protocol")))
pro = p;
@ -197,6 +209,11 @@ int main(int argc, const char **argv)
if ((p = lws_cmdline_option(argc, argv, "--port")))
port = atoi(p);
/* the default validity check is 5m / 5m10s... -v = 5s / 10s */
if (lws_cmdline_option(argc, argv, "-v"))
quick_pings = 1;
/*
* since we know this lws context is only ever going to be used with
* one client wsis / fds / sockets at a time, let lws know it doesn't

View file

@ -13,6 +13,7 @@ Option|Meaning
-d|Set logging verbosity
-s|Serve using TLS selfsigned cert (ie, connect to it with https://...)
-h|Strict Host: header checking against vhost name (localhost) and port
-v|Connection validity use 3s / 10s instead of default 5m / 5m10s
## usage

View file

@ -27,6 +27,11 @@ static struct lws_protocols protocols[] = {
{ NULL, NULL, 0, 0 } /* terminator */
};
static const lws_retry_bo_t retry = {
.secs_since_valid_ping = 3,
.secs_since_valid_hangup = 10,
};
static int interrupted;
static const struct lws_http_mount mount = {
@ -94,6 +99,9 @@ int main(int argc, const char **argv)
if (lws_cmdline_option(argc, argv, "-h"))
info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK;
if (lws_cmdline_option(argc, argv, "-v"))
info.retry_and_idle_policy = &retry;
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");