int V1F_FetchRespHdr(struct busyobj *bo) { struct http *hp; int i; double t; struct http_conn *htc; enum htc_status_e hs; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC); CHECK_OBJ_ORNULL(bo->req, REQ_MAGIC); htc = bo->htc; assert(*htc->rfd > 0); VSC_C_main->backend_req++; /* Receive response */ HTC_RxInit(htc, bo->ws); CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC); t = VTIM_real() + htc->first_byte_timeout; hs = HTC_RxStuff(htc, HTTP1_Complete, NULL, NULL, t, t + htc->between_bytes_timeout, cache_param->http_resp_size); if (hs != HTC_S_COMPLETE) { bo->acct.beresp_hdrbytes += htc->rxbuf_e - htc->rxbuf_b; switch (hs) { case HTC_S_JUNK: VSLb(bo->vsl, SLT_FetchError, "Received junk"); htc->doclose = SC_RX_JUNK; break; case HTC_S_CLOSE: VSLb(bo->vsl, SLT_FetchError, "backend closed"); htc->doclose = SC_RESP_CLOSE; break; case HTC_S_TIMEOUT: VSLb(bo->vsl, SLT_FetchError, "timeout"); htc->doclose = SC_RX_TIMEOUT; break; case HTC_S_OVERFLOW: VSLb(bo->vsl, SLT_FetchError, "overflow"); htc->doclose = SC_RX_OVERFLOW; break; default: VSLb(bo->vsl, SLT_FetchError, "HTC %s (%d)", HTC_Status(hs), hs); htc->doclose = SC_RX_BAD; break; } return (htc->rxbuf_e == htc->rxbuf_b ? 1 : -1); } VTCP_set_read_timeout(*htc->rfd, htc->between_bytes_timeout); hp = bo->beresp; i = HTTP1_DissectResponse(htc, hp, bo->bereq); bo->acct.beresp_hdrbytes += htc->rxbuf_e - htc->rxbuf_b; if (i) { VSLb(bo->vsl, SLT_FetchError, "http format error"); htc->doclose = SC_RX_JUNK; return (-1); } htc->doclose = http_DoConnection(hp); RFC2616_Response_Body(bo->wrk, bo); assert(bo->vfc->resp == bo->beresp); if (bo->htc->body_status != BS_NONE && bo->htc->body_status != BS_ERROR) if (V1F_Setup_Fetch(bo->vfc, bo->htc)) { VSLb(bo->vsl, SLT_FetchError, "overflow"); htc->doclose = SC_RX_OVERFLOW; return (-1); } return (0); }
static int cnt_start(struct sess *sp) { int done; char *p; const char *r = "HTTP/1.1 100 Continue\r\n\r\n"; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); AZ(sp->restarts); AZ(sp->obj); AZ(sp->vcl); /* Update stats of various sorts */ VSL_stats->client_req++; /* XXX not locked */ sp->t_req = TIM_real(); sp->wrk->lastused = sp->t_req; sp->acct_req.req++; /* Assign XID and log */ sp->xid = ++xids; /* XXX not locked */ WSP(sp, SLT_ReqStart, "%s %s %u", sp->addr, sp->port, sp->xid); /* Borrow VCL reference from worker thread */ VCL_Refresh(&sp->wrk->vcl); sp->vcl = sp->wrk->vcl; sp->wrk->vcl = NULL; http_Setup(sp->http, sp->ws); done = http_DissectRequest(sp); /* Catch request snapshot */ sp->ws_req = WS_Snapshot(sp->ws); /* Catch original request, before modification */ *sp->http0 = *sp->http; if (done != 0) { sp->err_code = done; sp->step = STP_ERROR; return (0); } sp->doclose = http_DoConnection(sp->http); /* XXX: Handle TRACE & OPTIONS of Max-Forwards = 0 */ /* * Handle Expect headers */ if (http_GetHdr(sp->http, H_Expect, &p)) { if (strcmp(p, "100-continue")) { sp->err_code = 417; sp->step = STP_ERROR; return (0); } /* XXX: Don't bother with write failures for now */ (void)write(sp->fd, r, strlen(r)); /* XXX: When we do ESI includes, this is not removed * XXX: because we use http0 as our basis. Believed * XXX: safe, but potentially confusing. */ http_Unset(sp->http, H_Expect); } sp->step = STP_RECV; return (0); }
static int http1_dissect(struct worker *wrk, struct req *req) { const char *r_100 = "HTTP/1.1 100 Continue\r\n\r\n"; const char *r_400 = "HTTP/1.1 400 Bad Request\r\n\r\n"; const char *r_417 = "HTTP/1.1 417 Expectation Failed\r\n\r\n"; const char *p; ssize_t r; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); /* Allocate a new vxid now that we know we'll need it. */ AZ(req->vsl->wid); req->vsl->wid = VXID_Get(wrk, VSL_CLIENTMARKER); VSLb(req->vsl, SLT_Begin, "req %u rxreq", VXID(req->sp->vxid)); VSL(SLT_Link, req->sp->vxid, "req %u rxreq", VXID(req->vsl->wid)); AZ(isnan(req->t_first)); /* First byte timestamp set by http1_wait */ AZ(isnan(req->t_req)); /* Complete req rcvd set by http1_wait */ req->t_prev = req->t_first; VSLb_ts_req(req, "Start", req->t_first); VSLb_ts_req(req, "Req", req->t_req); /* Borrow VCL reference from worker thread */ VCL_Refresh(&wrk->vcl); req->vcl = wrk->vcl; wrk->vcl = NULL; HTTP_Setup(req->http, req->ws, req->vsl, SLT_ReqMethod); req->err_code = HTTP1_DissectRequest(req->htc, req->http); /* If we could not even parse the request, just close */ if (req->err_code != 0) { VSLb(req->vsl, SLT_HttpGarbage, "%.*s", (int)(req->htc->rxbuf_e - req->htc->rxbuf_b), req->htc->rxbuf_b); wrk->stats->client_req_400++; r = write(req->sp->fd, r_400, strlen(r_400)); if (r > 0) req->acct.resp_hdrbytes += r; req->doclose = SC_RX_JUNK; return (-1); } assert (req->req_body_status == REQ_BODY_INIT); if (req->htc->body_status == BS_CHUNKED) { req->req_body_status = REQ_BODY_WITHOUT_LEN; } else if (req->htc->body_status == BS_LENGTH) { req->req_body_status = REQ_BODY_WITH_LEN; } else if (req->htc->body_status == BS_NONE) { req->req_body_status = REQ_BODY_NONE; } else if (req->htc->body_status == BS_EOF) { req->req_body_status = REQ_BODY_WITHOUT_LEN; } else { WRONG("Unknown req.body_length situation"); } if (http_GetHdr(req->http, H_Expect, &p)) { if (strcasecmp(p, "100-continue")) { wrk->stats->client_req_417++; req->err_code = 417; r = write(req->sp->fd, r_417, strlen(r_417)); if (r > 0) req->acct.resp_hdrbytes += r; req->doclose = SC_RX_JUNK; return (-1); } r = write(req->sp->fd, r_100, strlen(r_100)); if (r > 0) req->acct.resp_hdrbytes += r; if (r != strlen(r_100)) { req->doclose = SC_REM_CLOSE; return (-1); } http_Unset(req->http, H_Expect); } wrk->stats->client_req++; wrk->stats->s_req++; AZ(req->err_code); req->ws_req = WS_Snapshot(req->ws); req->doclose = http_DoConnection(req->http); if (req->doclose == SC_RX_BAD) { r = write(req->sp->fd, r_400, strlen(r_400)); if (r > 0) req->acct.resp_hdrbytes += r; return (-1); } assert(req->req_body_status != REQ_BODY_INIT); HTTP_Copy(req->http0, req->http); // For ESI & restart return (0); }
int V1F_FetchRespHdr(struct busyobj *bo) { struct http *hp; enum htc_status_e hs; int first; struct http_conn *htc; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC); CHECK_OBJ_ORNULL(bo->req, REQ_MAGIC); htc = bo->htc; VSC_C_main->backend_req++; /* Receive response */ SES_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 = SES_Rx(htc, 0); if (hs == HTC_S_MORE) hs = HTTP1_Complete(htc); if (hs == HTC_S_OVERFLOW) { WS_ReleaseP(htc->ws, htc->rxbuf_b); bo->acct.beresp_hdrbytes += htc->rxbuf_e - htc->rxbuf_b; VSLb(bo->vsl, SLT_FetchError, "http %sread error: overflow", first ? "first " : ""); htc->doclose = SC_RX_OVERFLOW; return (-1); } if (hs == HTC_S_EOF) { WS_ReleaseP(htc->ws, htc->rxbuf_b); bo->acct.beresp_hdrbytes += htc->rxbuf_e - htc->rxbuf_b; VSLb(bo->vsl, SLT_FetchError, "http %sread error: EOF", first ? "first " : ""); htc->doclose = SC_RX_TIMEOUT; return (first ? 1 : -1); } if (first) { first = 0; VTCP_set_read_timeout(htc->fd, htc->between_bytes_timeout); } } while (hs != HTC_S_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"); htc->doclose = SC_RX_JUNK; return (-1); } htc->doclose = http_DoConnection(hp); return (0); }
static enum req_fsm_nxt cnt_recv(struct worker *wrk, struct req *req) { unsigned recv_handling; struct SHA256Context sha256ctx; const char *xff; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC); AZ(req->objcore); AZ(isnan(req->t_first)); AZ(isnan(req->t_prev)); AZ(isnan(req->t_req)); VSLb(req->vsl, SLT_ReqStart, "%s %s", req->sp->client_addr_str, req->sp->client_port_str); http_VSL_log(req->http); if (req->restarts == 0) { /* * This really should be done earlier, but we want to capture * it in the VSL log. */ if (http_GetHdr(req->http, H_X_Forwarded_For, &xff)) { http_Unset(req->http, H_X_Forwarded_For); http_PrintfHeader(req->http, "X-Forwarded-For: %s, %s", xff, req->sp->client_addr_str); } else { http_PrintfHeader(req->http, "X-Forwarded-For: %s", req->sp->client_addr_str); } } if (req->err_code) { req->req_step = R_STP_SYNTH; return (REQ_FSM_MORE); } req->doclose = http_DoConnection(req->http); /* By default we use the first backend */ AZ(req->director_hint); req->director_hint = req->vcl->director[0]; AN(req->director_hint); req->d_ttl = -1; req->disable_esi = 0; req->hash_always_miss = 0; req->hash_ignore_busy = 0; req->client_identity = NULL; http_CollectHdr(req->http, H_Cache_Control); VCL_recv_method(req->vcl, wrk, req, NULL, req->http->ws); /* Attempts to cache req.body may fail */ if (req->req_body_status == REQ_BODY_FAIL) return (REQ_FSM_DONE); recv_handling = wrk->handling; /* We wash the A-E header here for the sake of VRY */ if (cache_param->http_gzip_support && (recv_handling != VCL_RET_PIPE) && (recv_handling != VCL_RET_PASS)) { if (RFC2616_Req_Gzip(req->http)) { http_ForceHeader(req->http, H_Accept_Encoding, "gzip"); } else { http_Unset(req->http, H_Accept_Encoding); } } req->sha256ctx = &sha256ctx; /* so HSH_AddString() can find it */ SHA256_Init(req->sha256ctx); VCL_hash_method(req->vcl, wrk, req, NULL, req->http->ws); assert(wrk->handling == VCL_RET_LOOKUP); SHA256_Final(req->digest, req->sha256ctx); req->sha256ctx = NULL; if (!strcmp(req->http->hd[HTTP_HDR_METHOD].b, "HEAD")) req->wantbody = 0; else req->wantbody = 1; switch(recv_handling) { case VCL_RET_PURGE: req->req_step = R_STP_PURGE; return (REQ_FSM_MORE); case VCL_RET_HASH: req->req_step = R_STP_LOOKUP; return (REQ_FSM_MORE); case VCL_RET_PIPE: if (req->esi_level == 0) { req->req_step = R_STP_PIPE; return (REQ_FSM_MORE); } VSLb(req->vsl, SLT_VCL_Error, "vcl_recv{} returns pipe for ESI included object." " Doing pass."); req->req_step = R_STP_PASS; return (REQ_FSM_DONE); case VCL_RET_PASS: req->req_step = R_STP_PASS; return (REQ_FSM_MORE); case VCL_RET_SYNTH: req->req_step = R_STP_SYNTH; return (REQ_FSM_MORE); default: WRONG("Illegal return from vcl_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); }