static void
cnt_vdp(struct req *req, struct busyobj *bo)
{
	const char *r;
	uint16_t status;
	int wantbody;

	CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
	req->res_mode = 0;
	wantbody = 1;
	status = http_GetStatus(req->resp);
	if (!strcmp(req->http0->hd[HTTP_HDR_METHOD].b, "HEAD")) {
		wantbody = 0;
	} else if (status < 200 || status == 204) {
		req->resp_len = 0;
		http_Unset(req->resp, H_Content_Length);
		wantbody = 0;
	} else if (status == 304) {
		http_Unset(req->resp, H_Content_Length);
		wantbody = 0;
	} else if (bo != NULL)
		req->resp_len = http_GetContentLength(req->resp);
	else
		req->resp_len = ObjGetLen(req->wrk, req->objcore);

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

	if (cache_param->http_gzip_support &&
	    ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) &&
	    !RFC2616_Req_Gzip(req->http)) {
		req->res_mode |= RES_GUNZIP;
		VDP_push(req, VDP_gunzip, NULL, 1);
	}

	/*
	 * Range comes after the others and pushes on bottom because
	 * it can (maybe) 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 (wantbody && http_GetHdr(req->http, H_Range, &r))
			VRG_dorange(req, r);
	}

	req->transport->deliver(req, bo, wantbody);
}
static enum body_status
http1_body_status(struct http *hp, struct http_conn *htc)
{
	ssize_t cl;
	const char *b;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);

	htc->content_length = -1;

	if (http_HdrIs(hp, H_Transfer_Encoding, "chunked")) {
		http_Unset(hp, H_Content_Length);
		return (BS_CHUNKED);
	}

	if (http_GetHdr(hp, H_Transfer_Encoding, &b))
		return (BS_ERROR);

	cl = http_GetContentLength(hp);
	if (cl == -2)
		return (BS_ERROR);
	if (cl >= 0) {
		htc->content_length = cl;
		return (cl == 0 ? BS_NONE : BS_LENGTH);
	}

	if (http_HdrIs(hp, H_Connection, "keep-alive")) {
		/*
		 * Keep alive with neither TE=Chunked or C-Len is impossible.
		 * We assume a zero length body.
		 */
		return (BS_NONE);
	}

	if (http_HdrIs(hp, H_Connection, "close")) {
		/*
		 * In this case, it is safe to just read what comes.
		 */
		return (BS_EOF);
	}

	if (hp->protover < 11) {
		/*
		 * With no Connection header, assume EOF.
		 */
		return (BS_EOF);
	}

	/*
	 * Fall back to EOF transfer.
	 */
	return (BS_EOF);
}
static enum body_status
http1_body_status(const struct http *hp, struct http_conn *htc, int request)
{
	ssize_t cl;
	const char *b;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);

	htc->content_length = -1;

	cl = http_GetContentLength(hp);
	if (cl == -2)
		return (BS_ERROR);
	if (http_GetHdr(hp, H_Transfer_Encoding, &b)) {
		if (strcasecmp(b, "chunked"))
			return (BS_ERROR);
		if (cl != -1) {
			/*
			 * RFC7230 3.3.3 allows more lenient handling
			 * but we're going to be strict.
			 */
			return (BS_ERROR);
		}
		return (BS_CHUNKED);
	}
	if (cl >= 0) {
		htc->content_length = cl;
		return (cl == 0 ? BS_NONE : BS_LENGTH);
	}

	if (hp->protover == 11 && (request || !http_HdrIs(hp, H_Connection, "close")))
		return (BS_NONE);

	if (http_HdrIs(hp, H_Connection, "keep-alive")) {
		/*
		 * Keep alive with neither TE=Chunked or C-Len is impossible.
		 * We assume a zero length body.
		 */
		return (BS_NONE);
	}

	/*
	 * Fall back to EOF transfer.
	 */
	return (BS_EOF);
}
Beispiel #4
0
static enum req_fsm_nxt
cnt_transmit(struct worker *wrk, struct req *req)
{
	struct boc *boc;
	const char *r;
	uint16_t status;
	int sendbody;
	intmax_t clval;

	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);

	/* Grab a ref to the bo if there is one */
	boc = HSH_RefBoc(req->objcore);

	clval = http_GetContentLength(req->resp);
	if (boc != NULL)
		req->resp_len = clval;
	else
		req->resp_len = ObjGetLen(req->wrk, req->objcore);

	req->res_mode = 0;

	/* RFC 7230, 3.3.3 */
	status = http_GetStatus(req->resp);
	if (!strcmp(req->http0->hd[HTTP_HDR_METHOD].b, "HEAD")) {
		if (req->objcore->flags & OC_F_PASS)
			sendbody = -1;
		else
			sendbody = 0;
	} else if (status < 200 || status == 204 || status == 304) {
		req->resp_len = -1;
		sendbody = 0;
	} else
		sendbody = 1;

	if (sendbody >= 0) {
		if (!req->disable_esi && req->resp_len != 0 &&
		    ObjHasAttr(wrk, req->objcore, OA_ESIDATA))
			VDP_push(req, VDP_ESI, NULL, 0, "ESI");

		if (cache_param->http_gzip_support &&
		    ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) &&
		    !RFC2616_Req_Gzip(req->http))
			VDP_push(req, VDP_gunzip, NULL, 1, "GUZ");

		if (cache_param->http_range_support &&
		    http_IsStatus(req->resp, 200)) {
			http_SetHeader(req->resp, "Accept-Ranges: bytes");
			if (sendbody && http_GetHdr(req->http, H_Range, &r))
				VRG_dorange(req, r);
		}
	}

	if (sendbody < 0) {
		/* Don't touch pass+HEAD C-L */
		sendbody = 0;
	} else if (clval >= 0 && clval == req->resp_len) {
		/* Reuse C-L header */
	} else {
		http_Unset(req->resp, H_Content_Length);
		if (req->resp_len >= 0 && sendbody)
			http_PrintfHeader(req->resp,
			    "Content-Length: %jd", req->resp_len);
	}

	req->transport->deliver(req, boc, sendbody);

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

	if (req->objcore->flags & (OC_F_PRIVATE | OC_F_PASS)) {
		if (boc != NULL) {
			HSH_Abandon(req->objcore);
			ObjWaitState(req->objcore, BOS_FINISHED);
		}
		ObjSlim(wrk, req->objcore);
	}

	if (boc != NULL)
		HSH_DerefBoc(wrk, req->objcore);

	(void)HSH_DerefObjCore(wrk, &req->objcore);
	http_Teardown(req->resp);

	return (REQ_FSM_DONE);
}