static void *
server_bgthread(struct worker *wrk, void *priv)
{
	struct vmod_fsdirector_file_system *fs;
	struct sockaddr_storage addr_s;
	socklen_t len;
	struct http_conn *htc;
	int fd;
	enum htc_status_e htc_status;

	CAST_OBJ_NOTNULL(fs, priv, VMOD_FSDIRECTOR_MAGIC);
	assert(fs->sock >= 0);

	htc = &fs->htc;
	fs->wrk = wrk;
	WS_Init(wrk->aws, fs->ws_name, malloc(WS_LEN), WS_LEN);

	while (1) {
		do {
			fd = accept(fs->sock, (void*)&addr_s, &len);
		} while (fd < 0 && errno == EAGAIN);

		if (fd < 0) {
			continue;
		}

		HTTP1_Init(htc, wrk->aws, fd, NULL, HTTP1_BUF, HTTP1_MAX_HDR);

		htc_status = HTTP1_Rx(htc);
		switch (htc_status) {
			case HTTP1_OVERFLOW:
			case HTTP1_ERROR_EOF:
			case HTTP1_ALL_WHITESPACE:
			case HTTP1_NEED_MORE:
				prepare_answer(htc, 400);
				prepare_body(htc);
				break;
			case HTTP1_COMPLETE:
				answer_appropriate(fs);
				break;
		}

		WS_Reset(wrk->aws, NULL);
		close(fd);
	}

	pthread_exit(0);

	NEEDLESS_RETURN(NULL);
}
Example #2
0
int
V1F_fetch_hdr(struct worker *wrk, struct busyobj *bo, struct req *req)
{
	struct vbc *vc;
	struct http *hp;
	enum htc_status_e hs;
	int retry = -1;
	int i, first;
	struct http_conn *htc;

	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
	CHECK_OBJ_ORNULL(req, REQ_MAGIC);
	CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
	htc = &bo->htc;

	AN(bo->director);

	hp = bo->bereq;

	bo->vbc = VDI_GetFd(NULL, bo);
	if (bo->vbc == NULL) {
		VSLb(bo->vsl, SLT_FetchError, "no backend connection");
		return (-1);
	}
	vc = bo->vbc;
	if (vc->recycled)
		retry = 1;

	/*
	 * Now that we know our backend, we can set a default Host:
	 * header if one is necessary.  This cannot be done in the VCL
	 * because the backend may be chosen by a director.
	 */
	if (!http_GetHdr(bo->bereq, H_Host, NULL))
		VDI_AddHostHeader(bo->bereq, vc);

	(void)VTCP_blocking(vc->fd);	/* XXX: we should timeout instead */
	WRW_Reserve(wrk, &vc->fd, bo->vsl, bo->t_fetch);
	bo->t_send = VTIM_mono();
	(void)HTTP1_Write(wrk, hp, 0);	/* XXX: stats ? */

	/* Deal with any message-body the request might (still) have */
	i = 0;

	if (req != NULL) {
		i = HTTP1_IterateReqBody(req, vbf_iter_req_body, wrk);
		if (req->req_body_status == REQ_BODY_DONE)
			retry = -1;
		if (req->req_body_status == REQ_BODY_FAIL) {
			VSLb(bo->vsl, SLT_FetchError,
			    "req.body read error: %d (%s)",
			    errno, strerror(errno));
			req->doclose = SC_RX_BODY;
			retry = -1;
		}
	}

	if (WRW_FlushRelease(wrk) || i != 0) {
		VSLb(bo->vsl, SLT_FetchError, "backend write error: %d (%s)",
		    errno, strerror(errno));
		VDI_CloseFd(&bo->vbc);
		/* XXX: other cleanup ? */
		return (retry);
	}

	VSC_C_main->backend_req++;
	bo->t_sent = VTIM_mono();

	/* Receive response */

	HTTP1_Init(htc, bo->ws, vc->fd, vc->vsl,
	    cache_param->http_resp_size,
	    cache_param->http_resp_hdr_len);

	VTCP_set_read_timeout(vc->fd, vc->first_byte_timeout);

	first = 1;
	do {
		hs = HTTP1_Rx(htc);
		if (hs == HTTP1_OVERFLOW) {
			VSLb(bo->vsl, SLT_FetchError,
			    "http %sread error: overflow",
			    first ? "first " : "");
			VDI_CloseFd(&bo->vbc);
			/* XXX: other cleanup ? */
			return (-1);
		}
		if (hs == HTTP1_ERROR_EOF) {
			VSLb(bo->vsl, SLT_FetchError, "http %sread error: EOF",
			    first ? "first " : "");
			VDI_CloseFd(&bo->vbc);
			/* XXX: other cleanup ? */
			return (retry);
		}
		if (first) {
			retry = -1;
			first = 0;
			VTCP_set_read_timeout(vc->fd,
			    vc->between_bytes_timeout);
		}
	} while (hs != HTTP1_COMPLETE);
	bo->t_hdr = VTIM_mono();

	hp = bo->beresp;

	if (HTTP1_DissectResponse(hp, htc)) {
		VSLb(bo->vsl, SLT_FetchError, "http format error");
		VDI_CloseFd(&bo->vbc);
		/* XXX: other cleanup ? */
		return (-1);
	}
	return (0);
}
void
HTTP1_Session(struct worker *wrk, struct req *req)
{
	enum req_fsm_nxt nxt = REQ_FSM_MORE;
	struct sess *sp;
	enum http1_cleanup_ret sdr;

	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	sp = req->sp;
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);

	/*
	 * Whenever we come in from the acceptor or waiter, we need to set
	 * blocking mode.  It would be simpler to do this in the acceptor
	 * or waiter, but we'd rather do the syscall in the worker thread.
	 * On systems which return errors for ioctl, we close early
	 */
	if (sp->sess_step == S_STP_NEWREQ && VTCP_blocking(sp->fd)) {
		if (errno == ECONNRESET)
			SES_Close(sp, SC_REM_CLOSE);
		else
			SES_Close(sp, SC_TX_ERROR);
		sdr = http1_cleanup(sp, wrk, req);
		assert(sdr == SESS_DONE_RET_GONE);
		return;
	}

	/*
	 * Return from waitinglist. Check to see if the remote has left.
	 */
	if (req->req_step == R_STP_LOOKUP && VTCP_check_hup(sp->fd)) {
		AN(req->hash_objhead);
		(void)HSH_DerefObjHead(wrk, &req->hash_objhead);
		AZ(req->hash_objhead);
		SES_Close(sp, SC_REM_CLOSE);
		sdr = http1_cleanup(sp, wrk, req);
		assert(sdr == SESS_DONE_RET_GONE);
		return;
	}

	if (sp->sess_step == S_STP_NEWREQ) {
		HTTP1_Init(req->htc, req->ws, sp->fd, req->vsl,
		    cache_param->http_req_size,
		    cache_param->http_req_hdr_len);
	}

	while (1) {
		assert(
		    sp->sess_step == S_STP_NEWREQ ||
		    req->req_step == R_STP_LOOKUP ||
		    req->req_step == R_STP_RECV);

		if (sp->sess_step == S_STP_WORKING) {
			if (req->req_step == R_STP_RECV)
				nxt = http1_dissect(wrk, req);
			if (nxt == REQ_FSM_MORE)
				nxt = CNT_Request(wrk, req);
			if (nxt == REQ_FSM_DISEMBARK)
				return;
			assert(nxt == REQ_FSM_DONE);
			sdr = http1_cleanup(sp, wrk, req);
			switch (sdr) {
			case SESS_DONE_RET_GONE:
				return;
			case SESS_DONE_RET_WAIT:
				sp->sess_step = S_STP_NEWREQ;
				break;
			case SESS_DONE_RET_START:
				sp->sess_step = S_STP_WORKING;
				req->req_step = R_STP_RECV;
				break;
			default:
				WRONG("Illegal enum http1_cleanup_ret");
			}
		}

		if (sp->sess_step == S_STP_NEWREQ) {
			nxt = http1_wait(sp, wrk, req);
			if (nxt != REQ_FSM_MORE)
				return;
			sp->sess_step = S_STP_WORKING;
			req->req_step = R_STP_RECV;
		}
	}
}