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