예제 #1
0
void
lws_cgi_remove_and_kill(struct lws *wsi)
{
	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
	struct lws_cgi **pcgi = &pt->http.cgi_list;

	/* remove us from the cgi list */
	lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->http.cgi);
	while (*pcgi) {
		if (*pcgi == wsi->http.cgi) {
			/* drop us from the pt cgi list */
			*pcgi = (*pcgi)->cgi_list;
			break;
		}
		pcgi = &(*pcgi)->cgi_list;
	}
	if (wsi->http.cgi->headers_buf) {
		lwsl_debug("close: freed cgi headers\n");
		lws_free_set_NULL(wsi->http.cgi->headers_buf);
	}
	/* we have a cgi going, we must kill it */
	wsi->http.cgi->being_closed = 1;
	lws_cgi_kill(wsi);
}
예제 #2
0
void
lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
{
	struct lws_context_per_thread *pt;
	struct lws **pwsi, *wsi1, *wsi2;
	struct lws_context *context;
	struct lws_tokens eff_buf;
	int n, m, ret;

	if (!wsi)
		return;

	context = wsi->context;
	pt = &context->pt[(int)wsi->tsi];

	/* if we have children, close them first */
	if (wsi->child_list) {
		wsi2 = wsi->child_list;
		while (wsi2) {
			//lwsl_notice("%s: closing %p: close child %p\n",
			//		__func__, wsi, wsi2);
			wsi1 = wsi2->sibling_list;
			//lwsl_notice("%s: closing %p: next sibling %p\n",
			//		__func__, wsi2, wsi1);
			wsi2->parent = NULL;
			/* stop it doing shutdown processing */
			wsi2->socket_is_permanently_unusable = 1;
			lws_close_free_wsi(wsi2, reason);
			wsi2 = wsi1;
		}
		wsi->child_list = NULL;
	}

#ifdef LWS_WITH_CGI
	if (wsi->mode == LWSCM_CGI) {
		/* we are not a network connection, but a handler for CGI io */
		if (wsi->parent && wsi->parent->cgi)
		/* end the binding between us and master */
		wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL;
		wsi->socket_is_permanently_unusable = 1;

		goto just_kill_connection;
	}

	if (wsi->cgi) {
		/* we have a cgi going, we must kill it */
		wsi->cgi->being_closed = 1;
		lws_cgi_kill(wsi);
	}
#endif

	if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED &&
	    wsi->u.http.fd != LWS_INVALID_FILE) {
		lws_plat_file_close(wsi, wsi->u.http.fd);
		wsi->u.http.fd = LWS_INVALID_FILE;
		context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
					       wsi->user_space, NULL, 0);
	}
	if (wsi->socket_is_permanently_unusable ||
	    reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
	    wsi->state == LWSS_SHUTDOWN)
		goto just_kill_connection;

	wsi->state_pre_close = wsi->state;

	switch (wsi->state_pre_close) {
	case LWSS_DEAD_SOCKET:
		return;

	/* we tried the polite way... */
	case LWSS_AWAITING_CLOSE_ACK:
		goto just_kill_connection;

	case LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE:
		if (wsi->trunc_len) {
			lws_callback_on_writable(wsi);
			return;
		}
		lwsl_info("wsi %p completed LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
		goto just_kill_connection;
	default:
		if (wsi->trunc_len) {
			lwsl_info("wsi %p entering LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
			wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE;
			lws_set_timeout(wsi, PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
			return;
		}
		break;
	}

	if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT ||
	    wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
		goto just_kill_connection;

	if (wsi->mode == LWSCM_HTTP_SERVING)
		context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
					       wsi->user_space, NULL, 0);
	if (wsi->mode == LWSCM_HTTP_CLIENT)
		context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP,
					       wsi->user_space, NULL, 0);

	/*
	 * are his extensions okay with him closing?  Eg he might be a mux
	 * parent and just his ch1 aspect is closing?
	 */

	if (lws_ext_cb_active(wsi,
		      LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) {
		lwsl_ext("extension vetoed close\n");
		return;
	}

	/*
	 * flush any tx pending from extensions, since we may send close packet
	 * if there are problems with send, just nuke the connection
	 */

	do {
		ret = 0;
		eff_buf.token = NULL;
		eff_buf.token_len = 0;

		/* show every extension the new incoming data */

		m = lws_ext_cb_active(wsi,
			  LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0);
		if (m < 0) {
			lwsl_ext("Extension reports fatal error\n");
			goto just_kill_connection;
		}
		if (m)
			/*
			 * at least one extension told us he has more
			 * to spill, so we will go around again after
			 */
			ret = 1;

		/* assuming they left us something to send, send it */

		if (eff_buf.token_len)
			if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
					  eff_buf.token_len) !=
			    eff_buf.token_len) {
				lwsl_debug("close: ext spill failed\n");
				goto just_kill_connection;
			}
	} while (ret);

	/*
	 * signal we are closing, lws_write will
	 * add any necessary version-specific stuff.  If the write fails,
	 * no worries we are closing anyway.  If we didn't initiate this
	 * close, then our state has been changed to
	 * LWSS_RETURNED_CLOSE_ALREADY and we will skip this.
	 *
	 * Likewise if it's a second call to close this connection after we
	 * sent the close indication to the peer already, we are in state
	 * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time.
	 */

	if (wsi->state_pre_close == LWSS_ESTABLISHED &&
	    (wsi->u.ws.close_in_ping_buffer_len || /* already a reason */
	     (reason != LWS_CLOSE_STATUS_NOSTATUS &&
	     (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) {
		lwsl_debug("sending close indication...\n");

		/* if no prepared close reason, use 1000 and no aux data */
		if (!wsi->u.ws.close_in_ping_buffer_len) {
			wsi->u.ws.close_in_ping_buffer_len = 2;
			wsi->u.ws.ping_payload_buf[LWS_PRE] =
				(reason >> 16) & 0xff;
			wsi->u.ws.ping_payload_buf[LWS_PRE + 1] =
				reason & 0xff;
		}
예제 #3
0
LWS_EXTERN int
lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
{
	struct lws_cgi **pcgi, *cgi = NULL;
	int status, n = 1;

	while (n > 0) {
		/* find finished guys but don't reap yet */
		n = waitpid(-1, &status, WNOHANG);
		if (n <= 0)
			continue;
		lwsl_debug("%s: observed PID %d terminated\n", __func__, n);

		pcgi = &pt->http.cgi_list;

		/* check all the subprocesses on the cgi list */
		while (*pcgi) {
			/* get the next one first as list may change */
			cgi = *pcgi;
			pcgi = &(*pcgi)->cgi_list;

			if (cgi->pid <= 0)
				continue;

			/* finish sending cached headers */
			if (cgi->headers_buf)
				continue;

			/* wait for stdout to be drained */
			if (cgi->content_length > cgi->content_length_seen)
				continue;

			if (cgi->content_length) {
				lwsl_debug("%s: wsi %p: expected content "
					   "length seen: %lld\n", __func__,
					   cgi->wsi,
				(unsigned long long)cgi->content_length_seen);
			}

			/* reap it */
			waitpid(n, &status, WNOHANG);
			/*
			 * he's already terminated so no need for kill()
			 * but we should do the terminated cgi callback
			 * and close him if he's not already closing
			 */
			if (n == cgi->pid) {
				lwsl_debug("%s: found PID %d on cgi list\n",
					    __func__, n);

				if (!cgi->content_length) {
					/*
					 * well, if he sends chunked...
					 * give him 2s after the
					 * cgi terminated to send buffered
					 */
					cgi->chunked_grace++;
					continue;
				}

				/* defeat kill() */
				cgi->pid = 0;
				lws_cgi_kill(cgi->wsi);

				break;
			}
			cgi = NULL;
		}
		/* if not found on the cgi list, as he's one of ours, reap */
		if (!cgi) {
			lwsl_debug("%s: reading PID %d although no cgi match\n",
					__func__, n);
			waitpid(n, &status, WNOHANG);
		}
	}

	pcgi = &pt->http.cgi_list;

	/* check all the subprocesses on the cgi list */
	while (*pcgi) {
		/* get the next one first as list may change */
		cgi = *pcgi;
		pcgi = &(*pcgi)->cgi_list;

		if (cgi->pid <= 0)
			continue;

		/* we deferred killing him after reaping his PID */
		if (cgi->chunked_grace) {
			cgi->chunked_grace++;
			if (cgi->chunked_grace < 2)
				continue;
			goto finish_him;
		}

		/* finish sending cached headers */
		if (cgi->headers_buf)
			continue;

		/* wait for stdout to be drained */
		if (cgi->content_length > cgi->content_length_seen)
			continue;

		if (cgi->content_length)
			lwsl_debug("%s: wsi %p: expected "
				   "content len seen: %lld\n", __func__,
				   cgi->wsi,
				(unsigned long long)cgi->content_length_seen);

		/* reap it */
		if (waitpid(cgi->pid, &status, WNOHANG) > 0) {

			if (!cgi->content_length) {
				/*
				 * well, if he sends chunked...
				 * give him 2s after the
				 * cgi terminated to send buffered
				 */
				cgi->chunked_grace++;
				continue;
			}
finish_him:
			lwsl_debug("%s: found PID %d on cgi list\n",
				    __func__, cgi->pid);

			/* defeat kill() */
			cgi->pid = 0;
			lws_cgi_kill(cgi->wsi);

			break;
		}
	}

	return 0;
}