SES_Proto_Sess(struct worker *wrk, void *arg) { struct req *req; struct sess *sp; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CAST_OBJ_NOTNULL(sp, arg, SESS_MAGIC); WS_Release(sp->ws, 0); /* * Assume we're going to receive something that will likely * involve a request... */ (void)VTCP_blocking(sp->fd); req = Req_New(wrk, sp); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); req->htc->fd = sp->fd; SES_RxInit(req->htc, req->ws, cache_param->http_req_size, cache_param->http_req_hdr_len); if (sp->sess_step < S_STP_H1_LAST) { wrk->task.func = SES_Proto_Req; wrk->task.priv = req; } else if (sp->sess_step < S_STP_PROXY_LAST) { wrk->task.func = VPX_Proto_Sess; wrk->task.priv = req; } else { WRONG("Wrong session step"); } }
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); }
int VTCP_connect(int s, const struct suckaddr *name, int msec) { int i, k; socklen_t l; struct pollfd fds[1]; const struct sockaddr *sa; socklen_t sl; assert(s >= 0); /* Set the socket non-blocking */ if (msec > 0) (void)VTCP_nonblocking(s); /* Attempt the connect */ AN(VSA_Sane(name)); sa = VSA_Get_Sockaddr(name, &sl); i = connect(s, sa, sl); if (i == 0 || errno != EINPROGRESS) return (i); assert(msec > 0); /* Exercise our patience, polling for write */ fds[0].fd = s; fds[0].events = POLLWRNORM; fds[0].revents = 0; i = poll(fds, 1, msec); if (i == 0) { /* Timeout, close and give up */ errno = ETIMEDOUT; return (-1); } /* Find out if we got a connection */ l = sizeof k; AZ(getsockopt(s, SOL_SOCKET, SO_ERROR, &k, &l)); /* An error means no connection established */ errno = k; if (k) return (-1); (void)VTCP_blocking(s); return (0); }
int VTCP_connected(int s) { int k; socklen_t l; /* Find out if we got a connection */ l = sizeof k; AZ(getsockopt(s, SOL_SOCKET, SO_ERROR, &k, &l)); /* An error means no connection established */ errno = k; if (k) { AZ(close(s)); return (-1); } (void)VTCP_blocking(s); return (s); }
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); }
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); }
static void HTTP1_Session(struct worker *wrk, struct req *req) { enum htc_status_e hs; struct sess *sp; const char *st; int i; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); sp = req->sp; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); /* * Whenever we come in from the acceptor or waiter, we need to set * blocking mode. It would be simpler to do this in the acceptor * or waiter, but we'd rather do the syscall in the worker thread. */ if (http1_getstate(sp) == H1NEWREQ) VTCP_blocking(sp->fd); req->transport = &HTTP1_transport; while (1) { st = http1_getstate(sp); if (st == H1NEWREQ) { CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC); assert(isnan(req->t_prev)); assert(isnan(req->t_req)); AZ(req->vcl); AZ(req->esi_level); AN(req->htc->ws->r); hs = HTC_RxStuff(req->htc, HTTP1_Complete, &req->t_first, &req->t_req, sp->t_idle + cache_param->timeout_linger, sp->t_idle + cache_param->timeout_idle, NAN, cache_param->http_req_size); AZ(req->htc->ws->r); if (hs < HTC_S_EMPTY) { req->acct.req_hdrbytes += req->htc->rxbuf_e - req->htc->rxbuf_b; Req_Cleanup(sp, wrk, req); Req_Release(req); switch (hs) { case HTC_S_CLOSE: SES_Delete(sp, SC_REM_CLOSE, NAN); return; case HTC_S_TIMEOUT: SES_Delete(sp, SC_RX_TIMEOUT, NAN); return; case HTC_S_OVERFLOW: SES_Delete(sp, SC_RX_OVERFLOW, NAN); return; case HTC_S_EOF: SES_Delete(sp, SC_REM_CLOSE, NAN); return; default: WRONG("htc_status (bad)"); } } if (hs == HTC_S_IDLE) { wrk->stats->sess_herd++; Req_Cleanup(sp, wrk, req); Req_Release(req); SES_Wait(sp, &HTTP1_transport); return; } if (hs != HTC_S_COMPLETE) WRONG("htc_status (nonbad)"); if (H2_prism_complete(req->htc) == HTC_S_COMPLETE) { if (!FEATURE(FEATURE_HTTP2)) { SES_Close(req->sp, SC_REQ_HTTP20); AZ(req->ws->r); AZ(wrk->aws->r); http1_setstate(sp, H1CLEANUP); continue; } http1_setstate(sp, NULL); H2_PU_Sess(wrk, sp, req); return; } i = http1_dissect(wrk, req); req->acct.req_hdrbytes += req->htc->rxbuf_e - req->htc->rxbuf_b; if (i) { assert(req->doclose > 0); SES_Close(req->sp, req->doclose); AZ(req->ws->r); AZ(wrk->aws->r); http1_setstate(sp, H1CLEANUP); continue; } if (http_HdrIs(req->http, H_Upgrade, "h2c")) { if (!FEATURE(FEATURE_HTTP2)) { VSLb(req->vsl, SLT_Debug, "H2 upgrade attempt"); } else if (req->htc->body_status != BS_NONE) { VSLb(req->vsl, SLT_Debug, "H2 upgrade attempt has body"); } else { http1_setstate(sp, NULL); req->err_code = 2; H2_OU_Sess(wrk, sp, req); return; } } req->req_step = R_STP_TRANSPORT; http1_setstate(sp, H1PROC); } else if (st == H1PROC) { req->task.func = http1_req; req->task.priv = req; wrk->stats->client_req++; CNT_Embark(wrk, req); if (req->req_step == R_STP_TRANSPORT) VCL_TaskEnter(req->vcl, req->privs); if (CNT_Request(req) == REQ_FSM_DISEMBARK) return; AZ(req->vcl0); req->task.func = NULL; req->task.priv = NULL; AZ(req->ws->r); AZ(wrk->aws->r); http1_setstate(sp, H1CLEANUP); } else if (st == H1CLEANUP) { AZ(wrk->aws->r); AZ(req->ws->r); if (sp->fd >= 0 && req->doclose != SC_NULL) SES_Close(sp, req->doclose); if (sp->fd < 0) { wrk->stats->sess_closed++; Req_Cleanup(sp, wrk, req); Req_Release(req); SES_Delete(sp, SC_NULL, NAN); return; } Req_Cleanup(sp, wrk, req); HTC_RxInit(req->htc, req->ws); if (req->htc->rxbuf_e != req->htc->rxbuf_b) wrk->stats->sess_readahead++; http1_setstate(sp, H1NEWREQ); } else { WRONG("Wrong H1 session state"); } } }
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 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 CNT_Session(struct sess *sp) { int done; struct worker *wrk; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); wrk = sp->wrk; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); /* * Possible entrance states */ assert( sp->step == STP_FIRST || sp->step == STP_START || sp->step == STP_LOOKUP || sp->step == STP_RECV); AZ(wrk->obj); AZ(wrk->objcore); /* * Whenever we come in from the acceptor or waiter, we need to set * blocking mode, but there is no point in setting it when we come from * ESI or when a parked sessions returns. * It would be simpler to do this in the acceptor or waiter, but we'd * rather do the syscall in the worker thread. * On systems which return errors for ioctl, we close early */ if ((sp->step == STP_FIRST || sp->step == STP_START) && VTCP_blocking(sp->fd)) { if (errno == ECONNRESET) SES_Close(sp, "remote closed"); else SES_Close(sp, "error"); sp->step = STP_DONE; } /* * NB: Once done is set, we can no longer touch sp! */ for (done = 0; !done; ) { assert(sp->wrk == wrk); /* * This is a good place to be paranoid about the various * pointers still pointing to the things we expect. */ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_ORNULL(wrk->obj, OBJECT_MAGIC); CHECK_OBJ_ORNULL(wrk->nobjhead, OBJHEAD_MAGIC); WS_Assert(wrk->ws); switch (sp->step) { #define STEP(l,u) \ case STP_##u: \ if (cache_param->diag_bitmap & 0x01) \ cnt_diag(sp, #u); \ done = cnt_##l(sp); \ break; #include "tbl/steps.h" #undef STEP default: WRONG("State engine misfire"); } WS_Assert(wrk->ws); CHECK_OBJ_ORNULL(wrk->nobjhead, OBJHEAD_MAGIC); } WSL_Flush(wrk, 0); AZ(wrk->obj); AZ(wrk->objcore); #define ACCT(foo) AZ(wrk->acct_tmp.foo); #include "tbl/acct_fields.h" #undef ACCT assert(WRW_IsReleased(wrk)); }
void HTTP1_Session(struct worker *wrk, struct req *req) { enum htc_status_e hs; struct sess *sp; const char *st; int i; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); sp = req->sp; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); /* * Whenever we come in from the acceptor or waiter, we need to set * blocking mode. It would be simpler to do this in the acceptor * or waiter, but we'd rather do the syscall in the worker thread. * On systems which return errors for ioctl, we close early */ if (http1_getstate(sp) == H1NEWREQ && VTCP_blocking(sp->fd)) { if (errno == ECONNRESET) SES_Close(sp, SC_REM_CLOSE); else SES_Close(sp, SC_TX_ERROR); AN(Req_Cleanup(sp, wrk, req)); return; } while (1) { st = http1_getstate(sp); if (st == H1NEWREQ) { assert(isnan(req->t_prev)); assert(isnan(req->t_req)); AZ(req->vcl); AZ(req->esi_level); hs = HTC_RxStuff(req->htc, HTTP1_Complete, &req->t_first, &req->t_req, sp->t_idle + cache_param->timeout_linger, sp->t_idle + cache_param->timeout_idle, cache_param->http_req_size); XXXAZ(req->htc->ws->r); if (hs < HTC_S_EMPTY) { req->acct.req_hdrbytes += req->htc->rxbuf_e - req->htc->rxbuf_b; CNT_AcctLogCharge(wrk->stats, req); Req_Release(req); switch(hs) { case HTC_S_CLOSE: SES_Delete(sp, SC_REM_CLOSE, NAN); return; case HTC_S_TIMEOUT: SES_Delete(sp, SC_RX_TIMEOUT, NAN); return; case HTC_S_OVERFLOW: SES_Delete(sp, SC_RX_OVERFLOW, NAN); return; case HTC_S_EOF: SES_Delete(sp, SC_REM_CLOSE, NAN); return; default: WRONG("htc_status (bad)"); } } if (hs == HTC_S_IDLE) { wrk->stats->sess_herd++; Req_Release(req); SES_Wait(sp, &HTTP1_transport); return; } if (hs != HTC_S_COMPLETE) WRONG("htc_status (nonbad)"); i = http1_dissect(wrk, req); req->acct.req_hdrbytes += req->htc->rxbuf_e - req->htc->rxbuf_b; if (i) { SES_Close(req->sp, req->doclose); http1_setstate(sp, H1CLEANUP); } else { req->req_step = R_STP_RECV; http1_setstate(sp, H1PROC); } } else if (st == H1BUSY) { /* * Return from waitinglist. * Check to see if the remote has left. */ if (VTCP_check_hup(sp->fd)) { AN(req->hash_objhead); (void)HSH_DerefObjHead(wrk, &req->hash_objhead); AZ(req->hash_objhead); SES_Close(sp, SC_REM_CLOSE); AN(Req_Cleanup(sp, wrk, req)); return; } http1_setstate(sp, H1PROC); } else if (st == H1PROC) { req->transport = &HTTP1_transport; if (CNT_Request(wrk, req) == REQ_FSM_DISEMBARK) { req->task.func = http1_req; req->task.priv = req; http1_setstate(sp, H1BUSY); return; } req->transport = NULL; http1_setstate(sp, H1CLEANUP); } else if (st == H1CLEANUP) { if (Req_Cleanup(sp, wrk, req)) return; HTC_RxInit(req->htc, req->ws); if (req->htc->rxbuf_e != req->htc->rxbuf_b) wrk->stats->sess_readahead++; http1_setstate(sp, H1NEWREQ); } else { WRONG("Wrong H1 session state"); } } }
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); }
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; }
void HTTP1_Session(struct worker *wrk, struct req *req) { enum req_fsm_nxt nxt = REQ_FSM_MORE; struct sess *sp; enum http1_cleanup_ret sdr; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); sp = req->sp; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); /* * Whenever we come in from the acceptor or waiter, we need to set * blocking mode. It would be simpler to do this in the acceptor * or waiter, but we'd rather do the syscall in the worker thread. * On systems which return errors for ioctl, we close early */ if (sp->sess_step == S_STP_NEWREQ && VTCP_blocking(sp->fd)) { if (errno == ECONNRESET) SES_Close(sp, SC_REM_CLOSE); else SES_Close(sp, SC_TX_ERROR); sdr = http1_cleanup(sp, wrk, req); assert(sdr == SESS_DONE_RET_GONE); return; } /* * Return from waitinglist. Check to see if the remote has left. */ if (req->req_step == R_STP_LOOKUP && VTCP_check_hup(sp->fd)) { AN(req->hash_objhead); (void)HSH_DerefObjHead(wrk, &req->hash_objhead); AZ(req->hash_objhead); SES_Close(sp, SC_REM_CLOSE); sdr = http1_cleanup(sp, wrk, req); assert(sdr == SESS_DONE_RET_GONE); return; } if (sp->sess_step == S_STP_NEWREQ) { HTTP1_Init(req->htc, req->ws, sp->fd, req->vsl, cache_param->http_req_size, cache_param->http_req_hdr_len); } while (1) { assert( sp->sess_step == S_STP_NEWREQ || req->req_step == R_STP_LOOKUP || req->req_step == R_STP_RECV); if (sp->sess_step == S_STP_WORKING) { if (req->req_step == R_STP_RECV) nxt = http1_dissect(wrk, req); if (nxt == REQ_FSM_MORE) nxt = CNT_Request(wrk, req); if (nxt == REQ_FSM_DISEMBARK) return; assert(nxt == REQ_FSM_DONE); sdr = http1_cleanup(sp, wrk, req); switch (sdr) { case SESS_DONE_RET_GONE: return; case SESS_DONE_RET_WAIT: sp->sess_step = S_STP_NEWREQ; break; case SESS_DONE_RET_START: sp->sess_step = S_STP_WORKING; req->req_step = R_STP_RECV; break; default: WRONG("Illegal enum http1_cleanup_ret"); } } if (sp->sess_step == S_STP_NEWREQ) { nxt = http1_wait(sp, wrk, req); if (nxt != REQ_FSM_MORE) return; sp->sess_step = S_STP_WORKING; req->req_step = R_STP_RECV; } } }
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); }