1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * libwebsockets - small server side websockets and web server implementation
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
7d4afb5ceSopenharmony_ci * of this software and associated documentation files (the "Software"), to
8d4afb5ceSopenharmony_ci * deal in the Software without restriction, including without limitation the
9d4afb5ceSopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10d4afb5ceSopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is
11d4afb5ceSopenharmony_ci * furnished to do so, subject to the following conditions:
12d4afb5ceSopenharmony_ci *
13d4afb5ceSopenharmony_ci * The above copyright notice and this permission notice shall be included in
14d4afb5ceSopenharmony_ci * all copies or substantial portions of the Software.
15d4afb5ceSopenharmony_ci *
16d4afb5ceSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d4afb5ceSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d4afb5ceSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19d4afb5ceSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20d4afb5ceSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21d4afb5ceSopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22d4afb5ceSopenharmony_ci * IN THE SOFTWARE.
23d4afb5ceSopenharmony_ci */
24d4afb5ceSopenharmony_ci
25d4afb5ceSopenharmony_ci#include "private-lib-core.h"
26d4afb5ceSopenharmony_ci
27d4afb5ceSopenharmony_civoid
28d4afb5ceSopenharmony_ci__lws_wsi_remove_from_sul(struct lws *wsi)
29d4afb5ceSopenharmony_ci{
30d4afb5ceSopenharmony_ci	lws_sul_cancel(&wsi->sul_timeout);
31d4afb5ceSopenharmony_ci	lws_sul_cancel(&wsi->sul_hrtimer);
32d4afb5ceSopenharmony_ci	lws_sul_cancel(&wsi->sul_validity);
33d4afb5ceSopenharmony_ci#if defined(LWS_WITH_SYS_FAULT_INJECTION)
34d4afb5ceSopenharmony_ci	lws_sul_cancel(&wsi->sul_fault_timedclose);
35d4afb5ceSopenharmony_ci#endif
36d4afb5ceSopenharmony_ci}
37d4afb5ceSopenharmony_ci
38d4afb5ceSopenharmony_ci/*
39d4afb5ceSopenharmony_ci * hrtimer
40d4afb5ceSopenharmony_ci */
41d4afb5ceSopenharmony_ci
42d4afb5ceSopenharmony_cistatic void
43d4afb5ceSopenharmony_cilws_sul_hrtimer_cb(lws_sorted_usec_list_t *sul)
44d4afb5ceSopenharmony_ci{
45d4afb5ceSopenharmony_ci	struct lws *wsi = lws_container_of(sul, struct lws, sul_hrtimer);
46d4afb5ceSopenharmony_ci
47d4afb5ceSopenharmony_ci	if (wsi->a.protocol &&
48d4afb5ceSopenharmony_ci	    wsi->a.protocol->callback(wsi, LWS_CALLBACK_TIMER,
49d4afb5ceSopenharmony_ci				    wsi->user_space, NULL, 0))
50d4afb5ceSopenharmony_ci		__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
51d4afb5ceSopenharmony_ci				     "hrtimer cb errored");
52d4afb5ceSopenharmony_ci}
53d4afb5ceSopenharmony_ci
54d4afb5ceSopenharmony_civoid
55d4afb5ceSopenharmony_ci__lws_set_timer_usecs(struct lws *wsi, lws_usec_t us)
56d4afb5ceSopenharmony_ci{
57d4afb5ceSopenharmony_ci	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
58d4afb5ceSopenharmony_ci
59d4afb5ceSopenharmony_ci	wsi->sul_hrtimer.cb = lws_sul_hrtimer_cb;
60d4afb5ceSopenharmony_ci	__lws_sul_insert_us(&pt->pt_sul_owner[LWSSULLI_MISS_IF_SUSPENDED],
61d4afb5ceSopenharmony_ci			    &wsi->sul_hrtimer, us);
62d4afb5ceSopenharmony_ci}
63d4afb5ceSopenharmony_ci
64d4afb5ceSopenharmony_civoid
65d4afb5ceSopenharmony_cilws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs)
66d4afb5ceSopenharmony_ci{
67d4afb5ceSopenharmony_ci	__lws_set_timer_usecs(wsi, usecs);
68d4afb5ceSopenharmony_ci}
69d4afb5ceSopenharmony_ci
70d4afb5ceSopenharmony_ci/*
71d4afb5ceSopenharmony_ci * wsi timeout
72d4afb5ceSopenharmony_ci */
73d4afb5ceSopenharmony_ci
74d4afb5ceSopenharmony_cistatic void
75d4afb5ceSopenharmony_cilws_sul_wsitimeout_cb(lws_sorted_usec_list_t *sul)
76d4afb5ceSopenharmony_ci{
77d4afb5ceSopenharmony_ci	struct lws *wsi = lws_container_of(sul, struct lws, sul_timeout);
78d4afb5ceSopenharmony_ci	struct lws_context *cx = wsi->a.context;
79d4afb5ceSopenharmony_ci	struct lws_context_per_thread *pt = &cx->pt[(int)wsi->tsi];
80d4afb5ceSopenharmony_ci
81d4afb5ceSopenharmony_ci	/* no need to log normal idle keepalive timeout */
82d4afb5ceSopenharmony_ci//		if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE)
83d4afb5ceSopenharmony_ci#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
84d4afb5ceSopenharmony_ci	if (wsi->pending_timeout != PENDING_TIMEOUT_USER_OK)
85d4afb5ceSopenharmony_ci		lwsl_wsi_info(wsi, "TIMEDOUT WAITING %d, dhdr %d, ah %p, wl %d",
86d4afb5ceSopenharmony_ci				   wsi->pending_timeout,
87d4afb5ceSopenharmony_ci				   wsi->hdr_parsing_completed, wsi->http.ah,
88d4afb5ceSopenharmony_ci				   pt->http.ah_wait_list_length);
89d4afb5ceSopenharmony_ci#if defined(LWS_WITH_CGI)
90d4afb5ceSopenharmony_ci	if (wsi->http.cgi)
91d4afb5ceSopenharmony_ci		lwsl_wsi_notice(wsi, "CGI timeout: %s", wsi->http.cgi->summary);
92d4afb5ceSopenharmony_ci#endif
93d4afb5ceSopenharmony_ci#else
94d4afb5ceSopenharmony_ci	if (wsi->pending_timeout != PENDING_TIMEOUT_USER_OK)
95d4afb5ceSopenharmony_ci		lwsl_wsi_info(wsi, "TIMEDOUT WAITING on %d ",
96d4afb5ceSopenharmony_ci				   wsi->pending_timeout);
97d4afb5ceSopenharmony_ci#endif
98d4afb5ceSopenharmony_ci	/* cgi timeout */
99d4afb5ceSopenharmony_ci	if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE)
100d4afb5ceSopenharmony_ci		/*
101d4afb5ceSopenharmony_ci		 * Since he failed a timeout, he already had a chance to
102d4afb5ceSopenharmony_ci		 * do something and was unable to... that includes
103d4afb5ceSopenharmony_ci		 * situations like half closed connections.  So process
104d4afb5ceSopenharmony_ci		 * this "failed timeout" close as a violent death and
105d4afb5ceSopenharmony_ci		 * don't try to do protocol cleanup like flush partials.
106d4afb5ceSopenharmony_ci		 */
107d4afb5ceSopenharmony_ci		wsi->socket_is_permanently_unusable = 1;
108d4afb5ceSopenharmony_ci#if defined(LWS_WITH_CLIENT)
109d4afb5ceSopenharmony_ci	if (lwsi_state(wsi) == LRS_WAITING_SSL)
110d4afb5ceSopenharmony_ci		lws_inform_client_conn_fail(wsi,
111d4afb5ceSopenharmony_ci			(void *)"Timed out waiting SSL", 21);
112d4afb5ceSopenharmony_ci	if (lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY)
113d4afb5ceSopenharmony_ci		lws_inform_client_conn_fail(wsi,
114d4afb5ceSopenharmony_ci			(void *)"Timed out waiting server reply", 30);
115d4afb5ceSopenharmony_ci#endif
116d4afb5ceSopenharmony_ci
117d4afb5ceSopenharmony_ci	lws_context_lock(cx, __func__);
118d4afb5ceSopenharmony_ci	lws_pt_lock(pt, __func__);
119d4afb5ceSopenharmony_ci	__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "timeout");
120d4afb5ceSopenharmony_ci	lws_pt_unlock(pt);
121d4afb5ceSopenharmony_ci	lws_context_unlock(cx);
122d4afb5ceSopenharmony_ci}
123d4afb5ceSopenharmony_ci
124d4afb5ceSopenharmony_civoid
125d4afb5ceSopenharmony_ci__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
126d4afb5ceSopenharmony_ci{
127d4afb5ceSopenharmony_ci	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
128d4afb5ceSopenharmony_ci
129d4afb5ceSopenharmony_ci	wsi->sul_timeout.cb = lws_sul_wsitimeout_cb;
130d4afb5ceSopenharmony_ci	__lws_sul_insert_us(&pt->pt_sul_owner[LWSSULLI_MISS_IF_SUSPENDED],
131d4afb5ceSopenharmony_ci			    &wsi->sul_timeout,
132d4afb5ceSopenharmony_ci			    ((lws_usec_t)secs) * LWS_US_PER_SEC);
133d4afb5ceSopenharmony_ci
134d4afb5ceSopenharmony_ci	lwsl_wsi_debug(wsi, "%d secs, reason %d\n", secs, reason);
135d4afb5ceSopenharmony_ci
136d4afb5ceSopenharmony_ci	wsi->pending_timeout = (char)reason;
137d4afb5ceSopenharmony_ci}
138d4afb5ceSopenharmony_ci
139d4afb5ceSopenharmony_civoid
140d4afb5ceSopenharmony_cilws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
141d4afb5ceSopenharmony_ci{
142d4afb5ceSopenharmony_ci	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
143d4afb5ceSopenharmony_ci
144d4afb5ceSopenharmony_ci	lws_context_lock(pt->context, __func__);
145d4afb5ceSopenharmony_ci	lws_pt_lock(pt, __func__);
146d4afb5ceSopenharmony_ci	lws_dll2_remove(&wsi->sul_timeout.list);
147d4afb5ceSopenharmony_ci	lws_pt_unlock(pt);
148d4afb5ceSopenharmony_ci
149d4afb5ceSopenharmony_ci	if (!secs)
150d4afb5ceSopenharmony_ci		goto bail;
151d4afb5ceSopenharmony_ci
152d4afb5ceSopenharmony_ci	if (secs == LWS_TO_KILL_SYNC) {
153d4afb5ceSopenharmony_ci		lwsl_wsi_debug(wsi, "TO_KILL_SYNC");
154d4afb5ceSopenharmony_ci		lws_context_unlock(pt->context);
155d4afb5ceSopenharmony_ci		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
156d4afb5ceSopenharmony_ci				   "to sync kill");
157d4afb5ceSopenharmony_ci		return;
158d4afb5ceSopenharmony_ci	}
159d4afb5ceSopenharmony_ci
160d4afb5ceSopenharmony_ci	if (secs == LWS_TO_KILL_ASYNC)
161d4afb5ceSopenharmony_ci		secs = 0;
162d4afb5ceSopenharmony_ci
163d4afb5ceSopenharmony_ci	// assert(!secs || !wsi->mux_stream_immortal);
164d4afb5ceSopenharmony_ci	if (secs && wsi->mux_stream_immortal)
165d4afb5ceSopenharmony_ci		lwsl_wsi_err(wsi, "on immortal stream %d %d", reason, secs);
166d4afb5ceSopenharmony_ci
167d4afb5ceSopenharmony_ci	lws_pt_lock(pt, __func__);
168d4afb5ceSopenharmony_ci	__lws_set_timeout(wsi, reason, secs);
169d4afb5ceSopenharmony_ci	lws_pt_unlock(pt);
170d4afb5ceSopenharmony_ci
171d4afb5ceSopenharmony_cibail:
172d4afb5ceSopenharmony_ci	lws_context_unlock(pt->context);
173d4afb5ceSopenharmony_ci}
174d4afb5ceSopenharmony_ci
175d4afb5ceSopenharmony_civoid
176d4afb5ceSopenharmony_cilws_set_timeout_us(struct lws *wsi, enum pending_timeout reason, lws_usec_t us)
177d4afb5ceSopenharmony_ci{
178d4afb5ceSopenharmony_ci	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
179d4afb5ceSopenharmony_ci
180d4afb5ceSopenharmony_ci	lws_pt_lock(pt, __func__);
181d4afb5ceSopenharmony_ci	lws_dll2_remove(&wsi->sul_timeout.list);
182d4afb5ceSopenharmony_ci	lws_pt_unlock(pt);
183d4afb5ceSopenharmony_ci
184d4afb5ceSopenharmony_ci	if (!us)
185d4afb5ceSopenharmony_ci		return;
186d4afb5ceSopenharmony_ci
187d4afb5ceSopenharmony_ci	lws_pt_lock(pt, __func__);
188d4afb5ceSopenharmony_ci	__lws_sul_insert_us(&pt->pt_sul_owner[LWSSULLI_MISS_IF_SUSPENDED],
189d4afb5ceSopenharmony_ci			    &wsi->sul_timeout, us);
190d4afb5ceSopenharmony_ci
191d4afb5ceSopenharmony_ci	lwsl_wsi_notice(wsi, "%llu us, reason %d",
192d4afb5ceSopenharmony_ci			     (unsigned long long)us, reason);
193d4afb5ceSopenharmony_ci
194d4afb5ceSopenharmony_ci	wsi->pending_timeout = (char)reason;
195d4afb5ceSopenharmony_ci	lws_pt_unlock(pt);
196d4afb5ceSopenharmony_ci}
197d4afb5ceSopenharmony_ci
198d4afb5ceSopenharmony_cistatic void
199d4afb5ceSopenharmony_cilws_validity_cb(lws_sorted_usec_list_t *sul)
200d4afb5ceSopenharmony_ci{
201d4afb5ceSopenharmony_ci	struct lws *wsi = lws_container_of(sul, struct lws, sul_validity);
202d4afb5ceSopenharmony_ci	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
203d4afb5ceSopenharmony_ci	const lws_retry_bo_t *rbo = wsi->retry_policy;
204d4afb5ceSopenharmony_ci
205d4afb5ceSopenharmony_ci	/* one of either the ping or hangup validity threshold was crossed */
206d4afb5ceSopenharmony_ci
207d4afb5ceSopenharmony_ci	if (wsi->validity_hup) {
208d4afb5ceSopenharmony_ci		lwsl_wsi_info(wsi, "validity too old");
209d4afb5ceSopenharmony_ci		struct lws_context *cx = wsi->a.context;
210d4afb5ceSopenharmony_ci		struct lws_context_per_thread *pt = &cx->pt[(int)wsi->tsi];
211d4afb5ceSopenharmony_ci
212d4afb5ceSopenharmony_ci		lws_context_lock(cx, __func__);
213d4afb5ceSopenharmony_ci		lws_pt_lock(pt, __func__);
214d4afb5ceSopenharmony_ci		__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
215d4afb5ceSopenharmony_ci				     "validity timeout");
216d4afb5ceSopenharmony_ci		lws_pt_unlock(pt);
217d4afb5ceSopenharmony_ci		lws_context_unlock(cx);
218d4afb5ceSopenharmony_ci		return;
219d4afb5ceSopenharmony_ci	}
220d4afb5ceSopenharmony_ci
221d4afb5ceSopenharmony_ci	/* schedule a protocol-dependent ping */
222d4afb5ceSopenharmony_ci
223d4afb5ceSopenharmony_ci	lwsl_wsi_info(wsi, "scheduling validity check");
224d4afb5ceSopenharmony_ci
225d4afb5ceSopenharmony_ci	if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_issue_keepalive))
226d4afb5ceSopenharmony_ci		lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_issue_keepalive).
227d4afb5ceSopenharmony_ci							issue_keepalive(wsi, 0);
228d4afb5ceSopenharmony_ci
229d4afb5ceSopenharmony_ci	/*
230d4afb5ceSopenharmony_ci	 * We arrange to come back here after the additional ping to hangup time
231d4afb5ceSopenharmony_ci	 * and do the hangup, unless we get validated (by, eg, a PONG) and
232d4afb5ceSopenharmony_ci	 * reset the timer
233d4afb5ceSopenharmony_ci	 */
234d4afb5ceSopenharmony_ci
235d4afb5ceSopenharmony_ci	assert(rbo->secs_since_valid_hangup > rbo->secs_since_valid_ping);
236d4afb5ceSopenharmony_ci
237d4afb5ceSopenharmony_ci	wsi->validity_hup = 1;
238d4afb5ceSopenharmony_ci	__lws_sul_insert_us(&pt->pt_sul_owner[!!wsi->conn_validity_wakesuspend],
239d4afb5ceSopenharmony_ci			    &wsi->sul_validity,
240d4afb5ceSopenharmony_ci			    ((uint64_t)rbo->secs_since_valid_hangup -
241d4afb5ceSopenharmony_ci				 rbo->secs_since_valid_ping) * LWS_US_PER_SEC);
242d4afb5ceSopenharmony_ci}
243d4afb5ceSopenharmony_ci
244d4afb5ceSopenharmony_ci/*
245d4afb5ceSopenharmony_ci * The role calls this back to actually confirm validity on a particular wsi
246d4afb5ceSopenharmony_ci * (which may not be the original wsi)
247d4afb5ceSopenharmony_ci */
248d4afb5ceSopenharmony_ci
249d4afb5ceSopenharmony_civoid
250d4afb5ceSopenharmony_ci_lws_validity_confirmed_role(struct lws *wsi)
251d4afb5ceSopenharmony_ci{
252d4afb5ceSopenharmony_ci	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
253d4afb5ceSopenharmony_ci	const lws_retry_bo_t *rbo = wsi->retry_policy;
254d4afb5ceSopenharmony_ci
255d4afb5ceSopenharmony_ci	if (!rbo || !rbo->secs_since_valid_hangup)
256d4afb5ceSopenharmony_ci		return;
257d4afb5ceSopenharmony_ci
258d4afb5ceSopenharmony_ci	wsi->validity_hup = 0;
259d4afb5ceSopenharmony_ci	wsi->sul_validity.cb = lws_validity_cb;
260d4afb5ceSopenharmony_ci
261d4afb5ceSopenharmony_ci	wsi->validity_hup = rbo->secs_since_valid_ping >=
262d4afb5ceSopenharmony_ci			    rbo->secs_since_valid_hangup;
263d4afb5ceSopenharmony_ci
264d4afb5ceSopenharmony_ci	lwsl_wsi_info(wsi, "setting validity timer %ds (hup %d)",
265d4afb5ceSopenharmony_ci			   wsi->validity_hup ? rbo->secs_since_valid_hangup :
266d4afb5ceSopenharmony_ci					    rbo->secs_since_valid_ping,
267d4afb5ceSopenharmony_ci			   wsi->validity_hup);
268d4afb5ceSopenharmony_ci
269d4afb5ceSopenharmony_ci	__lws_sul_insert_us(&pt->pt_sul_owner[!!wsi->conn_validity_wakesuspend],
270d4afb5ceSopenharmony_ci			    &wsi->sul_validity,
271d4afb5ceSopenharmony_ci			    ((uint64_t)(wsi->validity_hup ?
272d4afb5ceSopenharmony_ci				rbo->secs_since_valid_hangup :
273d4afb5ceSopenharmony_ci				rbo->secs_since_valid_ping)) * LWS_US_PER_SEC);
274d4afb5ceSopenharmony_ci}
275d4afb5ceSopenharmony_ci
276d4afb5ceSopenharmony_civoid
277d4afb5ceSopenharmony_cilws_validity_confirmed(struct lws *wsi)
278d4afb5ceSopenharmony_ci{
279d4afb5ceSopenharmony_ci	/*
280d4afb5ceSopenharmony_ci	 * This may be a stream inside a muxed network connection... leave it
281d4afb5ceSopenharmony_ci	 * to the role to figure out who actually needs to understand their
282d4afb5ceSopenharmony_ci	 * validity was confirmed.
283d4afb5ceSopenharmony_ci	 */
284d4afb5ceSopenharmony_ci	if (!wsi->h2_stream_carries_ws && /* only if not encapsulated */
285d4afb5ceSopenharmony_ci	    wsi->role_ops &&
286d4afb5ceSopenharmony_ci	    lws_rops_fidx(wsi->role_ops, LWS_ROPS_issue_keepalive))
287d4afb5ceSopenharmony_ci		lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_issue_keepalive).
288d4afb5ceSopenharmony_ci							issue_keepalive(wsi, 1);
289d4afb5ceSopenharmony_ci}
290