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); }
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); }
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; }
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); }