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