Skip to content
Snippets Groups Projects
context.c 45.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		context->tls.alpn_default = info->alpn;
    
    		char *p = context->tls.alpn_discovered, first = 1;
    
    		LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) {
    			if (ar->alpn) {
    
    				p += lws_snprintf(p,
    					context->tls.alpn_discovered +
    					sizeof(context->tls.alpn_discovered) -
    					2 - p, "%s", ar->alpn);
    
    		} LWS_FOR_EVERY_AVAILABLE_ROLE_END;
    
    
    		context->tls.alpn_default = context->tls.alpn_discovered;
    
    	lwsl_info("Default ALPN advertisment: %s\n", context->tls.alpn_default);
    
    Andy Green's avatar
    Andy Green committed
    	if (info->timeout_secs)
    		context->timeout_secs = info->timeout_secs;
    	else
    		context->timeout_secs = AWAITING_TIMEOUT;
    
    
    	context->ws_ping_pong_interval = info->ws_ping_pong_interval;
    
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" default timeout (secs): %u\n", context->timeout_secs);
    
    Andy Green's avatar
    Andy Green committed
    	if (info->max_http_header_data)
    		context->max_http_header_data = info->max_http_header_data;
    	else
    
    		if (info->max_http_header_data2)
    			context->max_http_header_data =
    					info->max_http_header_data2;
    		else
    			context->max_http_header_data = LWS_DEF_HEADER_LEN;
    
    Andy Green's avatar
    Andy Green committed
    	if (info->max_http_header_pool)
    		context->max_http_header_pool = info->max_http_header_pool;
    	else
    
    		if (info->max_http_header_pool2)
    			context->max_http_header_pool =
    					info->max_http_header_pool2;
    		else
    			context->max_http_header_pool = context->max_fds;
    
    	if (info->fd_limit_per_thread)
    		context->fd_limit_per_thread = info->fd_limit_per_thread;
    	else
    		context->fd_limit_per_thread = context->max_fds /
    					       context->count_threads;
    
    
    Andy Green's avatar
    Andy Green committed
    	/*
    	 * Allocate the per-thread storage for scratchpad buffers,
    	 * and header data pool
    
    Andy Green's avatar
    Andy Green committed
    	 */
    	for (n = 0; n < context->count_threads; n++) {
    
    		context->pt[n].serv_buf = lws_malloc(context->pt_serv_buf_size,
    						     "pt_serv_buf");
    
    Andy Green's avatar
    Andy Green committed
    		if (!context->pt[n].serv_buf) {
    			lwsl_err("OOM\n");
    			return NULL;
    		}
    
    		context->pt[n].context = context;
    		context->pt[n].tid = n;
    
    #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
    
    		context->pt[n].http.ah_list = NULL;
    		context->pt[n].http.ah_pool_length = 0;
    
    Andy Green's avatar
    Andy Green committed
    		lws_pt_mutex_init(&context->pt[n]);
    
    	lwsl_info(" Threads: %d each %d fds\n", context->count_threads,
    
    Andy Green's avatar
    Andy Green committed
    		    context->fd_limit_per_thread);
    
    
    	if (!info->ka_interval && info->ka_time > 0) {
    		lwsl_err("info->ka_interval can't be 0 if ka_time used\n");
    		return NULL;
    	}
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_PEER_LIMITS)
    	/* scale the peer hash table according to the max fds for the process,
    	 * so that the max list depth averages 16.  Eg, 1024 fd -> 64,
    	 * 102400 fd -> 6400
    	 */
    
    Andy Green's avatar
    Andy Green committed
    	context->pl_hash_elements =
    		(context->count_threads * context->fd_limit_per_thread) / 16;
    	context->pl_hash_table = lws_zalloc(sizeof(struct lws_peer *) *
    
    Andy Green's avatar
    Andy Green committed
    			context->pl_hash_elements, "peer limits hash table");
    
    Andy Green's avatar
    Andy Green committed
    	context->ip_limit_ah = info->ip_limit_ah;
    	context->ip_limit_wsi = info->ip_limit_wsi;
    #endif
    
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" mem: context:         %5lu B (%ld ctx + (%ld thr x %d))\n",
    
    		  (long)sizeof(struct lws_context) +
    
    		  (context->count_threads * context->pt_serv_buf_size),
    
    		  (long)sizeof(struct lws_context),
    		  (long)context->count_threads,
    
    #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" mem: http hdr rsvd:   %5lu B (%u thr x (%u + %lu) x %u))\n",
    
    		    (long)(context->max_http_header_data +
    
    Andy Green's avatar
    Andy Green committed
    		     sizeof(struct allocated_headers)) *
    
    Andy Green's avatar
    Andy Green committed
    		    context->max_http_header_pool * context->count_threads,
    		    context->count_threads,
    
    Andy Green's avatar
    Andy Green committed
    		    context->max_http_header_data,
    
    		    (long)sizeof(struct allocated_headers),
    
    		    context->max_http_header_pool);
    
    Andy Green's avatar
    Andy Green committed
    	n = sizeof(struct lws_pollfd) * context->count_threads *
    	    context->fd_limit_per_thread;
    
    Andy Green's avatar
    Andy Green committed
    	context->pt[0].fds = lws_zalloc(n, "fds table");
    
    Andy Green's avatar
    Andy Green committed
    	if (context->pt[0].fds == NULL) {
    
    		lwsl_err("OOM allocating %d fds\n", context->max_fds);
    
    		goto bail;
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" mem: pollfd map:      %5u\n", n);
    
    	if (info->server_string) {
    		context->server_string = info->server_string;
    
    		context->server_string_len = (short)
    				strlen(context->server_string);
    
    Andy Green's avatar
    Andy Green committed
    #if LWS_MAX_SMP > 1
    
    Andy Green's avatar
    Andy Green committed
    	/* each thread serves his own chunk of fds */
    
    	for (n = 1; n < (int)context->count_threads; n++)
    
    Andy Green's avatar
    Andy Green committed
    		context->pt[n].fds = context->pt[n - 1].fds +
    				     context->fd_limit_per_thread;
    #endif
    
    	if (lws_plat_init(context, info))
    
    		goto bail;
    
    	if (context->event_loop_ops->init_context)
    		if (context->event_loop_ops->init_context(context, info))
    			goto bail;
    
    
    	if (context->event_loop_ops->init_pt)
    		for (n = 0; n < context->count_threads; n++) {
    			void *lp = NULL;
    
    			if (info->foreign_loops)
    				lp = info->foreign_loops[n];
    
    			if (context->event_loop_ops->init_pt(context, lp, n))
    				goto bail;
    		}
    
    	if (lws_create_event_pipes(context))
    		goto bail;
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_context_init_ssl_library(info);
    
    
    Andy Green's avatar
    Andy Green committed
    	context->user_space = info->user;
    
    
    Andy Green's avatar
    Andy Green committed
    	/*
    	 * if he's not saying he'll make his own vhosts later then act
    	 * compatibly and make a default vhost using the data in the info
    	 */
    	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
    
    		if (!lws_create_vhost(context, info)) {
    
    Andy Green's avatar
    Andy Green committed
    			lwsl_err("Failed to create default vhost\n");
    
    			for (n = 0; n < context->count_threads; n++)
    				lws_free_set_NULL(context->pt[n].serv_buf);
    #if defined(LWS_WITH_PEER_LIMITS)
    			lws_free_set_NULL(context->pl_hash_table);
    #endif
    			lws_free_set_NULL(context->pt[0].fds);
    			lws_plat_context_late_destroy(context);
    			lws_free_set_NULL(context);
    
    Andy Green's avatar
    Andy Green committed
    			return NULL;
    		}
    
    
    	lws_context_init_extensions(info, context);
    
    
    	lwsl_info(" mem: per-conn:        %5lu bytes + protocol rx buf\n",
    
    	strcpy(context->canonical_hostname, "unknown");
    
    	lws_server_get_canonical_hostname(context, info);
    
    Andy Green's avatar
    Andy Green committed
    	context->uid = info->uid;
    	context->gid = info->gid;
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
    	memcpy(context->caps, info->caps, sizeof(context->caps));
    	context->count_caps = info->count_caps;
    #endif
    
    
    	/*
    	 * drop any root privs for this process
    	 * to listen on port < 1023 we would have needed root, but now we are
    	 * listening, we don't want the power for anything else
    	 */
    
    Andy Green's avatar
    Andy Green committed
    	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
    		lws_plat_drop_app_privileges(info);
    
    Andy Green's avatar
    Andy Green committed
    	/* expedite post-context init (eg, protocols) */
    	lws_cancel_service(context);
    
    
    #if defined(LWS_WITH_SELFTESTS)
    	lws_jws_selftest();
    #endif
    
    
    	lws_context_destroy(context);
    
    	return NULL;
    
    fail_event_libs:
    	lwsl_err("Requested event library support not configured, available:\n");
    	{
    		const struct lws_event_loop_ops **elops = available_event_libs;
    
    		while (*elops) {
    			lwsl_err("  - %s\n", (*elops)->name);
    			elops++;
    		}
    	}
    	lws_free(context);
    
    
    Andy Green's avatar
    Andy Green committed
    LWS_VISIBLE LWS_EXTERN void
    lws_context_deprecate(struct lws_context *context, lws_reload_func cb)
    {
    	struct lws_vhost *vh = context->vhost_list, *vh1;
    
    	/*
    	 * "deprecation" means disable the context from accepting any new
    	 * connections and free up listen sockets to be used by a replacement
    	 * context.
    	 *
    	 * Otherwise the deprecated context remains operational, until its
    	 * number of connected sockets falls to zero, when it is deleted.
    	 */
    
    	/* for each vhost, close his listen socket */
    
    	while (vh) {
    
    Andy Green's avatar
    Andy Green committed
    		struct lws *wsi = vh->lserv_wsi;
    
    
    Andy Green's avatar
    Andy Green committed
    		if (wsi) {
    			wsi->socket_is_permanently_unusable = 1;
    
    			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "ctx deprecate");
    
    Andy Green's avatar
    Andy Green committed
    			wsi->context->deprecation_pending_listen_close_count++;
    			/*
    			 * other vhosts can share the listen port, they
    			 * point to the same wsi.  So zap those too.
    			 */
    			vh1 = context->vhost_list;
    			while (vh1) {
    				if (vh1->lserv_wsi == wsi)
    					vh1->lserv_wsi = NULL;
    				vh1 = vh1->vhost_next;
    			}
    		}
    		vh = vh->vhost_next;
    	}
    
    	context->deprecated = 1;
    	context->deprecation_cb = cb;
    }
    
    LWS_VISIBLE LWS_EXTERN int
    lws_context_is_deprecated(struct lws_context *context)
    {
    	return context->deprecated;
    }
    
    
    Andy Green's avatar
    Andy Green committed
    lws_vhost_destroy1(struct lws_vhost *vh)
    {
    	struct lws_context *context = vh->context;
    
    
    	lwsl_info("%s\n", __func__);
    
    Andy Green's avatar
    Andy Green committed
    
    
    	lws_context_lock(context, "vhost destroy 1"); /* ---------- context { */
    
    
    Andy Green's avatar
    Andy Green committed
    	if (vh->being_destroyed)
    
    Andy Green's avatar
    Andy Green committed
    
    
    	lws_vhost_lock(vh); /* -------------- vh { */
    
    
    Andy Green's avatar
    Andy Green committed
    	vh->being_destroyed = 1;
    
    	/*
    
    	 * PHASE 1: take down or reassign any listen wsi
    	 *
    
    Andy Green's avatar
    Andy Green committed
    	 * Are there other vhosts that are piggybacking on our listen socket?
    	 * If so we need to hand the listen socket off to one of the others
    
    	 * so it will remain open.
    	 *
    	 * If not, leave it attached to the closing vhost, the vh being marked
    	 * being_destroyed will defeat any service and it will get closed in
    	 * later phases.
    
    Andy Green's avatar
    Andy Green committed
    	 */
    
    	if (vh->lserv_wsi)
    
    Andy Green's avatar
    Andy Green committed
    		lws_start_foreach_ll(struct lws_vhost *, v,
    				     context->vhost_list) {
    
    Andy Green's avatar
    Andy Green committed
    			if (v != vh &&
    			    !v->being_destroyed &&
    			    v->listen_port == vh->listen_port &&
    			    ((!v->iface && !vh->iface) ||
    			    (v->iface && vh->iface &&
    			    !strcmp(v->iface, vh->iface)))) {
    				/*
    				 * this can only be a listen wsi, which is
    				 * restricted... it has no protocol or other
    				 * bindings or states.  So we can simply
    				 * swap it to a vhost that has the same
    				 * iface + port, but is not closing.
    				 */
    				assert(v->lserv_wsi == NULL);
    				v->lserv_wsi = vh->lserv_wsi;
    
    				lwsl_notice("%s: listen skt from %s to %s\n",
    					    __func__, vh->name, v->name);
    
    
    				if (v->lserv_wsi) {
    					lws_vhost_unbind_wsi(vh->lserv_wsi);
    					lws_vhost_bind_wsi(v, v->lserv_wsi);
    				}
    
    Andy Green's avatar
    Andy Green committed
    
    				break;
    			}
    		} lws_end_foreach_ll(v, vhost_next);
    
    
    	lws_vhost_unlock(vh); /* } vh -------------- */
    
    
    Andy Green's avatar
    Andy Green committed
    	/*
    
    	 * lws_check_deferred_free() will notice there is a vhost that is
    	 * marked for destruction during the next 1s, for all tsi.
    	 *
    	 * It will start closing all wsi on this vhost.  When the last wsi
    	 * is closed, it will trigger lws_vhost_destroy2()
    
    Andy Green's avatar
    Andy Green committed
    	 */
    
    
    out:
    	lws_context_unlock(context); /* --------------------------- context { */
    
    Andy Green's avatar
    Andy Green committed
    
    
    __lws_vhost_destroy2(struct lws_vhost *vh)
    
    {
    	const struct lws_protocols *protocol = NULL;
    	struct lws_context *context = vh->context;
    	struct lws_deferred_free *df;
    	struct lws wsi;
    	int n;
    
    Andy Green's avatar
    Andy Green committed
    
    
    	/*
    	 * destroy any pending timed events
    	 */
    
    	while (vh->timed_vh_protocol_list)
    
    		__lws_timed_callback_remove(vh, vh->timed_vh_protocol_list);
    
    Andy Green's avatar
    Andy Green committed
    	/*
    	 * let the protocols destroy the per-vhost protocol objects
    	 */
    
    	memset(&wsi, 0, sizeof(wsi));
    	wsi.context = vh->context;
    
    	wsi.vhost = vh; /* not a real bound wsi */
    
    Andy Green's avatar
    Andy Green committed
    	protocol = vh->protocols;
    
    	if (protocol && vh->created_vhost_protocols) {
    
    Andy Green's avatar
    Andy Green committed
    		n = 0;
    		while (n < vh->count_protocols) {
    			wsi.protocol = protocol;
    			protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY,
    					   NULL, NULL, 0);
    			protocol++;
    			n++;
    		}
    	}
    
    	/*
    	 * remove vhost from context list of vhosts
    	 */
    
    	lws_start_foreach_llp(struct lws_vhost **, pv, context->vhost_list) {
    		if (*pv == vh) {
    			*pv = vh->vhost_next;
    			break;
    		}
    	} lws_end_foreach_llp(pv, vhost_next);
    
    	/* add ourselves to the pending destruction list */
    
    	vh->vhost_next = vh->context->vhost_pending_destruction_list;
    	vh->context->vhost_pending_destruction_list = vh;
    
    
    	lwsl_info("%s: %p\n", __func__, vh);
    
    Andy Green's avatar
    Andy Green committed
    
    	/* if we are still on deferred free list, remove ourselves */
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_start_foreach_llp(struct lws_deferred_free **, pdf,
    			      context->deferred_free_list) {
    
    Andy Green's avatar
    Andy Green committed
    		if ((*pdf)->payload == vh) {
    			df = *pdf;
    			*pdf = df->next;
    			lws_free(df);
    			break;
    		}
    	} lws_end_foreach_llp(pdf, next);
    
    	/* remove ourselves from the pending destruction list */
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_start_foreach_llp(struct lws_vhost **, pv,
    			      context->vhost_pending_destruction_list) {
    
    Andy Green's avatar
    Andy Green committed
    		if ((*pv) == vh) {
    			*pv = (*pv)->vhost_next;
    			break;
    		}
    	} lws_end_foreach_llp(pv, vhost_next);
    
    	/*
    	 * Free all the allocations associated with the vhost
    	 */
    
    	protocol = vh->protocols;
    	if (protocol) {
    		n = 0;
    		while (n < vh->count_protocols) {
    			if (vh->protocol_vh_privs &&
    			    vh->protocol_vh_privs[n]) {
    				lws_free(vh->protocol_vh_privs[n]);
    				vh->protocol_vh_privs[n] = NULL;
    			}
    			protocol++;
    			n++;
    		}
    	}
    	if (vh->protocol_vh_privs)
    		lws_free(vh->protocol_vh_privs);
    	lws_ssl_SSL_CTX_destroy(vh);
    
    	lws_free(vh->same_vh_protocol_heads);
    
    
    	if (context->plugin_list ||
    	    (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
    		lws_free((void *)vh->protocols);
    
    Andy Green's avatar
    Andy Green committed
    
    
    	LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
    		if (ar->destroy_vhost)
    			ar->destroy_vhost(vh);
    	LWS_FOR_EVERY_AVAILABLE_ROLE_END;
    
    
    Andy Green's avatar
    Andy Green committed
    #ifdef LWS_WITH_ACCESS_LOG
    	if (vh->log_fd != (int)LWS_INVALID_FILE)
    		close(vh->log_fd);
    #endif
    
    
    #if defined (LWS_WITH_TLS)
    	lws_free_set_NULL(vh->tls.alloc_cert_path);
    #endif
    
    Andy Green's avatar
    Andy Green committed
    #if LWS_MAX_SMP > 1
           pthread_mutex_destroy(&vh->lock);
    #endif
    
    
    #if defined(LWS_WITH_UNIX_SOCK)
    
    	if (LWS_UNIX_SOCK_ENABLED(vh)) {
    
    		n = unlink(vh->iface);
    		if (n)
    			lwsl_info("Closing unix socket %s: errno %d\n",
    				  vh->iface, errno);
    	}
    #endif
    
    Andy Green's avatar
    Andy Green committed
    	/*
    	 * although async event callbacks may still come for wsi handles with
    	 * pending close in the case of asycn event library like libuv,
    	 * they do not refer to the vhost.  So it's safe to free.
    	 */
    
    
    	if (vh->finalize)
    		vh->finalize(vh, vh->finalize_arg);
    
    
    	lwsl_info("  %s: Freeing vhost %p\n", __func__, vh);
    
    Andy Green's avatar
    Andy Green committed
    
    	memset(vh, 0, sizeof(*vh));
    
    	lws_free(vh);
    
    /*
     * each service thread calls this once a second or so
     */
    
    
    Andy Green's avatar
    Andy Green committed
    int
    
    lws_check_deferred_free(struct lws_context *context, int tsi, int force)
    
    Andy Green's avatar
    Andy Green committed
    {
    
    	struct lws_context_per_thread *pt;
    	int n;
    
    Andy Green's avatar
    Andy Green committed
    
    
    	/*
    	 * If we see a vhost is being destroyed, forcibly close every wsi on
    	 * this tsi associated with this vhost.  That will include the listen
    	 * socket if it is still associated with the closing vhost.
    	 *
    	 * For SMP, we do this once per tsi per destroyed vhost.  The reference
    	 * counting on the vhost as the bound wsi close will notice that there
    	 * are no bound wsi left, that vhost destruction can complete,
    	 * and perform it.  It doesn't matter which service thread does that
    	 * because there is nothing left using the vhost to conflict.
    	 */
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_context_lock(context, "check deferred free"); /* ------ context { */
    
    James Chicca's avatar
    James Chicca committed
    	lws_start_foreach_ll_safe(struct lws_vhost *, v, context->vhost_list, vhost_next) {
    
    		if (v->being_destroyed
    #if LWS_MAX_SMP > 1
    			&& !v->close_flow_vs_tsi[tsi]
    #endif
    		) {
    
    			pt = &context->pt[tsi];
    
    			lws_pt_lock(pt, "vhost removal"); /* -------------- pt { */
    
    #if LWS_MAX_SMP > 1
    			v->close_flow_vs_tsi[tsi] = 1;
    #endif
    
    			for (n = 0; (unsigned int)n < pt->fds_count; n++) {
    				struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
    				if (!wsi)
    					continue;
    				if (wsi->vhost != v)
    					continue;
    
    				__lws_close_free_wsi(wsi,
    					LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY,
    					"vh destroy"
    					/* no protocol close */);
    				n--;
    			}
    
    			lws_pt_unlock(pt); /* } pt -------------- */
    
    Andy Green's avatar
    Andy Green committed
    		}
    
    James Chicca's avatar
    James Chicca committed
    	} lws_end_foreach_ll_safe(v);
    
    
    
    	lws_context_unlock(context); /* } context ------------------- */
    
    Andy Green's avatar
    Andy Green committed
    
    	return 0;
    }
    
    LWS_VISIBLE void
    lws_vhost_destroy(struct lws_vhost *vh)
    {
    
    Andy Green's avatar
    Andy Green committed
    	struct lws_deferred_free *df = lws_malloc(sizeof(*df), "deferred free");
    
    	struct lws_context *context = vh->context;
    
    Andy Green's avatar
    Andy Green committed
    
    	if (!df)
    		return;
    
    
    	lws_context_lock(context, __func__); /* ------ context { */
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_vhost_destroy1(vh);
    
    
    	if (!vh->count_bound_wsi) {
    		/*
    		 * After listen handoff, there are already no wsi bound to this
    		 * vhost by any pt: nothing can be servicing any wsi belonging
    		 * to it any more.
    		 *
    		 * Finalize the vh destruction immediately
    		 */
    
    		__lws_vhost_destroy2(vh);
    
    Andy Green's avatar
    Andy Green committed
    	/* part 2 is deferred to allow all the handle closes to complete */
    
    	df->next = vh->context->deferred_free_list;
    
    	df->deadline = lws_now_secs();
    
    Andy Green's avatar
    Andy Green committed
    	df->payload = vh;
    	vh->context->deferred_free_list = df;
    
    
    out:
    	lws_context_unlock(context); /* } context ------------------- */
    
    /*
     * When using an event loop, the context destruction is in three separate
     * parts.  This is to cover both internal and foreign event loops cleanly.
     *
     *  - lws_context_destroy() simply starts a soft close of all wsi and
     *     related allocations.  The event loop continues.
     *
     *     As the closes complete in the event loop, reference counting is used
     *     to determine when everything is closed.  It then calls
     *     lws_context_destroy2().
     *
     *  - lws_context_destroy2() cleans up the rest of the higher-level logical
     *     lws pieces like vhosts.  If the loop was foreign, it then proceeds to
     *     lws_context_destroy3().  If it the loop is internal, it stops the
     *     internal loops and waits for lws_context_destroy() to be called again
     *     outside the event loop (since we cannot destroy the loop from
     *     within the loop).  That will cause lws_context_destroy3() to run
     *     directly.
     *
     *  - lws_context_destroy3() destroys any internal event loops and then
     *     destroys the context itself, setting what was info.pcontext to NULL.
     */
    
    
    /*
     * destroy the actual context itself
     */
    
    
    static void
    lws_context_destroy3(struct lws_context *context)
    {
    	struct lws_context **pcontext_finalize = context->pcontext_finalize;
    
    	int n;
    
    	for (n = 0; n < context->count_threads; n++) {
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
    		struct lws_context_per_thread *pt = &context->pt[n];
    #endif
    
    
    		if (context->event_loop_ops->destroy_pt)
    			context->event_loop_ops->destroy_pt(context, n);
    
    		lws_free_set_NULL(context->pt[n].serv_buf);
    
    #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
    		while (pt->http.ah_list)
    			_lws_destroy_ah(pt, pt->http.ah_list);
    #endif
    	}
    
    	if (context->pt[0].fds)
    		lws_free_set_NULL(context->pt[0].fds);
    
    
    	lws_free(context);
    	lwsl_info("%s: ctx %p freed\n", __func__, context);
    
    	if (pcontext_finalize)
    		*pcontext_finalize = NULL;
    }
    
    
    void
    lws_context_destroy2(struct lws_context *context)
    {
    	struct lws_vhost *vh = NULL, *vh1;
    #if defined(LWS_WITH_PEER_LIMITS)
    
    #endif
    
    	lwsl_info("%s: ctx %p\n", __func__, context);
    
    
    	lws_context_lock(context, "context destroy 2"); /* ------ context { */
    
    
    	/*
    	 * free all the per-vhost allocations
    	 */
    
    	vh = context->vhost_list;
    	while (vh) {
    		vh1 = vh->vhost_next;
    
    		__lws_vhost_destroy2(vh);
    
    		vh = vh1;
    	}
    
    	/* remove ourselves from the pending destruction list */
    
    	while (context->vhost_pending_destruction_list)
    		/* removes itself from list */
    
    		__lws_vhost_destroy2(context->vhost_pending_destruction_list);
    
    
    
    	lws_stats_log_dump(context);
    
    	lws_ssl_context_destroy(context);
    	lws_plat_context_late_destroy(context);
    
    #if defined(LWS_WITH_PEER_LIMITS)
    
    	for (nu = 0; nu < context->pl_hash_elements; nu++)	{
    
    		lws_start_foreach_llp(struct lws_peer **, peer,
    
    				      context->pl_hash_table[nu]) {
    
    			struct lws_peer *df = *peer;
    			*peer = df->next;
    			lws_free(df);
    			continue;
    		} lws_end_foreach_llp(peer, next);
    	}
    	lws_free(context->pl_hash_table);
    #endif
    
    	if (context->external_baggage_free_on_destroy)
    		free(context->external_baggage_free_on_destroy);
    
    
    	lws_check_deferred_free(context, 0, 1);
    
    Andy Green's avatar
    Andy Green committed
    	lws_mutex_refcount_destroy(&context->mr);
    
    #endif
    
    	if (context->event_loop_ops->destroy_context2)
    		if (context->event_loop_ops->destroy_context2(context)) {
    
    Andy Green's avatar
    Andy Green committed
    			lws_context_unlock(context); /* } context ----------- */
    
    			context->finalize_destroy_after_internal_loops_stopped = 1;
    			return;
    		}
    
    
    Andy Green's avatar
    Andy Green committed
    	if (!context->pt[0].event_loop_foreign) {
    		int n;
    
    		for (n = 0; n < context->count_threads; n++)
    
    Andy Green's avatar
    Andy Green committed
    			if (context->pt[n].inside_service) {
    				lws_context_unlock(context); /* } context --- */
    
    Andy Green's avatar
    Andy Green committed
    			}
    
    Andy Green's avatar
    Andy Green committed
    	}
    
    	lws_context_unlock(context); /* } context ------------------- */
    
    
    	lws_context_destroy3(context);
    }
    
    
    lws_context_destroy(struct lws_context *context)
    
    	volatile struct lws_foreign_thread_pollfd *ftp, *next;
    	volatile struct lws_context_per_thread *vpt;
    
    Andy Green's avatar
    Andy Green committed
    	struct lws_vhost *vh = NULL;
    
    Andy Green's avatar
    Andy Green committed
    		return;
    
    
    	if (context->finalize_destroy_after_internal_loops_stopped) {
    		if (context->event_loop_ops->destroy_context2)
    			context->event_loop_ops->destroy_context2(context);
    
    		lws_context_destroy3(context);
    
    		return;
    	}
    
    
    Andy Green's avatar
    Andy Green committed
    	if (context->being_destroyed1) {
    
    		if (!context->being_destroyed2) {
    			lws_context_destroy2(context);
    
    			return;
    		}
    
    		lwsl_info("%s: ctx %p: already being destroyed\n",
    
    Andy Green's avatar
    Andy Green committed
    			    __func__, context);
    
    
    		lws_context_destroy3(context);
    
    	lwsl_info("%s: ctx %p\n", __func__, context);
    
    	context->being_destroyed = 1;
    
    Andy Green's avatar
    Andy Green committed
    	context->being_destroyed1 = 1;
    
    	context->requested_kill = 1;
    
    	memset(&wsi, 0, sizeof(wsi));
    	wsi.context = context;
    
    
    #ifdef LWS_LATENCY
    	if (context->worst_latency_info[0])
    		lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
    #endif
    
    
    Andy Green's avatar
    Andy Green committed
    	while (m--) {
    
    Andy Green's avatar
    Andy Green committed
    		struct lws_context_per_thread *pt = &context->pt[m];
    
    		vpt = (volatile struct lws_context_per_thread *)pt;
    
    		ftp = vpt->foreign_pfd_list;
    		while (ftp) {
    			next = ftp->next;
    			lws_free((void *)ftp);
    			ftp = next;
    		}
    		vpt->foreign_pfd_list = NULL;
    
    Andy Green's avatar
    Andy Green committed
    
    
    Andy Green's avatar
    Andy Green committed
    		for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
    
    Andy Green's avatar
    Andy Green committed
    			struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
    
    Andy Green's avatar
    Andy Green committed
    			if (!wsi)
    				continue;
    
    Andy Green's avatar
    Andy Green committed
    
    
    Sungtae Kim's avatar
    Sungtae Kim committed
    			if (wsi->event_pipe)
    				lws_destroy_event_pipe(wsi);
    			else
    
    					LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY,
    					"ctx destroy"
    
    Andy Green's avatar
    Andy Green committed
    			n--;
    		}
    
    		lws_pt_mutex_destroy(pt);
    
    Andy Green's avatar
    Andy Green committed
    	}
    
    Andy Green's avatar
    Andy Green committed
    
    
    	/*
    	 * inform all the protocols that they are done and will have no more
    
    Andy Green's avatar
    Andy Green committed
    	 * callbacks.
    	 *
    	 * We can't free things until after the event loop shuts down.
    
    	if (context->protocol_init_done)
    		vh = context->vhost_list;
    
    Andy Green's avatar
    Andy Green committed
    	while (vh) {
    
    		struct lws_vhost *vhn = vh->vhost_next;
    
    Andy Green's avatar
    Andy Green committed
    		lws_vhost_destroy1(vh);
    
    Andy Green's avatar
    Andy Green committed
    	}
    
    
    	lws_plat_context_early_destroy(context);
    
    Andy Green's avatar
    Andy Green committed
    
    
    	/*
    	 * We face two different needs depending if foreign loop or not.
    	 *
    	 * 1) If foreign loop, we really want to advance the destroy_context()
    	 *    past here, and block only for libuv-style async close completion.
    	 *
    	 * 2a) If poll, and we exited by ourselves and are calling a final
    	 *     destroy_context() outside of any service already, we want to
    	 *     advance all the way in one step.
    	 *
    	 * 2b) If poll, and we are reacting to a SIGINT, service thread(s) may
    	 *     be in poll wait or servicing.  We can't advance the
    	 *     destroy_context() to the point it's freeing things; we have to
    	 *     leave that for the final destroy_context() after the service
    	 *     thread(s) are finished calling for service.
    	 */
    
    	if (context->event_loop_ops->destroy_context1) {
    		context->event_loop_ops->destroy_context1(context);
    
    Andy Green's avatar
    Andy Green committed
    
    
    Andy Green's avatar
    Andy Green committed
    	}
    
    	lws_context_destroy2(context);