static void * server_bgthread(struct worker *wrk, void *priv) { struct vmod_fsdirector_file_system *fs; struct sockaddr_storage addr_s; socklen_t len; struct http_conn *htc; int fd; enum htc_status_e htc_status; CAST_OBJ_NOTNULL(fs, priv, VMOD_FSDIRECTOR_MAGIC); assert(fs->sock >= 0); htc = &fs->htc; fs->wrk = wrk; WS_Init(wrk->aws, fs->ws_name, malloc(WS_LEN), WS_LEN); while (1) { do { fd = accept(fs->sock, (void*)&addr_s, &len); } while (fd < 0 && errno == EAGAIN); if (fd < 0) { continue; } HTTP1_Init(htc, wrk->aws, fd, NULL, HTTP1_BUF, HTTP1_MAX_HDR); htc_status = HTTP1_Rx(htc); switch (htc_status) { case HTTP1_OVERFLOW: case HTTP1_ERROR_EOF: case HTTP1_ALL_WHITESPACE: case HTTP1_NEED_MORE: prepare_answer(htc, 400); prepare_body(htc); break; case HTTP1_COMPLETE: answer_appropriate(fs); break; } WS_Reset(wrk->aws, NULL); close(fd); } pthread_exit(0); NEEDLESS_RETURN(NULL); }
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 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; } } }