/*
 * Find the healthy backend corresponding to the weight r [0...1[
 */
static struct vbc *
vdi_random_pick_one(struct sess *sp, const struct vdi_random *vs, double r)
{
    double w[vs->nhosts];
    int i;
    double s1;

    assert(r >= 0.0 && r < 1.0);

    memset(w, 0, sizeof w);
    /* Sum up the weights of healty backends */
    s1 = 0.0;
    for (i = 0; i < vs->nhosts; i++) {
        if (VDI_Healthy(vs->hosts[i].backend, sp))
            w[i] = vs->hosts[i].weight;
        s1 += w[i];
    }

    if (s1 == 0.0)
        return (NULL);

    r *= s1;
    s1 = 0.0;
    for (i = 0; i < vs->nhosts; i++)  {
        s1 += w[i];
        if (r < s1)
            return(VDI_GetFd(vs->hosts[i].backend, sp));
    }
    return (NULL);
}
예제 #2
0
static struct vbc *
vdi_round_robin_getfd(const struct director *d, struct sess *sp)
{
	int i;
	struct vdi_round_robin *vs;
	struct director *backend;
	struct vbc *vbe;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
	CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC);

	/*
	 * In fallback mode we ignore the next_host and always grab the
	 * first healthy backend we can find.
	 */
	for (i = 0; i < vs->nhosts; i++) {
		if (vs->mode == m_round_robin) {
			backend = vs->hosts[vs->next_host].backend;
			vs->next_host = (vs->next_host + 1) % vs->nhosts;
		} else /* m_fallback */ {
			backend = vs->hosts[i].backend;
		}
		if (!VDI_Healthy(backend, sp))
			continue;
		vbe = VDI_GetFd(backend, sp);
		if (vbe != NULL)
			return (vbe);
	}

	return (NULL);
}
예제 #3
0
vdi_simple_gethdrs(const struct director *d, struct worker *wrk,
    struct busyobj *bo)
{
	int i;

	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
	CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);

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

	i = V1F_fetch_hdr(wrk, bo);
	/*
	 * If we recycle a backend connection, there is a finite chance
	 * that the backend closed it before we get a request to it.
	 * Do a single retry in that case.
	 */
	if (i == 1) {
		AZ(bo->vbc);
		VSC_C_main->backend_retry++;
		bo->vbc = VDI_GetFd(d, wrk, bo);
		if (bo->vbc == NULL) {
			VSLb(bo->vsl, SLT_FetchError, "no backend connection");
			return (-1);
		}
		i = V1F_fetch_hdr(wrk, bo);
	}
	if (i != 0)
		AZ(bo->vbc);
	else
		AN(bo->vbc);
	return (i);
}
static struct vbc *
vdi_round_robin_getfd(const struct director *d, struct sess *sp)
{
	int i;
	struct vdi_round_robin *vs;
	struct director *backend;
	struct vbc *vbe;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
	CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC);

	for (i = 0; i < vs->nhosts; i++) {
		backend = vs->hosts[vs->next_host].backend;
		vs->next_host = (vs->next_host + 1) % vs->nhosts;
		if (!VDI_Healthy(backend, sp))
			continue;
		vbe = VDI_GetFd(backend, sp);
		if (vbe != NULL)
			return (vbe);
	}

	return (NULL);
}
예제 #5
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);
}
예제 #6
0
int
FetchHdr(struct sess *sp, int need_host_hdr)
{
	struct vbc *vc;
	struct worker *wrk;
	struct http *hp;
	int retry = -1;
	int i;
	struct http_conn *htc;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
	wrk = sp->wrk;
	CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC);
	htc = &wrk->busyobj->htc;

	AN(sp->req->director);
	AZ(sp->req->obj);

	if (sp->req->objcore != NULL) {		/* pass has no objcore */
		CHECK_OBJ_NOTNULL(sp->req->objcore, OBJCORE_MAGIC);
		AN(sp->req->objcore->flags & OC_F_BUSY);
	}

	hp = wrk->busyobj->bereq;

	sp->wrk->busyobj->vbc = VDI_GetFd(NULL, sp);
	if (sp->wrk->busyobj->vbc == NULL) {
		WSP(sp, SLT_FetchError, "no backend connection");
		return (-1);
	}
	vc = sp->wrk->busyobj->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 (need_host_hdr)
		VDI_AddHostHeader(sp->wrk, vc);

	(void)VTCP_blocking(vc->fd);	/* XXX: we should timeout instead */
	WRW_Reserve(wrk, &vc->fd);
	(void)http_Write(wrk, vc->vsl_id, hp, 0);	/* XXX: stats ? */

	/* Deal with any message-body the request might have */
	i = FetchReqBody(sp);
	if (WRW_FlushRelease(wrk) || i > 0) {
		WSP(sp, SLT_FetchError, "backend write error: %d (%s)",
		    errno, strerror(errno));
		VDI_CloseFd(sp->wrk, &sp->wrk->busyobj->vbc);
		/* XXX: other cleanup ? */
		return (retry);
	}

	/* Checkpoint the vsl.here */
	WSL_Flush(wrk, 0);

	/* XXX is this the right place? */
	VSC_C_main->backend_req++;

	/* Receive response */

	HTC_Init(htc, wrk->ws, vc->fd, vc->vsl_id,
	    cache_param->http_resp_size,
	    cache_param->http_resp_hdr_len);

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

	i = HTC_Rx(htc);

	if (i < 0) {
		WSP(sp, SLT_FetchError, "http first read error: %d %d (%s)",
		    i, errno, strerror(errno));
		VDI_CloseFd(sp->wrk, &sp->wrk->busyobj->vbc);
		/* XXX: other cleanup ? */
		/* Retryable if we never received anything */
		return (i == -1 ? retry : -1);
	}

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

	while (i == 0) {
		i = HTC_Rx(htc);
		if (i < 0) {
			WSP(sp, SLT_FetchError,
			    "http first read error: %d %d (%s)",
			    i, errno, strerror(errno));
			VDI_CloseFd(sp->wrk, &sp->wrk->busyobj->vbc);
			/* XXX: other cleanup ? */
			return (-1);
		}
	}

	hp = wrk->busyobj->beresp;

	if (http_DissectResponse(wrk, htc, hp)) {
		WSP(sp, SLT_FetchError, "http format error");
		VDI_CloseFd(sp->wrk, &sp->wrk->busyobj->vbc);
		/* XXX: other cleanup ? */
		return (-1);
	}
	return (0);
}
예제 #7
0
void
PipeSession(struct sess *sp)
{
	struct vbc *vc;
	struct worker *w;
	struct pollfd fds[2];
	int i;

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

	sp->vbc = VDI_GetFd(NULL, sp);
	if (sp->vbc == NULL)
		return;
	vc = sp->vbc;
	(void)VTCP_blocking(vc->fd);

	WRW_Reserve(w, &vc->fd);
	sp->wrk->acct_tmp.hdrbytes +=
	    http_Write(w, sp->vsl_id, sp->wrk->bereq, 0);

	if (sp->htc->pipeline.b != NULL)
		sp->wrk->acct_tmp.bodybytes +=
		    WRW_Write(w, sp->htc->pipeline.b, Tlen(sp->htc->pipeline));

	i = WRW_FlushRelease(w);

	if (i) {
		SES_Close(sp, "pipe");
		VDI_CloseFd(sp);
		return;
	}

	sp->t_resp = VTIM_real();

	memset(fds, 0, sizeof fds);

	// XXX: not yet (void)VTCP_linger(vc->fd, 0);
	fds[0].fd = vc->fd;
	fds[0].events = POLLIN | POLLERR;

	// XXX: not yet (void)VTCP_linger(sp->fd, 0);
	fds[1].fd = sp->fd;
	fds[1].events = POLLIN | POLLERR;

	while (fds[0].fd > -1 || fds[1].fd > -1) {
		fds[0].revents = 0;
		fds[1].revents = 0;
		i = poll(fds, 2, params->pipe_timeout * 1000);
		if (i < 1)
			break;
		if (fds[0].revents && rdf(vc->fd, sp->fd)) {
			if (fds[1].fd == -1)
				break;
			(void)shutdown(vc->fd, SHUT_RD);
			(void)shutdown(sp->fd, SHUT_WR);
			fds[0].events = 0;
			fds[0].fd = -1;
		}
		if (fds[1].revents && rdf(sp->fd, vc->fd)) {
			if (fds[0].fd == -1)
				break;
			(void)shutdown(sp->fd, SHUT_RD);
			(void)shutdown(vc->fd, SHUT_WR);
			fds[1].events = 0;
			fds[1].fd = -1;
		}
	}
	SES_Close(sp, "pipe");
	VDI_CloseFd(sp);
}
예제 #8
0
void
PipeRequest(struct req *req)
{
	struct vbc *vc;
	struct worker *wrk;
	struct pollfd fds[2];
	struct busyobj *bo;
	int i;

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

	vc = VDI_GetFd(NULL, bo);
	if (vc == NULL)
		return;
	bo->vbc = vc;		/* For panic dumping */
	(void)VTCP_blocking(vc->fd);

	WRW_Reserve(wrk, &vc->fd, bo->vsl, req->t_req);
	req->acct_req.hdrbytes += HTTP1_Write(wrk, bo->bereq, 0);

	if (req->htc->pipeline.b != NULL)
		req->acct_req.bodybytes +=
		    WRW_Write(wrk, req->htc->pipeline.b,
		    Tlen(req->htc->pipeline));

	i = WRW_FlushRelease(wrk);

	if (i) {
		SES_Close(req->sp, SC_TX_PIPE);
		VDI_CloseFd(&vc);
		return;
	}

	req->t_resp = VTIM_real();

	memset(fds, 0, sizeof fds);

	// XXX: not yet (void)VTCP_linger(vc->fd, 0);
	fds[0].fd = vc->fd;
	fds[0].events = POLLIN | POLLERR;

	// XXX: not yet (void)VTCP_linger(req->sp->fd, 0);
	fds[1].fd = req->sp->fd;
	fds[1].events = POLLIN | POLLERR;

	while (fds[0].fd > -1 || fds[1].fd > -1) {
		fds[0].revents = 0;
		fds[1].revents = 0;
		i = poll(fds, 2, cache_param->pipe_timeout * 1000);
		if (i < 1)
			break;
		if (fds[0].revents && rdf(vc->fd, req->sp->fd)) {
			if (fds[1].fd == -1)
				break;
			(void)shutdown(vc->fd, SHUT_RD);
			(void)shutdown(req->sp->fd, SHUT_WR);
			fds[0].events = 0;
			fds[0].fd = -1;
		}
		if (fds[1].revents && rdf(req->sp->fd, vc->fd)) {
			if (fds[0].fd == -1)
				break;
			(void)shutdown(req->sp->fd, SHUT_RD);
			(void)shutdown(vc->fd, SHUT_WR);
			fds[1].events = 0;
			fds[1].fd = -1;
		}
	}
	SES_Close(req->sp, SC_TX_PIPE);
	VDI_CloseFd(&vc);
	bo->vbc = NULL;
}