示例#1
0
V1D_Deliver(struct req *req, struct boc *boc, int sendbody)
{
	int err = 0;

	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	CHECK_OBJ_ORNULL(boc, BOC_MAGIC);
	CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);

	if (sendbody) {
		if (http_GetHdr(req->resp, H_Content_Length, NULL))
			req->res_mode |= RES_LEN;
		else if (req->http->protover == 11) {
			req->res_mode |= RES_CHUNKED;
			http_SetHeader(req->resp, "Transfer-Encoding: chunked");
		} else {
			req->res_mode |= RES_EOF;
			req->doclose = SC_TX_EOF;
		}
	}

	VSLb(req->vsl, SLT_Debug, "RES_MODE %x", req->res_mode);

	if (req->doclose) {
		if (!http_HdrIs(req->resp, H_Connection, "close")) {
			http_Unset(req->resp, H_Connection);
			http_SetHeader(req->resp, "Connection: close");
		}
	} else if (!http_GetHdr(req->resp, H_Connection, NULL))
		http_SetHeader(req->resp, "Connection: keep-alive");

	if (sendbody && req->resp_len != 0)
		VDP_push(req, v1d_bytes, NULL, 1);

	AZ(req->wrk->v1l);
	V1L_Reserve(req->wrk, req->ws, &req->sp->fd, req->vsl, req->t_prev);

	if (WS_Overflowed(req->ws)) {
		v1d_error(req, "workspace_client overflow");
		AZ(req->wrk->v1l);
		return;
	}

	req->acct.resp_hdrbytes += HTTP1_Write(req->wrk, req->resp, HTTP1_Resp);
	if (DO_DEBUG(DBG_FLUSH_HEAD))
		(void)V1L_Flush(req->wrk);

	if (sendbody && req->resp_len != 0) {
		if (req->res_mode & RES_CHUNKED)
			V1L_Chunked(req->wrk);
		err = VDP_DeliverObj(req);
		if (!err && (req->res_mode & RES_CHUNKED))
			V1L_EndChunk(req->wrk);
	}

	if ((V1L_FlushRelease(req->wrk) || err) && req->sp->fd >= 0)
		SES_Close(req->sp, SC_REM_CLOSE);
	AZ(req->wrk->v1l);
	VDP_close(req);
}
int
V1F_SendReq(struct worker *wrk, struct busyobj *bo, uint64_t *ctr,
    int onlycached)
{
	struct http *hp;
	int j;
	ssize_t i;
	struct http_conn *htc;
	int do_chunked = 0;

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

	htc = bo->htc;
	hp = bo->bereq;

	if (bo->req != NULL &&
	    bo->req->req_body_status == REQ_BODY_WITHOUT_LEN) {
		http_PrintfHeader(hp, "Transfer-Encoding: chunked");
		do_chunked = 1;
	}

	(void)VTCP_blocking(htc->fd);	/* XXX: we should timeout instead */
	V1L_Reserve(wrk, wrk->aws, &htc->fd, bo->vsl, bo->t_prev);
	*ctr += HTTP1_Write(wrk, hp, HTTP1_Req);

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

	if (bo->req != NULL &&
	    (bo->req->req_body_status == REQ_BODY_CACHED || !onlycached)) {
		if (do_chunked)
			V1L_Chunked(wrk);
		i = VRB_Iterate(bo->req, vbf_iter_req_body, bo);

		if (bo->req->req_body_status == REQ_BODY_FAIL) {
			assert(i < 0);
			VSLb(bo->vsl, SLT_FetchError,
			    "req.body read error: %d (%s)",
			    errno, strerror(errno));
			bo->req->doclose = SC_RX_BODY;
		}
		if (do_chunked)
			V1L_EndChunk(wrk);
	}

	j = V1L_FlushRelease(wrk);
	if (j != 0 || i < 0) {
		VSLb(bo->vsl, SLT_FetchError, "backend write error: %d (%s)",
		    errno, strerror(errno));
		VSLb_ts_busyobj(bo, "Bereq", W_TIM_real(wrk));
		htc->doclose = SC_TX_ERROR;
		return (-1);
	}
	VSLb_ts_busyobj(bo, "Bereq", W_TIM_real(wrk));
	return (0);
}
示例#3
0
int
V1F_SendReq(struct worker *wrk, struct busyobj *bo, uint64_t *ctr_hdrbytes,
    uint64_t *ctr_bodybytes, int onlycached, char *abuf, char *pbuf)
{
	struct http *hp;
	int j;
	ssize_t i;
	uint64_t bytes, hdrbytes;
	struct http_conn *htc;
	int do_chunked = 0;

	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
	CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
	CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC);
	CHECK_OBJ_ORNULL(bo->req, REQ_MAGIC);
	AN(ctr_hdrbytes);
	AN(ctr_bodybytes);

	htc = bo->htc;
	assert(*htc->rfd > 0);
	hp = bo->bereq;

	if (bo->req != NULL &&
	    bo->req->req_body_status == REQ_BODY_WITHOUT_LEN) {
		http_PrintfHeader(hp, "Transfer-Encoding: chunked");
		do_chunked = 1;
	}

	VSLb(bo->vsl, SLT_BackendStart, "%s %s", abuf, pbuf);

	(void)VTCP_blocking(*htc->rfd);	/* XXX: we should timeout instead */
	V1L_Open(wrk, wrk->aws, htc->rfd, bo->vsl, bo->t_prev, 0);
	hdrbytes = HTTP1_Write(wrk, hp, HTTP1_Req);

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

	if (bo->req != NULL &&
	    (bo->req->req_body_status == REQ_BODY_CACHED || !onlycached)) {
		if (do_chunked)
			V1L_Chunked(wrk);
		i = VRB_Iterate(bo->req, vbf_iter_req_body, bo);

		if (bo->req->req_body_status == REQ_BODY_FAIL) {
			assert(i < 0);
			VSLb(bo->vsl, SLT_FetchError,
			    "req.body read error: %d (%s)",
			    errno, vstrerror(errno));
			bo->req->doclose = SC_RX_BODY;
		}
		if (do_chunked)
			V1L_EndChunk(wrk);
	}

	j = V1L_Close(wrk, &bytes);

	/* Bytes accounting */
	if (bytes < hdrbytes)
		*ctr_hdrbytes += bytes;
	else {
		*ctr_hdrbytes += hdrbytes;
		*ctr_bodybytes += bytes - hdrbytes;
	}

	if (j != 0 || i < 0) {
		VSLb(bo->vsl, SLT_FetchError, "backend write error: %d (%s)",
		    errno, vstrerror(errno));
		VSLb_ts_busyobj(bo, "Bereq", W_TIM_real(wrk));
		htc->doclose = SC_TX_ERROR;
		return (-1);
	}
	VSLb_ts_busyobj(bo, "Bereq", W_TIM_real(wrk));
	return (0);
}
void
V1D_Deliver(struct req *req, struct busyobj *bo)
{
	const char *r;
	enum objiter_status ois;

	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);

	req->res_mode = 0;

	/*
	 * Determine ESI status first.  Not dependent on wantbody, because
	 * we want ESI to supress C-L in HEAD too.
	 */
	if (!req->disable_esi &&
	    ObjGetattr(req->wrk, req->objcore, OA_ESIDATA, NULL) != NULL)
		req->res_mode |= RES_ESI;

	/*
	 * ESI-childen don't care about headers -> early escape
	 */
	if (req->esi_level > 0) {
		ESI_DeliverChild(req, bo);
		return;
	}

	if (req->res_mode & RES_ESI) {
		RFC2616_Weaken_Etag(req->resp);
	} else if (http_IsStatus(req->resp, 304)) {
		http_Unset(req->resp, H_Content_Length);
		req->wantbody = 0;
	} else if (bo == NULL &&
	    !http_GetHdr(req->resp, H_Content_Length, NULL)) {
		http_PrintfHeader(req->resp,
		    "Content-Length: %ju", (uintmax_t)ObjGetLen(
		    req->wrk, req->objcore));
	}

	if (cache_param->http_gzip_support &&
	    ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) &&
	    !RFC2616_Req_Gzip(req->http)) {
		/*
		 * We don't know what it uncompresses to
		 * XXX: we could cache that, but would still deliver
		 * XXX: with multiple writes because of the gunzip buffer
		 */
		req->res_mode |= RES_GUNZIP;
		VDP_push(req, VDP_gunzip, NULL, 0);
	}

	if (req->res_mode & RES_ESI) {
		/* Gunzip could have added back a C-L */
		http_Unset(req->resp, H_Content_Length);
	}

	/*
	 * Range comes after the others and pushes on bottom because it
	 * can generate a correct C-L header.
	 */
	if (cache_param->http_range_support && http_IsStatus(req->resp, 200)) {
		http_SetHeader(req->resp, "Accept-Ranges: bytes");
		if (req->wantbody && http_GetHdr(req->http, H_Range, &r))
			VRG_dorange(req, bo, r);
	}


	if (http_GetHdr(req->resp, H_Content_Length, NULL))
		req->res_mode |= RES_LEN;

	if (req->wantbody && !(req->res_mode & RES_LEN)) {
		if (req->http->protover >= 11) {
			req->res_mode |= RES_CHUNKED;
			http_SetHeader(req->resp, "Transfer-Encoding: chunked");
		} else {
			req->res_mode |= RES_EOF;
			req->doclose = SC_TX_EOF;
		}
	}

	VSLb(req->vsl, SLT_Debug, "RES_MODE %x", req->res_mode);

	if (req->doclose) {
		if (!http_HdrIs(req->resp, H_Connection, "close")) {
			http_Unset(req->resp, H_Connection);
			http_SetHeader(req->resp, "Connection: close");
		}
	} else if (!http_GetHdr(req->resp, H_Connection, NULL))
		http_SetHeader(req->resp, "Connection: keep-alive");

	VDP_push(req, v1d_bytes, NULL, 1);

	V1L_Reserve(req->wrk, req->ws, &req->sp->fd, req->vsl, req->t_prev);

	req->acct.resp_hdrbytes += HTTP1_Write(req->wrk, req->resp, HTTP1_Resp);
	if (DO_DEBUG(DBG_FLUSH_HEAD))
		(void)V1L_Flush(req->wrk);

	ois = OIS_DONE;
	if (req->wantbody) {
		if (req->res_mode & RES_CHUNKED)
			V1L_Chunked(req->wrk);

		ois = VDP_DeliverObj(req);
		(void)VDP_bytes(req, VDP_FLUSH, NULL, 0);

		if (ois == OIS_DONE && (req->res_mode & RES_CHUNKED))
			V1L_EndChunk(req->wrk);
	}

	if ((V1L_FlushRelease(req->wrk) || ois != OIS_DONE) && req->sp->fd >= 0)
		SES_Close(req->sp, SC_REM_CLOSE);
	VDP_close(req);
}
示例#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);
}
void
V1P_Process(struct req *req, struct busyobj *bo, int fd)
{
	struct worker *wrk;
	struct pollfd fds[2];
	int i;
	struct acct_pipe acct_pipe;

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

	req->res_mode = RES_PIPE;

	memset(&acct_pipe, 0, sizeof acct_pipe);
	acct_pipe.req = req->acct.req_hdrbytes;
	req->acct.req_hdrbytes = 0;

	CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC);
	CHECK_OBJ_NOTNULL(bo->htc->vbc, VBC_MAGIC);
	bo->wrk = req->wrk;
	bo->director_state = DIR_S_BODY;
	(void)VTCP_blocking(fd);

	V1L_Reserve(wrk, wrk->aws, &fd, bo->vsl, req->t_req);
	acct_pipe.bereq += HTTP1_Write(wrk, bo->bereq, HTTP1_Req);

	if (req->htc->pipeline_b != NULL)
		acct_pipe.in += V1L_Write(wrk, req->htc->pipeline_b,
		    req->htc->pipeline_e - req->htc->pipeline_b);

	i = V1L_FlushRelease(wrk);

	VSLb_ts_req(req, "Pipe", W_TIM_real(wrk));

	if (i == 0) {
		if (bo->htc->vbc->state == VBC_STATE_STOLEN)
			VBT_Wait(req->wrk, bo->htc->vbc);

		memset(fds, 0, sizeof fds);
		fds[0].fd = fd;
		fds[0].events = POLLIN | POLLERR;
		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,
			    (int)(cache_param->pipe_timeout * 1e3));
			if (i < 1)
				break;
			if (fds[0].revents &&
			    rdf(fd, req->sp->fd, &acct_pipe.out)) {
				if (fds[1].fd == -1)
					break;
				(void)shutdown(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, fd, &acct_pipe.in)) {
				if (fds[0].fd == -1)
					break;
				(void)shutdown(req->sp->fd, SHUT_RD);
				(void)shutdown(fd, SHUT_WR);
				fds[1].events = 0;
				fds[1].fd = -1;
			}
		}
	}
	VSLb_ts_req(req, "PipeSess", W_TIM_real(wrk));
	pipecharge(req, &acct_pipe, bo->htc->vbc->backend->vsc);
	SES_Close(req->sp, SC_TX_PIPE);
	bo->doclose = SC_TX_PIPE;
}
void
V1D_Deliver(struct req *req)
{
	char *r;

	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC);
	CHECK_OBJ_NOTNULL(req->obj->objcore, OBJCORE_MAGIC);

	req->res_mode = 0;

	if (!req->disable_esi && req->obj->esidata != NULL) {
		/* In ESI mode, we can't know the aggregate length */
		req->res_mode &= ~RES_LEN;
		req->res_mode |= RES_ESI;
	} else if (req->resp->status == 304) {
		req->res_mode &= ~RES_LEN;
		http_Unset(req->resp, H_Content_Length);
		req->wantbody = 0;
	} else if (req->obj->objcore->busyobj == NULL) {
		/* XXX: Not happy with this convoluted test */
		req->res_mode |= RES_LEN;
		if (!(req->obj->objcore->flags & OC_F_PASS) ||
		    req->obj->len != 0) {
			http_Unset(req->resp, H_Content_Length);
			http_PrintfHeader(req->resp,
			    "Content-Length: %zd", req->obj->len);
		}
	}

	if (req->esi_level > 0) {
		/* Included ESI object, always CHUNKED or EOF */
		req->res_mode &= ~RES_LEN;
		req->res_mode |= RES_ESI_CHILD;
	}

	if (cache_param->http_gzip_support && req->obj->gziped &&
	    !RFC2616_Req_Gzip(req->http)) {
		/*
		 * We don't know what it uncompresses to
		 * XXX: we could cache that, but would still deliver
		 * XXX: with multiple writes because of the gunzip buffer
		 */
		req->res_mode &= ~RES_LEN;
		req->res_mode |= RES_GUNZIP;
	}

	if (!(req->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
		/* We havn't chosen yet, do so */
		if (!req->wantbody) {
			/* Nothing */
		} else if (req->http->protover >= 11) {
			req->res_mode |= RES_CHUNKED;
		} else {
			req->res_mode |= RES_EOF;
			req->doclose = SC_TX_EOF;
		}
	}
	VSLb(req->vsl, SLT_Debug, "RES_MODE %x", req->res_mode);

	if (!(req->res_mode & RES_LEN))
		http_Unset(req->resp, H_Content_Length);

	if (req->res_mode & RES_GUNZIP)
		http_Unset(req->resp, H_Content_Encoding);

	if (req->res_mode & RES_CHUNKED)
		http_SetHeader(req->resp, "Transfer-Encoding: chunked");

	http_SetHeader(req->resp,
	    req->doclose ? "Connection: close" : "Connection: keep-alive");

	req->vdps[0] = v1d_bytes;
	req->vdp_nxt = 0;

	if (
	    req->wantbody &&
	    !(req->res_mode & (RES_ESI|RES_ESI_CHILD)) &&
	    cache_param->http_range_support &&
	    req->obj->response == 200) {
		http_SetHeader(req->resp, "Accept-Ranges: bytes");
		if (http_GetHdr(req->http, H_Range, &r))
			v1d_dorange(req, r);
	}

	if (req->res_mode & RES_ESI)
		RFC2616_Weaken_Etag(req->resp);

	WRW_Reserve(req->wrk, &req->sp->fd, req->vsl, req->t_resp);

	/*
	 * Send HTTP protocol header, unless interior ESI object
	 */
	if (!(req->res_mode & RES_ESI_CHILD))
		req->acct_req.hdrbytes += HTTP1_Write(req->wrk, req->resp, 1);

	if (req->res_mode & RES_CHUNKED)
		WRW_Chunked(req->wrk);

	if (!req->wantbody) {
		/* This was a HEAD or conditional request */
	} else if (req->res_mode & RES_ESI) {
		AZ(req->obj->objcore->busyobj);
		ESI_Deliver(req);
	} else if (req->res_mode & RES_ESI_CHILD && req->gzip_resp) {
		while (req->obj->objcore->busyobj)
			(void)usleep(10000);
		ESI_DeliverChild(req);
	} else if (req->res_mode & RES_GUNZIP ||
	    (req->res_mode & RES_ESI_CHILD &&
	    !req->gzip_resp && req->obj->gziped)) {
		VDP_push(req, VDP_gunzip);
		req->vgz = VGZ_NewUngzip(req->vsl, "U D -");
		AZ(VGZ_WrwInit(req->vgz));
		v1d_WriteDirObj(req);
		(void)VGZ_Destroy(&req->vgz);
		VDP_pop(req, VDP_gunzip);
	} else {
		v1d_WriteDirObj(req);
	}

	if (req->res_mode & RES_CHUNKED && !(req->res_mode & RES_ESI_CHILD))
		WRW_EndChunk(req->wrk);

	if (WRW_FlushRelease(req->wrk) && req->sp->fd >= 0)
		SES_Close(req->sp, SC_REM_CLOSE);
}
示例#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;
}
int
V1F_fetch_hdr(struct worker *wrk, struct busyobj *bo, const char *def_host)
{
	struct http *hp;
	enum http1_status_e hs;
	int retry = 1;
	int j, first;
	ssize_t i;
	struct http_conn *htc;
	int do_chunked = 0;

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

	htc = bo->htc;
	hp = bo->bereq;

	/*
	 * 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) && def_host != NULL)
		http_PrintfHeader(hp, "Host: %s", def_host);

	if (bo->req != NULL &&
	    bo->req->req_body_status == REQ_BODY_WITHOUT_LEN) {
		http_PrintfHeader(hp, "Transfer-Encoding: chunked");
		do_chunked = 1;
	}

	(void)VTCP_blocking(htc->fd);	/* XXX: we should timeout instead */
	V1L_Reserve(wrk, wrk->aws, &htc->fd, bo->vsl, bo->t_prev);
	bo->acct.bereq_hdrbytes = HTTP1_Write(wrk, hp, HTTP1_Req);

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

	if (bo->req != NULL) {
		if (do_chunked)
			V1L_Chunked(wrk);
		i = VRB_Iterate(bo->req, vbf_iter_req_body, bo);

		if (bo->req->req_body_status == REQ_BODY_TAKEN) {
			retry = -1;
		} else if (bo->req->req_body_status == REQ_BODY_FAIL) {
			VSLb(bo->vsl, SLT_FetchError,
			    "req.body read error: %d (%s)",
			    errno, strerror(errno));
			bo->req->doclose = SC_RX_BODY;
			retry = -1;
		}
		if (do_chunked)
			V1L_EndChunk(wrk);
	}

	j = V1L_FlushRelease(wrk);
	if (j != 0 || i < 0) {
		VSLb(bo->vsl, SLT_FetchError, "backend write error: %d (%s)",
		    errno, strerror(errno));
		VSLb_ts_busyobj(bo, "Bereq", W_TIM_real(wrk));
		bo->doclose = SC_TX_ERROR;
		return (retry);
	}
	VSLb_ts_busyobj(bo, "Bereq", W_TIM_real(wrk));

	VSC_C_main->backend_req++;

	/* Receive response */

	HTTP1_RxInit(htc, bo->ws, cache_param->http_resp_size,
	    cache_param->http_resp_hdr_len);
	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC);

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

	first = 1;
	do {
		hs = HTTP1_Rx(htc);
		if (hs == HTTP1_OVERFLOW) {
			bo->acct.beresp_hdrbytes +=
			    htc->rxbuf_e - htc->rxbuf_b;
			VSLb(bo->vsl, SLT_FetchError,
			    "http %sread error: overflow",
			    first ? "first " : "");
			bo->doclose = SC_RX_OVERFLOW;
			return (-1);
		}
		if (hs == HTTP1_ERROR_EOF) {
			bo->acct.beresp_hdrbytes +=
			    htc->rxbuf_e - htc->rxbuf_b;
			VSLb(bo->vsl, SLT_FetchError, "http %sread error: EOF",
			    first ? "first " : "");
			bo->doclose = SC_RX_TIMEOUT;
			return (retry);
		}
		if (first) {
			retry = -1;
			first = 0;
			VTCP_set_read_timeout(htc->fd,
			    htc->between_bytes_timeout);
		}
	} while (hs != HTTP1_COMPLETE);
	bo->acct.beresp_hdrbytes += htc->rxbuf_e - htc->rxbuf_b;

	hp = bo->beresp;

	if (HTTP1_DissectResponse(hp, htc)) {
		VSLb(bo->vsl, SLT_FetchError, "http format error");
		bo->doclose = SC_RX_JUNK;
		return (-1);
	}

	bo->doclose = http_DoConnection(hp);

	return (0);
}