diff options
Diffstat (limited to 'thirdparty/libwebsockets/core/service.c')
-rw-r--r-- | thirdparty/libwebsockets/core/service.c | 987 |
1 files changed, 987 insertions, 0 deletions
diff --git a/thirdparty/libwebsockets/core/service.c b/thirdparty/libwebsockets/core/service.c new file mode 100644 index 0000000000..6523058814 --- /dev/null +++ b/thirdparty/libwebsockets/core/service.c @@ -0,0 +1,987 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +int +lws_callback_as_writeable(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int n, m; + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB, 1); +#if defined(LWS_WITH_STATS) + if (wsi->active_writable_req_us) { + uint64_t ul = time_in_microseconds() - + wsi->active_writable_req_us; + + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_MS_WRITABLE_DELAY, ul); + lws_stats_atomic_max(wsi->context, pt, + LWSSTATS_MS_WORST_WRITABLE_DELAY, ul); + wsi->active_writable_req_us = 0; + } +#endif + + n = wsi->role_ops->writeable_cb[lwsi_role_server(wsi)]; + + m = user_callback_handle_rxflow(wsi->protocol->callback, + wsi, (enum lws_callback_reasons) n, + wsi->user_space, NULL, 0); + + return m; +} + +LWS_VISIBLE int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) +{ + volatile struct lws *vwsi = (volatile struct lws *)wsi; + int n; + + //lwsl_notice("%s: %p\n", __func__, wsi); + + vwsi->leave_pollout_active = 0; + vwsi->handling_pollout = 1; + /* + * if another thread wants POLLOUT on us, from here on while + * handling_pollout is set, he will only set leave_pollout_active. + * If we are going to disable POLLOUT, we will check that first. + */ + wsi->could_have_pending = 0; /* clear back-to-back write detection */ + + /* + * user callback is lowest priority to get these notifications + * actually, since other pending things cannot be disordered + * + * Priority 1: pending truncated sends are incomplete ws fragments + * If anything else sent first the protocol would be + * corrupted. + */ + + if (wsi->trunc_len) { + //lwsl_notice("%s: completing partial\n", __func__); + if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset, + wsi->trunc_len) < 0) { + lwsl_info("%s signalling to close\n", __func__); + goto bail_die; + } + /* leave POLLOUT active either way */ + goto bail_ok; + } else + if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) { + wsi->socket_is_permanently_unusable = 1; + goto bail_die; /* retry closing now */ + } + +#ifdef LWS_WITH_CGI + /* + * A cgi master's wire protocol remains h1 or h2. He is just getting + * his data from his child cgis. + */ + if (wsi->http.cgi) { + /* also one shot */ + if (pollfd) + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + return 1; + } + goto user_service_go_again; + } +#endif + + /* if we got here, we should have wire protocol ops set on the wsi */ + assert(wsi->role_ops); + + if (!wsi->role_ops->handle_POLLOUT) + goto bail_ok; + + switch ((wsi->role_ops->handle_POLLOUT)(wsi)) { + case LWS_HP_RET_BAIL_OK: + goto bail_ok; + case LWS_HP_RET_BAIL_DIE: + goto bail_die; + case LWS_HP_RET_USER_SERVICE: + break; + default: + assert(0); + } + + /* one shot */ + + if (wsi->parent_carries_io) { + vwsi->handling_pollout = 0; + vwsi->leave_pollout_active = 0; + + return lws_callback_as_writeable(wsi); + } + + if (pollfd) { + int eff = vwsi->leave_pollout_active; + + if (!eff) { + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + goto bail_die; + } + } + + vwsi->handling_pollout = 0; + + /* cannot get leave_pollout_active set after the above */ + if (!eff && wsi->leave_pollout_active) { + /* + * got set inbetween sampling eff and clearing + * handling_pollout, force POLLOUT on + */ + lwsl_debug("leave_pollout_active\n"); + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { + lwsl_info("failed at set pollfd\n"); + goto bail_die; + } + } + + vwsi->leave_pollout_active = 0; + } + + if (lwsi_role_client(wsi) && + !wsi->hdr_parsing_completed && + lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS && + lwsi_state(wsi) != LRS_ISSUE_HTTP_BODY + ) + goto bail_ok; + + +#ifdef LWS_WITH_CGI +user_service_go_again: +#endif + + if (wsi->role_ops->perform_user_POLLOUT) { + if (wsi->role_ops->perform_user_POLLOUT(wsi) == -1) + goto bail_die; + else + goto bail_ok; + } + + lwsl_debug("%s: %p: non mux: wsistate 0x%x, ops %s\n", __func__, wsi, + wsi->wsistate, wsi->role_ops->name); + + vwsi = (volatile struct lws *)wsi; + vwsi->leave_pollout_active = 0; + + n = lws_callback_as_writeable(wsi); + vwsi->handling_pollout = 0; + + if (vwsi->leave_pollout_active) + lws_change_pollfd(wsi, 0, LWS_POLLOUT); + + return n; + + /* + * since these don't disable the POLLOUT, they are always doing the + * right thing for leave_pollout_active whether it was set or not. + */ + +bail_ok: + vwsi->handling_pollout = 0; + vwsi->leave_pollout_active = 0; + + return 0; + +bail_die: + vwsi->handling_pollout = 0; + vwsi->leave_pollout_active = 0; + + return -1; +} + +static int +__lws_service_timeout_check(struct lws *wsi, time_t sec) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int n = 0; + + (void)n; + + /* + * if we went beyond the allowed time, kill the + * connection + */ + if (wsi->dll_timeout.prev && + lws_compare_time_t(wsi->context, sec, wsi->pending_timeout_set) > + wsi->pending_timeout_limit) { + + if (wsi->desc.sockfd != LWS_SOCK_INVALID && + wsi->position_in_fds_table >= 0) + n = pt->fds[wsi->position_in_fds_table].events; + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_TIMEOUTS, 1); + + /* no need to log normal idle keepalive timeout */ + if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE) +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + lwsl_info("wsi %p: TIMEDOUT WAITING on %d " + "(did hdr %d, ah %p, wl %d, pfd " + "events %d) %llu vs %llu\n", + (void *)wsi, wsi->pending_timeout, + wsi->hdr_parsing_completed, wsi->http.ah, + pt->http.ah_wait_list_length, n, + (unsigned long long)sec, + (unsigned long long)wsi->pending_timeout_limit); +#if defined(LWS_WITH_CGI) + if (wsi->http.cgi) + lwsl_notice("CGI timeout: %s\n", wsi->http.cgi->summary); +#endif +#else + lwsl_info("wsi %p: TIMEDOUT WAITING on %d ", (void *)wsi, + wsi->pending_timeout); +#endif + + /* + * Since he failed a timeout, he already had a chance to do + * something and was unable to... that includes situations like + * half closed connections. So process this "failed timeout" + * close as a violent death and don't try to do protocol + * cleanup like flush partials. + */ + wsi->socket_is_permanently_unusable = 1; + if (lwsi_state(wsi) == LRS_WAITING_SSL && wsi->protocol) + wsi->protocol->callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, + (void *)"Timed out waiting SSL", 21); + + __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "timeout"); + + return 1; + } + + return 0; +} + +int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + uint8_t *buffered; + size_t blen; + int ret = 0, m; + + /* his RX is flowcontrolled, don't send remaining now */ + blen = lws_buflist_next_segment_len(&wsi->buflist, &buffered); + if (blen) { + if (buf >= buffered && buf + len <= buffered + blen) { + /* rxflow while we were spilling prev rxflow */ + lwsl_info("%s: staying in rxflow buf\n", __func__); + + return 1; + } + ret = 1; + } + + /* a new rxflow, buffer it and warn caller */ + + m = lws_buflist_append_segment(&wsi->buflist, buf + n, len - n); + + if (m < 0) + return -1; + if (m) { + lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi); + lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist); + } + + return ret; +} + +/* this is used by the platform service code to stop us waiting for network + * activity in poll() when we have something that already needs service + */ + +LWS_VISIBLE LWS_EXTERN int +lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + + /* Figure out if we really want to wait in poll() + * We only need to wait if really nothing already to do and we have + * to wait for something from network + */ +#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) + /* 1) if we know we are draining rx ext, do not wait in poll */ + if (pt->ws.rx_draining_ext_list) + return 0; +#endif + + /* 2) if we know we have non-network pending data, do not wait in poll */ + + if (pt->context->tls_ops && + pt->context->tls_ops->fake_POLLIN_for_buffered) + if (pt->context->tls_ops->fake_POLLIN_for_buffered(pt)) + return 0; + + /* 3) If there is any wsi with rxflow buffered and in a state to process + * it, we should not wait in poll + */ + + lws_start_foreach_dll(struct lws_dll_lws *, d, pt->dll_head_buflist.next) { + struct lws *wsi = lws_container_of(d, struct lws, dll_buflist); + + if (lwsi_state(wsi) != LRS_DEFERRING_ACTION) + return 0; + + } lws_end_foreach_dll(d); + + return timeout_ms; +} + +/* + * POLLIN said there is something... we must read it, and either use it; or + * if other material already in the buflist append it and return the buflist + * head material. + */ +int +lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi, + struct lws_tokens *ebuf) +{ + int n, prior = (int)lws_buflist_next_segment_len(&wsi->buflist, NULL); + + ebuf->token = (char *)pt->serv_buf; + ebuf->len = lws_ssl_capable_read(wsi, pt->serv_buf, + wsi->context->pt_serv_buf_size); + + if (ebuf->len == LWS_SSL_CAPABLE_MORE_SERVICE && prior) + goto get_from_buflist; + + if (ebuf->len <= 0) + return 0; + + /* nothing in buflist already? Then just use what we read */ + + if (!prior) + return 0; + + /* stash what we read */ + + n = lws_buflist_append_segment(&wsi->buflist, (uint8_t *)ebuf->token, + ebuf->len); + if (n < 0) + return -1; + if (n) { + lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi); + lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist); + } + + /* get the first buflist guy in line */ + +get_from_buflist: + + ebuf->len = (int)lws_buflist_next_segment_len(&wsi->buflist, + (uint8_t **)&ebuf->token); + + return 1; /* came from buflist */ +} + +int +lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used, + int buffered) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int m; + + /* it's in the buflist; we didn't use any */ + + if (!used && buffered) + return 0; + + if (used && buffered) { + m = lws_buflist_use_segment(&wsi->buflist, used); + lwsl_info("%s: draining rxflow: used %d, next %d\n", + __func__, used, m); + if (m) + return 0; + + lwsl_info("%s: removed %p from dll_buflist\n", __func__, wsi); + lws_dll_lws_remove(&wsi->dll_buflist); + + return 0; + } + + /* any remainder goes on the buflist */ + + if (used != ebuf->len) { + m = lws_buflist_append_segment(&wsi->buflist, + (uint8_t *)ebuf->token + used, + ebuf->len - used); + if (m < 0) + return 1; /* OOM */ + if (m) { + lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi); + lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist); + } + } + + return 0; +} + +void +lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt) +{ + struct lws_pollfd pfd; + + if (!pt->dll_head_buflist.next) + return; + + /* + * service all guys with pending rxflow that reached a state they can + * accept the pending data + */ + + lws_pt_lock(pt, __func__); + + lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, + pt->dll_head_buflist.next) { + struct lws *wsi = lws_container_of(d, struct lws, dll_buflist); + + pfd.events = LWS_POLLIN; + pfd.revents = LWS_POLLIN; + pfd.fd = -1; + + lwsl_debug("%s: rxflow processing: %p 0x%x\n", __func__, wsi, + wsi->wsistate); + + if (!lws_is_flowcontrolled(wsi) && + lwsi_state(wsi) != LRS_DEFERRING_ACTION && + (wsi->role_ops->handle_POLLIN)(pt, wsi, &pfd) == + LWS_HPI_RET_PLEASE_CLOSE_ME) + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "close_and_handled"); + + } lws_end_foreach_dll_safe(d, d1); + + lws_pt_unlock(pt); +} + +/* + * guys that need POLLIN service again without waiting for network action + * can force POLLIN here if not flowcontrolled, so they will get service. + * + * Return nonzero if anybody got their POLLIN faked + */ +int +lws_service_flag_pending(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + +#if defined(LWS_WITH_TLS) + struct lws *wsi, *wsi_next; +#endif + int forced = 0; + + lws_pt_lock(pt, __func__); + + /* + * 1) If there is any wsi with a buflist and in a state to process + * it, we should not wait in poll + */ + + lws_start_foreach_dll(struct lws_dll_lws *, d, pt->dll_head_buflist.next) { + struct lws *wsi = lws_container_of(d, struct lws, dll_buflist); + + if (lwsi_state(wsi) != LRS_DEFERRING_ACTION) { + forced = 1; + break; + } + } lws_end_foreach_dll(d); + +#if defined(LWS_ROLE_WS) + forced |= role_ops_ws.service_flag_pending(context, tsi); +#endif + +#if defined(LWS_WITH_TLS) + /* + * 2) For all guys with buffered SSL read data already saved up, if they + * are not flowcontrolled, fake their POLLIN status so they'll get + * service to use up the buffered incoming data, even though their + * network socket may have nothing + */ + wsi = pt->tls.pending_read_list; + while (wsi) { + wsi_next = wsi->tls.pending_read_list_next; + pt->fds[wsi->position_in_fds_table].revents |= + pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; + if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) { + forced = 1; + /* + * he's going to get serviced now, take him off the + * list of guys with buffered SSL. If he still has some + * at the end of the service, he'll get put back on the + * list then. + */ + __lws_ssl_remove_wsi_from_buffered_list(wsi); + } + + wsi = wsi_next; + } +#endif + + lws_pt_unlock(pt); + + return forced; +} + +static int +lws_service_periodic_checks(struct lws_context *context, + struct lws_pollfd *pollfd, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + lws_sockfd_type our_fd = 0, tmp_fd; + struct lws *wsi; + int timed_out = 0; + time_t now; +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + struct allocated_headers *ah; + int m; +#endif + + if (!context->protocol_init_done) + if (lws_protocol_init(context)) + return -1; + + time(&now); + + /* + * handle case that system time was uninitialized when lws started + * at boot, and got initialized a little later + */ + if (context->time_up < 1464083026 && now > 1464083026) + context->time_up = now; + + if (context->last_timeout_check_s && + now - context->last_timeout_check_s > 100) { + /* + * There has been a discontiguity. Any stored time that is + * less than context->time_discontiguity should have context-> + * time_fixup added to it. + * + * Some platforms with no RTC will experience this as a normal + * event when ntp sets their clock, but we can have started + * long before that with a 0-based unix time. + */ + + context->time_discontiguity = now; + context->time_fixup = now - context->last_timeout_check_s; + + lwsl_notice("time discontiguity: at old time %llus, " + "new time %llus: +%llus\n", + (unsigned long long)context->last_timeout_check_s, + (unsigned long long)context->time_discontiguity, + (unsigned long long)context->time_fixup); + + context->last_timeout_check_s = now - 1; + } + + if (!lws_compare_time_t(context, context->last_timeout_check_s, now)) + return 0; + + context->last_timeout_check_s = now; + +#if defined(LWS_WITH_STATS) + if (!tsi && now - context->last_dump > 10) { + lws_stats_log_dump(context); + context->last_dump = now; + } +#endif + + lws_plat_service_periodic(context); + lws_check_deferred_free(context, 0); + +#if defined(LWS_WITH_PEER_LIMITS) + lws_peer_cull_peer_wait_list(context); +#endif + + /* retire unused deprecated context */ +#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_ESP32) +#if !defined(_WIN32) + if (context->deprecated && !context->count_wsi_allocated) { + lwsl_notice("%s: ending deprecated context\n", __func__); + kill(getpid(), SIGINT); + return 0; + } +#endif +#endif + /* global timeout check once per second */ + + if (pollfd) + our_fd = pollfd->fd; + + /* + * Phase 1: check every wsi on the timeout check list + */ + + lws_pt_lock(pt, __func__); + + lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, + context->pt[tsi].dll_head_timeout.next) { + wsi = lws_container_of(d, struct lws, dll_timeout); + tmp_fd = wsi->desc.sockfd; + if (__lws_service_timeout_check(wsi, now)) { + /* he did time out... */ + if (tmp_fd == our_fd) + /* it was the guy we came to service! */ + timed_out = 1; + /* he's gone, no need to mark as handled */ + } + } lws_end_foreach_dll_safe(d, d1); + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + /* + * Phase 2: double-check active ah timeouts independent of wsi + * timeout status + */ + + ah = pt->http.ah_list; + while (ah) { + int len; + char buf[256]; + const unsigned char *c; + + if (!ah->in_use || !ah->wsi || !ah->assigned || + (ah->wsi->vhost && + lws_compare_time_t(context, now, ah->assigned) < + ah->wsi->vhost->timeout_secs_ah_idle + 360)) { + ah = ah->next; + continue; + } + + /* + * a single ah session somehow got held for + * an unreasonable amount of time. + * + * Dump info on the connection... + */ + wsi = ah->wsi; + buf[0] = '\0'; +#if !defined(LWS_PLAT_OPTEE) + lws_get_peer_simple(wsi, buf, sizeof(buf)); +#else + buf[0] = '\0'; +#endif + lwsl_notice("ah excessive hold: wsi %p\n" + " peer address: %s\n" + " ah pos %u\n", + wsi, buf, ah->pos); + buf[0] = '\0'; + m = 0; + do { + c = lws_token_to_string(m); + if (!c) + break; + if (!(*c)) + break; + + len = lws_hdr_total_length(wsi, m); + if (!len || len > (int)sizeof(buf) - 1) { + m++; + continue; + } + + if (lws_hdr_copy(wsi, buf, + sizeof buf, m) > 0) { + buf[sizeof(buf) - 1] = '\0'; + + lwsl_notice(" %s = %s\n", + (const char *)c, buf); + } + m++; + } while (1); + + /* explicitly detach the ah */ + lws_header_table_detach(wsi, 0); + + /* ... and then drop the connection */ + + m = 0; + if (wsi->desc.sockfd == our_fd) { + m = timed_out; + + /* it was the guy we came to service! */ + timed_out = 1; + } + + if (!m) /* if he didn't already timeout */ + __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "excessive ah"); + + ah = pt->http.ah_list; + } +#endif + lws_pt_unlock(pt); + +#if 0 + { + char s[300], *p = s; + + for (n = 0; n < context->count_threads; n++) + p += sprintf(p, " %7lu (%5d), ", + context->pt[n].count_conns, + context->pt[n].fds_count); + + lwsl_notice("load: %s\n", s); + } +#endif + /* + * Phase 3: vhost / protocol timer callbacks + */ + + wsi = NULL; + lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) { + struct lws_timed_vh_protocol *nx; + if (v->timed_vh_protocol_list) { + lws_start_foreach_ll(struct lws_timed_vh_protocol *, + q, v->timed_vh_protocol_list) { + if (now >= q->time) { + if (!wsi) + wsi = lws_zalloc(sizeof(*wsi), "cbwsi"); + wsi->context = context; + wsi->vhost = v; + wsi->protocol = q->protocol; + lwsl_debug("timed cb: vh %s, protocol %s, reason %d\n", v->name, q->protocol->name, q->reason); + q->protocol->callback(wsi, q->reason, NULL, NULL, 0); + nx = q->next; + lws_timed_callback_remove(v, q); + q = nx; + continue; /* we pointed ourselves to the next from the now-deleted guy */ + } + } lws_end_foreach_ll(q, next); + } + } lws_end_foreach_ll(v, vhost_next); + if (wsi) + lws_free(wsi); + + /* + * Phase 4: check for unconfigured vhosts due to required + * interface missing before + */ + + lws_context_lock(context); + lws_start_foreach_llp(struct lws_vhost **, pv, + context->no_listener_vhost_list) { + struct lws_vhost *v = *pv; + lwsl_debug("deferred iface: checking if on vh %s\n", (*pv)->name); + if (_lws_vhost_init_server(NULL, *pv) == 0) { + /* became happy */ + lwsl_notice("vh %s: became connected\n", v->name); + *pv = v->no_listener_vhost_list; + v->no_listener_vhost_list = NULL; + break; + } + } lws_end_foreach_llp(pv, no_listener_vhost_list); + lws_context_unlock(context); + + /* + * Phase 5: role periodic checks + */ +#if defined(LWS_ROLE_WS) + role_ops_ws.periodic_checks(context, tsi, now); +#endif +#if defined(LWS_ROLE_CGI) + role_ops_cgi.periodic_checks(context, tsi, now); +#endif + + /* + * Phase 6: check the remaining cert lifetime daily + */ + + if (context->tls_ops && + context->tls_ops->periodic_housekeeping) + context->tls_ops->periodic_housekeeping(context, now); + + return timed_out; +} + +LWS_VISIBLE int +lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, + int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws *wsi; + + if (!context || context->being_destroyed1) + return -1; + + /* the socket we came to service timed out, nothing to do */ + if (lws_service_periodic_checks(context, pollfd, tsi) || !pollfd) + return 0; + + /* no, here to service a socket descriptor */ + wsi = wsi_from_fd(context, pollfd->fd); + if (!wsi) + /* not lws connection ... leave revents alone and return */ + return 0; + + /* + * so that caller can tell we handled, past here we need to + * zero down pollfd->revents after handling + */ + + /* handle session socket closed */ + + if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) && + (pollfd->revents & LWS_POLLHUP)) { + wsi->socket_is_permanently_unusable = 1; + lwsl_debug("Session Socket %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + goto close_and_handled; + } + +#ifdef _WIN32 + if (pollfd->revents & LWS_POLLOUT) + wsi->sock_send_blocking = FALSE; +#endif + + if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) && + (pollfd->revents & LWS_POLLHUP)) { + lwsl_debug("pollhup\n"); + wsi->socket_is_permanently_unusable = 1; + goto close_and_handled; + } + +#if defined(LWS_WITH_TLS) + if (lwsi_state(wsi) == LRS_SHUTDOWN && + lws_is_ssl(wsi) && wsi->tls.ssl) { + switch (__lws_tls_shutdown(wsi)) { + case LWS_SSL_CAPABLE_DONE: + case LWS_SSL_CAPABLE_ERROR: + goto close_and_handled; + + case LWS_SSL_CAPABLE_MORE_SERVICE_READ: + case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE: + case LWS_SSL_CAPABLE_MORE_SERVICE: + goto handled; + } + } +#endif + wsi->could_have_pending = 0; /* clear back-to-back write detection */ + + /* okay, what we came here to do... */ + + /* if we got here, we should have wire protocol ops set on the wsi */ + assert(wsi->role_ops); + + // lwsl_notice("%s: %s: wsistate 0x%x\n", __func__, wsi->role_ops->name, + // wsi->wsistate); + + switch ((wsi->role_ops->handle_POLLIN)(pt, wsi, pollfd)) { + case LWS_HPI_RET_WSI_ALREADY_DIED: + return 1; + case LWS_HPI_RET_HANDLED: + break; + case LWS_HPI_RET_PLEASE_CLOSE_ME: +close_and_handled: + lwsl_debug("%p: Close and handled\n", wsi); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "close_and_handled"); +#if defined(_DEBUG) && defined(LWS_WITH_LIBUV) + /* + * confirm close has no problem being called again while + * it waits for libuv service to complete the first async + * close + */ + if (context->event_loop_ops == &event_loop_ops_uv) + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "close_and_handled uv repeat test"); +#endif + /* + * pollfd may point to something else after the close + * due to pollfd swapping scheme on delete on some platforms + * we can't clear revents now because it'd be the wrong guy's + * revents + */ + return 1; + default: + assert(0); + } +#if defined(LWS_WITH_TLS) +handled: +#endif + pollfd->revents = 0; + + lws_pt_lock(pt, __func__); + __lws_hrtimer_service(pt); + lws_pt_unlock(pt); + + return 0; +} + +LWS_VISIBLE int +lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd) +{ + return lws_service_fd_tsi(context, pollfd, 0); +} + +LWS_VISIBLE int +lws_service(struct lws_context *context, int timeout_ms) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int n; + + if (!context) + return 1; + + pt->inside_service = 1; + + if (context->event_loop_ops->run_pt) { + /* we are configured for an event loop */ + context->event_loop_ops->run_pt(context, 0); + + pt->inside_service = 0; + + return 1; + } + n = lws_plat_service(context, timeout_ms); + + pt->inside_service = 0; + + return n; +} + +LWS_VISIBLE int +lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + int n; + + pt->inside_service = 1; + + if (context->event_loop_ops->run_pt) { + /* we are configured for an event loop */ + context->event_loop_ops->run_pt(context, tsi); + + pt->inside_service = 0; + + return 1; + } + + n = _lws_plat_service_tsi(context, timeout_ms, tsi); + + pt->inside_service = 0; + + return n; +} |