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