| | varnish-cache/bin/varnishd/cache/cache_req_fsm.c |
| 0 |
|
/*- |
| 1 |
|
* Copyright (c) 2006 Verdens Gang AS |
| 2 |
|
* Copyright (c) 2006-2017 Varnish Software AS |
| 3 |
|
* All rights reserved. |
| 4 |
|
* |
| 5 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
| 6 |
|
* |
| 7 |
|
* SPDX-License-Identifier: BSD-2-Clause |
| 8 |
|
* |
| 9 |
|
* Redistribution and use in source and binary forms, with or without |
| 10 |
|
* modification, are permitted provided that the following conditions |
| 11 |
|
* are met: |
| 12 |
|
* 1. Redistributions of source code must retain the above copyright |
| 13 |
|
* notice, this list of conditions and the following disclaimer. |
| 14 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
| 15 |
|
* notice, this list of conditions and the following disclaimer in the |
| 16 |
|
* documentation and/or other materials provided with the distribution. |
| 17 |
|
* |
| 18 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 19 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 20 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 21 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
| 22 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 23 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 24 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 25 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 26 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 27 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 28 |
|
* SUCH DAMAGE. |
| 29 |
|
* |
| 30 |
|
* This file contains the request-handling state engine, which is intended to |
| 31 |
|
* (over time) be(come) protocol agnostic. |
| 32 |
|
* We already use this now with ESI:includes, which are for all relevant |
| 33 |
|
* purposes a different "protocol" |
| 34 |
|
* |
| 35 |
|
* A special complication is the fact that we can suspend processing of |
| 36 |
|
* a request when hash-lookup finds a busy objhdr. |
| 37 |
|
* |
| 38 |
|
*/ |
| 39 |
|
|
| 40 |
|
#include "config.h" |
| 41 |
|
|
| 42 |
|
#include "cache_varnishd.h" |
| 43 |
|
#include "cache_filter.h" |
| 44 |
|
#include "cache_objhead.h" |
| 45 |
|
#include "cache_transport.h" |
| 46 |
|
#include "vcc_interface.h" |
| 47 |
|
|
| 48 |
|
#include "http1/cache_http1.h" |
| 49 |
|
#include "storage/storage.h" |
| 50 |
|
#include "vcl.h" |
| 51 |
|
#include "vct.h" |
| 52 |
|
#include "vsha256.h" |
| 53 |
|
#include "vtim.h" |
| 54 |
|
|
| 55 |
|
#define REQ_STEPS \ |
| 56 |
|
REQ_STEP(transport, TRANSPORT, ) \ |
| 57 |
|
REQ_STEP(restart, RESTART, static) \ |
| 58 |
|
REQ_STEP(recv, RECV, ) \ |
| 59 |
|
REQ_STEP(pipe, PIPE, static) \ |
| 60 |
|
REQ_STEP(pass, PASS, static) \ |
| 61 |
|
REQ_STEP(lookup, LOOKUP, static) \ |
| 62 |
|
REQ_STEP(purge, PURGE, static) \ |
| 63 |
|
REQ_STEP(miss, MISS, static) \ |
| 64 |
|
REQ_STEP(fetch, FETCH, static) \ |
| 65 |
|
REQ_STEP(deliver, DELIVER, static) \ |
| 66 |
|
REQ_STEP(vclfail, VCLFAIL, static) \ |
| 67 |
|
REQ_STEP(synth, SYNTH, static) \ |
| 68 |
|
REQ_STEP(transmit, TRANSMIT, static) \ |
| 69 |
|
REQ_STEP(finish, FINISH, static) |
| 70 |
|
|
| 71 |
|
#define REQ_STEP(l, U, priv) \ |
| 72 |
|
static req_state_f cnt_##l; \ |
| 73 |
|
priv const struct req_step R_STP_##U[1] = {{ \ |
| 74 |
|
.name = "Req Step " #l, \ |
| 75 |
|
.func = cnt_##l, \ |
| 76 |
|
}}; |
| 77 |
|
REQ_STEPS |
| 78 |
|
#undef REQ_STEP |
| 79 |
|
|
| 80 |
|
/*-------------------------------------------------------------------- |
| 81 |
|
* Handle "Expect:" and "Connection:" on incoming request |
| 82 |
|
*/ |
| 83 |
|
|
| 84 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 85 |
15068 |
cnt_transport(struct worker *wrk, struct req *req) |
| 86 |
|
{ |
| 87 |
|
const char *p; |
| 88 |
|
|
| 89 |
15068 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 90 |
15068 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 91 |
15068 |
CHECK_OBJ_NOTNULL(req->http, HTTP_MAGIC); |
| 92 |
15068 |
CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC); |
| 93 |
15068 |
AN(req->req_body_status); |
| 94 |
|
|
| 95 |
15068 |
if (http_GetHdr(req->http, H_Expect, &p)) { |
| 96 |
48 |
if (!http_expect_eq(p, 100-continue)) { |
| 97 |
8 |
req->doclose = SC_RX_JUNK; |
| 98 |
8 |
(void)req->transport->minimal_response(req, 417); |
| 99 |
8 |
wrk->stats->client_req_417++; |
| 100 |
8 |
return (REQ_FSM_DONE); |
| 101 |
|
} |
| 102 |
40 |
if (req->http->protover >= 11 && |
| 103 |
40 |
req->htc->pipeline_b == NULL) // XXX: HTTP1 vs 2 ? |
| 104 |
36 |
req->want100cont = 1; |
| 105 |
40 |
http_Unset(req->http, H_Expect); |
| 106 |
40 |
} |
| 107 |
|
|
| 108 |
15060 |
AZ(req->err_code); |
| 109 |
|
|
| 110 |
15060 |
req->doclose = http_DoConnection(req->http, SC_REQ_CLOSE); |
| 111 |
15060 |
if (req->doclose == SC_RX_BAD) { |
| 112 |
12 |
wrk->stats->client_req_400++; |
| 113 |
12 |
(void)req->transport->minimal_response(req, 400); |
| 114 |
12 |
return (REQ_FSM_DONE); |
| 115 |
|
} |
| 116 |
|
|
| 117 |
15048 |
if (req->req_body_status->avail == 1) { |
| 118 |
520 |
AN(req->transport->req_body != NULL); |
| 119 |
520 |
VFP_Setup(req->vfc, wrk); |
| 120 |
520 |
req->vfc->resp = req->http; // XXX |
| 121 |
520 |
req->transport->req_body(req); |
| 122 |
520 |
} |
| 123 |
|
|
| 124 |
15048 |
req->ws_req = WS_Snapshot(req->ws); |
| 125 |
15048 |
HTTP_Clone(req->http0, req->http); // For ESI & restart |
| 126 |
15048 |
req->req_step = R_STP_RECV; |
| 127 |
15048 |
return (REQ_FSM_MORE); |
| 128 |
15068 |
} |
| 129 |
|
|
| 130 |
|
/*-------------------------------------------------------------------- |
| 131 |
|
* Deliver an object to client |
| 132 |
|
*/ |
| 133 |
|
|
| 134 |
|
int |
| 135 |
13254 |
Resp_Setup_Deliver(struct req *req) |
| 136 |
|
{ |
| 137 |
|
struct http *h; |
| 138 |
|
struct objcore *oc; |
| 139 |
|
const void *hdrs; |
| 140 |
|
|
| 141 |
13254 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 142 |
13254 |
oc = req->objcore; |
| 143 |
13254 |
CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); |
| 144 |
|
|
| 145 |
13254 |
h = req->resp; |
| 146 |
|
|
| 147 |
13254 |
HTTP_Setup(h, req->ws, req->vsl, SLT_RespMethod); |
| 148 |
|
|
| 149 |
13254 |
hdrs = ObjGetAttr(req->wrk, oc, OA_HEADERS, NULL); |
| 150 |
13254 |
if (hdrs == NULL || HTTP_Decode(h, hdrs)) |
| 151 |
8 |
return (-1); |
| 152 |
|
|
| 153 |
13246 |
http_ForceField(h, HTTP_HDR_PROTO, "HTTP/1.1"); |
| 154 |
|
|
| 155 |
13246 |
if (req->is_hit) |
| 156 |
9116 |
http_PrintfHeader(h, "X-Varnish: %ju %ju", VXID(req->vsl->wid), |
| 157 |
4558 |
VXID(ObjGetXID(req->wrk, oc))); |
| 158 |
|
else |
| 159 |
8688 |
http_PrintfHeader(h, "X-Varnish: %ju", VXID(req->vsl->wid)); |
| 160 |
|
|
| 161 |
|
/* |
| 162 |
|
* We base Age calculation upon the last timestamp taken during client |
| 163 |
|
* request processing. This gives some inaccuracy, but since Age is only |
| 164 |
|
* full second resolution that shouldn't matter. (Last request timestamp |
| 165 |
|
* could be a Start timestamp taken before the object entered into cache |
| 166 |
|
* leading to negative age. Truncate to zero in that case). |
| 167 |
|
*/ |
| 168 |
26492 |
http_PrintfHeader(h, "Age: %.0f", |
| 169 |
13246 |
floor(fmax(0., req->t_prev - oc->t_origin))); |
| 170 |
|
|
| 171 |
13246 |
http_AppendHeader(h, H_Via, http_ViaHeader()); |
| 172 |
|
|
| 173 |
14346 |
if (cache_param->http_gzip_support && |
| 174 |
13225 |
ObjCheckFlag(req->wrk, oc, OF_GZIPED) && |
| 175 |
1100 |
!RFC2616_Req_Gzip(req->http)) |
| 176 |
400 |
RFC2616_Weaken_Etag(h); |
| 177 |
13246 |
return (0); |
| 178 |
13254 |
} |
| 179 |
|
|
| 180 |
|
void |
| 181 |
2263 |
Resp_Setup_Synth(struct req *req) |
| 182 |
|
{ |
| 183 |
|
struct http *h; |
| 184 |
|
|
| 185 |
2263 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 186 |
|
|
| 187 |
2263 |
h = req->resp; |
| 188 |
|
|
| 189 |
2263 |
HTTP_Setup(h, req->ws, req->vsl, SLT_RespMethod); |
| 190 |
|
|
| 191 |
2263 |
AZ(req->objcore); |
| 192 |
2263 |
http_PutResponse(h, "HTTP/1.1", req->err_code, req->err_reason); |
| 193 |
|
|
| 194 |
2263 |
http_TimeHeader(h, "Date: ", W_TIM_real(req->wrk)); |
| 195 |
2263 |
http_SetHeader(h, "Server: Varnish"); |
| 196 |
2263 |
http_PrintfHeader(h, "X-Varnish: %ju", VXID(req->vsl->wid)); |
| 197 |
|
|
| 198 |
|
/* |
| 199 |
|
* For late 100-continue, we suggest to VCL to close the connection to |
| 200 |
|
* neither send a 100-continue nor drain-read the request. But VCL has |
| 201 |
|
* the option to veto by removing Connection: close |
| 202 |
|
*/ |
| 203 |
2263 |
if (req->want100cont) |
| 204 |
12 |
http_SetHeader(h, "Connection: close"); |
| 205 |
2263 |
} |
| 206 |
|
|
| 207 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 208 |
13213 |
cnt_deliver(struct worker *wrk, struct req *req) |
| 209 |
|
{ |
| 210 |
|
unsigned status; |
| 211 |
|
|
| 212 |
13213 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 213 |
13213 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 214 |
13213 |
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); |
| 215 |
13213 |
CHECK_OBJ_NOTNULL(req->objcore->objhead, OBJHEAD_MAGIC); |
| 216 |
13213 |
AZ(req->stale_oc); |
| 217 |
13213 |
AN(req->vcl); |
| 218 |
|
|
| 219 |
13213 |
assert(req->objcore->refcnt > 0); |
| 220 |
|
|
| 221 |
13213 |
ObjTouch(req->wrk, req->objcore, req->t_prev); |
| 222 |
|
|
| 223 |
13213 |
if (Resp_Setup_Deliver(req)) { |
| 224 |
8 |
(void)HSH_DerefObjCore(wrk, &req->objcore); |
| 225 |
8 |
req->err_code = 500; |
| 226 |
8 |
req->req_step = R_STP_SYNTH; |
| 227 |
8 |
return (REQ_FSM_MORE); |
| 228 |
|
} |
| 229 |
|
|
| 230 |
13205 |
status = http_GetStatus(req->resp); |
| 231 |
13205 |
if (cache_param->http_range_support && status == 200 && |
| 232 |
11725 |
!(req->objcore->flags & OC_F_PRIVATE)) |
| 233 |
9514 |
http_ForceHeader(req->resp, H_Accept_Ranges, "bytes"); |
| 234 |
|
|
| 235 |
13205 |
req->t_resp = W_TIM_real(wrk); |
| 236 |
13205 |
VCL_deliver_method(req->vcl, wrk, req, NULL, NULL); |
| 237 |
|
|
| 238 |
13205 |
assert(req->restarts <= req->max_restarts); |
| 239 |
|
|
| 240 |
13205 |
if (wrk->vpi->handling != VCL_RET_DELIVER) { |
| 241 |
521 |
HSH_Cancel(wrk, req->objcore, NULL); |
| 242 |
521 |
(void)HSH_DerefObjCore(wrk, &req->objcore); |
| 243 |
521 |
http_Teardown(req->resp); |
| 244 |
|
|
| 245 |
521 |
switch (wrk->vpi->handling) { |
| 246 |
|
case VCL_RET_RESTART: |
| 247 |
124 |
req->req_step = R_STP_RESTART; |
| 248 |
124 |
break; |
| 249 |
|
case VCL_RET_FAIL: |
| 250 |
365 |
req->req_step = R_STP_VCLFAIL; |
| 251 |
365 |
break; |
| 252 |
|
case VCL_RET_SYNTH: |
| 253 |
32 |
req->req_step = R_STP_SYNTH; |
| 254 |
32 |
break; |
| 255 |
|
default: |
| 256 |
0 |
WRONG("Illegal return from vcl_deliver{}"); |
| 257 |
0 |
} |
| 258 |
|
|
| 259 |
521 |
return (REQ_FSM_MORE); |
| 260 |
|
} |
| 261 |
|
|
| 262 |
12684 |
VSLb_ts_req(req, "Process", W_TIM_real(wrk)); |
| 263 |
|
|
| 264 |
12684 |
assert(wrk->vpi->handling == VCL_RET_DELIVER); |
| 265 |
|
|
| 266 |
12684 |
if (IS_TOPREQ(req) && RFC2616_Do_Cond(req)) |
| 267 |
120 |
http_PutResponse(req->resp, "HTTP/1.1", 304, NULL); |
| 268 |
|
|
| 269 |
12684 |
req->req_step = R_STP_TRANSMIT; |
| 270 |
12684 |
return (REQ_FSM_MORE); |
| 271 |
13213 |
} |
| 272 |
|
|
| 273 |
|
/*-------------------------------------------------------------------- |
| 274 |
|
* VCL failed, die horribly |
| 275 |
|
*/ |
| 276 |
|
|
| 277 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 278 |
647 |
cnt_vclfail(struct worker *wrk, struct req *req) |
| 279 |
|
{ |
| 280 |
|
struct vrt_ctx ctx[1]; |
| 281 |
|
|
| 282 |
647 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 283 |
647 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 284 |
|
|
| 285 |
647 |
AZ(req->objcore); |
| 286 |
647 |
AZ(req->stale_oc); |
| 287 |
|
|
| 288 |
647 |
INIT_OBJ(ctx, VRT_CTX_MAGIC); |
| 289 |
647 |
VCL_Req2Ctx(ctx, req); |
| 290 |
|
|
| 291 |
647 |
Req_Rollback(ctx); |
| 292 |
|
|
| 293 |
647 |
if (req->req_reset) { |
| 294 |
107 |
req->err_code = 408; |
| 295 |
107 |
req->err_reason = "Client disconnected"; |
| 296 |
107 |
} else { |
| 297 |
540 |
req->err_code = 503; |
| 298 |
540 |
req->err_reason = "VCL failed"; |
| 299 |
|
} |
| 300 |
647 |
req->req_step = R_STP_SYNTH; |
| 301 |
647 |
req->doclose = SC_VCL_FAILURE; |
| 302 |
647 |
req->vdp_filter_list = NULL; |
| 303 |
647 |
return (REQ_FSM_MORE); |
| 304 |
|
} |
| 305 |
|
|
| 306 |
|
/*-------------------------------------------------------------------- |
| 307 |
|
* Emit a synthetic response |
| 308 |
|
*/ |
| 309 |
|
|
| 310 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 311 |
2256 |
cnt_synth(struct worker *wrk, struct req *req) |
| 312 |
|
{ |
| 313 |
|
struct vsb *synth_body; |
| 314 |
|
ssize_t sz, szl; |
| 315 |
|
uint16_t status; |
| 316 |
|
uint8_t *ptr; |
| 317 |
|
const char *body; |
| 318 |
|
|
| 319 |
2256 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 320 |
2256 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 321 |
|
|
| 322 |
2256 |
AZ(req->objcore); |
| 323 |
2256 |
AZ(req->stale_oc); |
| 324 |
|
|
| 325 |
2256 |
wrk->stats->s_synth++; |
| 326 |
|
|
| 327 |
2256 |
if (req->err_code < 100) |
| 328 |
0 |
req->err_code = 501; |
| 329 |
|
|
| 330 |
2256 |
Resp_Setup_Synth(req); |
| 331 |
|
|
| 332 |
2256 |
req->vdp_filter_list = NULL; |
| 333 |
2256 |
synth_body = VSB_new_auto(); |
| 334 |
2256 |
AN(synth_body); |
| 335 |
|
|
| 336 |
2256 |
req->t_resp = W_TIM_real(wrk); |
| 337 |
2256 |
VCL_synth_method(req->vcl, wrk, req, NULL, synth_body); |
| 338 |
|
|
| 339 |
2256 |
AZ(VSB_finish(synth_body)); |
| 340 |
|
|
| 341 |
2256 |
VSLb_ts_req(req, "Process", W_TIM_real(wrk)); |
| 342 |
|
|
| 343 |
2256 |
while (wrk->vpi->handling == VCL_RET_FAIL) { |
| 344 |
124 |
if (req->esi_level > 0) { |
| 345 |
4 |
wrk->vpi->handling = VCL_RET_DELIVER; |
| 346 |
4 |
break; |
| 347 |
|
} |
| 348 |
120 |
VSB_destroy(&synth_body); |
| 349 |
120 |
(void)VRB_Ignore(req); |
| 350 |
120 |
status = req->req_reset ? 408 : 500; |
| 351 |
120 |
(void)req->transport->minimal_response(req, status); |
| 352 |
120 |
req->doclose = SC_VCL_FAILURE; // XXX: Not necessary any more ? |
| 353 |
120 |
VSLb_ts_req(req, "Resp", W_TIM_real(wrk)); |
| 354 |
120 |
http_Teardown(req->resp); |
| 355 |
120 |
return (REQ_FSM_DONE); |
| 356 |
|
} |
| 357 |
|
|
| 358 |
2136 |
if (wrk->vpi->handling == VCL_RET_RESTART && req->restarts > req->max_restarts) |
| 359 |
4 |
wrk->vpi->handling = VCL_RET_DELIVER; |
| 360 |
|
|
| 361 |
2136 |
if (wrk->vpi->handling == VCL_RET_RESTART) { |
| 362 |
|
/* |
| 363 |
|
* XXX: Should we reset req->doclose = SC_VCL_FAILURE |
| 364 |
|
* XXX: If so, to what ? |
| 365 |
|
*/ |
| 366 |
44 |
HTTP_Setup(req->resp, req->ws, req->vsl, SLT_RespMethod); |
| 367 |
44 |
VSB_destroy(&synth_body); |
| 368 |
44 |
req->req_step = R_STP_RESTART; |
| 369 |
44 |
return (REQ_FSM_MORE); |
| 370 |
|
} |
| 371 |
2092 |
assert(wrk->vpi->handling == VCL_RET_DELIVER); |
| 372 |
|
|
| 373 |
2092 |
http_Unset(req->resp, H_Content_Length); |
| 374 |
4184 |
http_PrintfHeader(req->resp, "Content-Length: %zd", |
| 375 |
2092 |
VSB_len(synth_body)); |
| 376 |
|
|
| 377 |
3616 |
if (req->doclose == SC_NULL && |
| 378 |
1536 |
(http_HdrIs(req->resp, H_Connection, "close") || |
| 379 |
1524 |
http_HdrIs(req->http, H_Connection, "close"))) |
| 380 |
16 |
req->doclose = SC_RESP_CLOSE; |
| 381 |
|
|
| 382 |
|
/* Discard any lingering request body before delivery */ |
| 383 |
2092 |
(void)VRB_Ignore(req); |
| 384 |
|
|
| 385 |
2092 |
req->objcore = HSH_Private(wrk); |
| 386 |
2092 |
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); |
| 387 |
2092 |
szl = -1; |
| 388 |
2092 |
if (STV_NewObject(wrk, req->objcore, stv_transient, 0)) { |
| 389 |
2084 |
body = VSB_data(synth_body); |
| 390 |
2084 |
szl = VSB_len(synth_body); |
| 391 |
2084 |
assert(szl >= 0); |
| 392 |
4044 |
while (szl > 0) { |
| 393 |
1960 |
sz = szl; |
| 394 |
1960 |
if (! ObjGetSpace(wrk, req->objcore, &sz, &ptr)) { |
| 395 |
0 |
szl = -1; |
| 396 |
0 |
break; |
| 397 |
|
} |
| 398 |
1960 |
if (sz > szl) |
| 399 |
0 |
sz = szl; |
| 400 |
1960 |
szl -= sz; |
| 401 |
1960 |
memcpy(ptr, body, sz); |
| 402 |
1960 |
ObjExtend(wrk, req->objcore, sz, szl == 0 ? 1 : 0); |
| 403 |
1960 |
body += sz; |
| 404 |
|
} |
| 405 |
2084 |
} |
| 406 |
|
|
| 407 |
2092 |
if (szl >= 0) |
| 408 |
2084 |
AZ(ObjSetU64(wrk, req->objcore, OA_LEN, VSB_len(synth_body))); |
| 409 |
2092 |
HSH_DerefBoc(wrk, req->objcore); |
| 410 |
2092 |
VSB_destroy(&synth_body); |
| 411 |
|
|
| 412 |
2092 |
if (szl < 0) { |
| 413 |
8 |
VSLb(req->vsl, SLT_Error, "Could not get storage"); |
| 414 |
8 |
req->doclose = SC_OVERLOAD; |
| 415 |
8 |
VSLb_ts_req(req, "Resp", W_TIM_real(wrk)); |
| 416 |
8 |
(void)HSH_DerefObjCore(wrk, &req->objcore); |
| 417 |
8 |
http_Teardown(req->resp); |
| 418 |
8 |
return (REQ_FSM_DONE); |
| 419 |
|
} |
| 420 |
|
|
| 421 |
2084 |
req->req_step = R_STP_TRANSMIT; |
| 422 |
2084 |
return (REQ_FSM_MORE); |
| 423 |
2256 |
} |
| 424 |
|
|
| 425 |
|
/*-------------------------------------------------------------------- |
| 426 |
|
* The mechanics of sending a response (from deliver or synth) |
| 427 |
|
*/ |
| 428 |
|
|
| 429 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 430 |
14768 |
cnt_transmit(struct worker *wrk, struct req *req) |
| 431 |
|
{ |
| 432 |
14768 |
enum req_fsm_nxt nxt = REQ_FSM_MORE; |
| 433 |
|
enum vtr_deliver_e dnxt; |
| 434 |
|
uint16_t status; |
| 435 |
|
int sendbody, head; |
| 436 |
|
intmax_t clval; |
| 437 |
|
|
| 438 |
14768 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 439 |
14768 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 440 |
14768 |
CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC); |
| 441 |
14768 |
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); |
| 442 |
14768 |
AZ(req->stale_oc); |
| 443 |
14768 |
AZ(req->res_pipe | req->res_esi); |
| 444 |
14768 |
AZ(req->boc); |
| 445 |
14768 |
req->req_step = R_STP_FINISH; |
| 446 |
|
|
| 447 |
|
/* Grab a ref to the bo if there is one (=streaming) */ |
| 448 |
14768 |
req->boc = HSH_RefBoc(req->objcore); |
| 449 |
14768 |
if (req->boc && req->boc->state < BOS_STREAM) |
| 450 |
0 |
(void)ObjWaitState(req->objcore, BOS_STREAM); |
| 451 |
14768 |
clval = http_GetContentLength(req->resp); |
| 452 |
|
/* RFC 7230, 3.3.3 */ |
| 453 |
14768 |
status = http_GetStatus(req->resp); |
| 454 |
14768 |
head = http_method_eq(req->http0->hd[HTTP_HDR_METHOD].b, HEAD); |
| 455 |
|
|
| 456 |
14768 |
if (req->boc != NULL || (req->objcore->flags & (OC_F_FAILED))) |
| 457 |
3495 |
req->resp_len = clval; |
| 458 |
|
else |
| 459 |
11273 |
req->resp_len = ObjGetLen(req->wrk, req->objcore); |
| 460 |
|
|
| 461 |
14768 |
if (head || status < 200 || status == 204 || status == 304) { |
| 462 |
|
// rfc7230,l,1748,1752 |
| 463 |
676 |
sendbody = 0; |
| 464 |
676 |
} else { |
| 465 |
14092 |
sendbody = 1; |
| 466 |
|
} |
| 467 |
|
|
| 468 |
14768 |
VDP_Init(req->vdc, req->wrk, req->vsl, req, NULL, &req->resp_len); |
| 469 |
14768 |
if (req->vdp_filter_list == NULL) |
| 470 |
13628 |
req->vdp_filter_list = resp_Get_Filter_List(req); |
| 471 |
14768 |
if (req->vdp_filter_list == NULL || |
| 472 |
14768 |
VCL_StackVDP(req->vdc, req->vcl, req->vdp_filter_list, req, NULL)) { |
| 473 |
252 |
VSLb(req->vsl, SLT_Error, "Failure to push processors"); |
| 474 |
252 |
req->doclose = SC_OVERLOAD; |
| 475 |
252 |
req->acct.resp_bodybytes += |
| 476 |
252 |
VDP_Close(req->vdc, req->objcore, req->boc); |
| 477 |
252 |
} else { |
| 478 |
14516 |
if (status < 200 || status == 204) { |
| 479 |
|
// rfc7230,l,1691,1695 |
| 480 |
92 |
http_Unset(req->resp, H_Content_Length); |
| 481 |
14516 |
} else if (status == 304) { |
| 482 |
|
// rfc7230,l,1675,1677 |
| 483 |
136 |
http_Unset(req->resp, H_Content_Length); |
| 484 |
14424 |
} else if (clval >= 0 && clval == req->resp_len) { |
| 485 |
|
/* Reuse C-L header */ |
| 486 |
14288 |
} else if (head) { |
| 487 |
|
/* rfc9110,l,3226,3227 |
| 488 |
|
* "MAY send Content-Length ... [for] HEAD" |
| 489 |
|
* do not touch to support cached HEAD #4245 |
| 490 |
|
*/ |
| 491 |
16 |
req->resp_len = 0; |
| 492 |
16 |
} else { |
| 493 |
3667 |
http_Unset(req->resp, H_Content_Length); |
| 494 |
3667 |
if (req->resp_len >= 0) |
| 495 |
4350 |
http_PrintfHeader(req->resp, |
| 496 |
2175 |
"Content-Length: %jd", req->resp_len); |
| 497 |
|
} |
| 498 |
14516 |
if (req->resp_len == 0) |
| 499 |
4144 |
sendbody = 0; |
| 500 |
14516 |
dnxt = req->transport->deliver(req, sendbody); |
| 501 |
14516 |
if (dnxt == VTR_D_DISEMBARK) |
| 502 |
288 |
nxt = REQ_FSM_DISEMBARK; |
| 503 |
|
else |
| 504 |
14228 |
assert(dnxt == VTR_D_DONE); |
| 505 |
|
} |
| 506 |
14768 |
return (nxt); |
| 507 |
|
} |
| 508 |
|
|
| 509 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 510 |
14765 |
cnt_finish(struct worker *wrk, struct req *req) |
| 511 |
|
{ |
| 512 |
|
|
| 513 |
14765 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 514 |
14765 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 515 |
|
|
| 516 |
14765 |
VSLb_ts_req(req, "Resp", W_TIM_real(wrk)); |
| 517 |
|
|
| 518 |
14765 |
if (req->doclose == SC_NULL && (req->objcore->flags & OC_F_FAILED)) { |
| 519 |
|
/* The object we delivered failed due to a streaming error. |
| 520 |
|
* Fail the request. */ |
| 521 |
64 |
req->doclose = SC_TX_ERROR; |
| 522 |
64 |
} |
| 523 |
|
|
| 524 |
14765 |
if (req->boc != NULL) { |
| 525 |
3486 |
HSH_DerefBoc(wrk, req->objcore); |
| 526 |
3486 |
req->boc = NULL; |
| 527 |
3486 |
} |
| 528 |
|
|
| 529 |
14765 |
(void)HSH_DerefObjCore(wrk, &req->objcore); |
| 530 |
14765 |
http_Teardown(req->resp); |
| 531 |
|
|
| 532 |
14765 |
req->vdp_filter_list = NULL; |
| 533 |
14765 |
req->res_pipe = 0; |
| 534 |
14765 |
req->res_esi = 0; |
| 535 |
14765 |
return (REQ_FSM_DONE); |
| 536 |
|
} |
| 537 |
|
|
| 538 |
|
/*-------------------------------------------------------------------- |
| 539 |
|
* Initiated a fetch (pass/miss) which we intend to deliver |
| 540 |
|
*/ |
| 541 |
|
|
| 542 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 543 |
8772 |
cnt_fetch(struct worker *wrk, struct req *req) |
| 544 |
|
{ |
| 545 |
|
|
| 546 |
8772 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 547 |
8772 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 548 |
8772 |
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); |
| 549 |
8772 |
AZ(req->stale_oc); |
| 550 |
|
|
| 551 |
8772 |
wrk->stats->s_fetch++; |
| 552 |
8772 |
(void)VRB_Ignore(req); |
| 553 |
|
|
| 554 |
8772 |
if (req->objcore->flags & OC_F_FAILED) { |
| 555 |
108 |
req->err_code = 503; |
| 556 |
108 |
req->req_step = R_STP_SYNTH; |
| 557 |
108 |
(void)HSH_DerefObjCore(wrk, &req->objcore); |
| 558 |
108 |
AZ(req->objcore); |
| 559 |
108 |
return (REQ_FSM_MORE); |
| 560 |
|
} |
| 561 |
|
|
| 562 |
8664 |
req->req_step = R_STP_DELIVER; |
| 563 |
8664 |
return (REQ_FSM_MORE); |
| 564 |
8772 |
} |
| 565 |
|
|
| 566 |
|
/*-------------------------------------------------------------------- |
| 567 |
|
* Attempt to lookup objhdr from hash. We disembark and reenter |
| 568 |
|
* this state if we get suspended on a busy objhdr. |
| 569 |
|
*/ |
| 570 |
|
|
| 571 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 572 |
10652 |
cnt_lookup(struct worker *wrk, struct req *req) |
| 573 |
|
{ |
| 574 |
|
struct objcore *oc, *busy; |
| 575 |
|
enum lookup_e lr; |
| 576 |
|
int waitinglist_gen; |
| 577 |
|
|
| 578 |
10652 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 579 |
10652 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 580 |
10652 |
AZ(req->stale_oc); |
| 581 |
|
|
| 582 |
10652 |
AN(req->vcl); |
| 583 |
|
|
| 584 |
10652 |
VRY_Prep(req); |
| 585 |
10652 |
waitinglist_gen = req->waitinglist_gen; |
| 586 |
|
|
| 587 |
10652 |
if (req->waitinglist_gen) { |
| 588 |
227 |
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); |
| 589 |
227 |
req->waitinglist_gen = 0; |
| 590 |
227 |
} else |
| 591 |
10425 |
AZ(req->objcore); |
| 592 |
|
|
| 593 |
10652 |
wrk->strangelove = 0; |
| 594 |
10652 |
lr = HSH_Lookup(req, &oc, &busy); |
| 595 |
10652 |
if (lr == HSH_BUSY) { |
| 596 |
|
/* |
| 597 |
|
* We lost the session to a busy object, disembark the |
| 598 |
|
* worker thread. We return to STP_LOOKUP when the busy |
| 599 |
|
* object has been unbusied, and still have the objhead |
| 600 |
|
* around to restart the lookup with. |
| 601 |
|
*/ |
| 602 |
221 |
return (REQ_FSM_DISEMBARK); |
| 603 |
|
} |
| 604 |
10431 |
assert(wrk->strangelove >= 0); |
| 605 |
10431 |
if ((unsigned)wrk->strangelove >= cache_param->vary_notice) |
| 606 |
496 |
VSLb(req->vsl, SLT_Notice, "vsl: High number of variants (%d)", |
| 607 |
248 |
wrk->strangelove); |
| 608 |
10431 |
if (waitinglist_gen) |
| 609 |
221 |
VSLb_ts_req(req, "Waitinglist", W_TIM_real(wrk)); |
| 610 |
|
|
| 611 |
10431 |
if (req->vcf != NULL) { |
| 612 |
24 |
(void)req->vcf->func(req, NULL, NULL, 2); |
| 613 |
24 |
req->vcf = NULL; |
| 614 |
24 |
} |
| 615 |
|
|
| 616 |
10431 |
if (busy == NULL) { |
| 617 |
4344 |
VRY_Finish(req, DISCARD); |
| 618 |
4344 |
} else { |
| 619 |
6087 |
AN(busy->flags & OC_F_BUSY); |
| 620 |
6087 |
VRY_Finish(req, KEEP); |
| 621 |
|
} |
| 622 |
|
|
| 623 |
10431 |
AZ(req->objcore); |
| 624 |
10431 |
if (lr == HSH_MISS || lr == HSH_HITMISS) { |
| 625 |
5711 |
AN(busy); |
| 626 |
5711 |
AN(busy->flags & OC_F_BUSY); |
| 627 |
5711 |
req->objcore = busy; |
| 628 |
5711 |
req->stale_oc = oc; |
| 629 |
5711 |
req->req_step = R_STP_MISS; |
| 630 |
5711 |
if (lr == HSH_HITMISS) |
| 631 |
144 |
req->is_hitmiss = 1; |
| 632 |
5711 |
return (REQ_FSM_MORE); |
| 633 |
|
} |
| 634 |
4720 |
if (lr == HSH_HITPASS) { |
| 635 |
44 |
AZ(busy); |
| 636 |
44 |
AZ(oc); |
| 637 |
44 |
req->req_step = R_STP_PASS; |
| 638 |
44 |
req->is_hitpass = 1; |
| 639 |
44 |
return (REQ_FSM_MORE); |
| 640 |
|
} |
| 641 |
|
|
| 642 |
4676 |
assert(lr == HSH_HIT || lr == HSH_GRACE); |
| 643 |
|
|
| 644 |
4676 |
CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); |
| 645 |
4676 |
AZ(oc->flags & OC_F_BUSY); |
| 646 |
4676 |
req->objcore = oc; |
| 647 |
4676 |
AZ(oc->flags & OC_F_HFM); |
| 648 |
|
|
| 649 |
4676 |
VCL_hit_method(req->vcl, wrk, req, NULL, NULL); |
| 650 |
|
|
| 651 |
4676 |
switch (wrk->vpi->handling) { |
| 652 |
|
case VCL_RET_DELIVER: |
| 653 |
4554 |
if (busy != NULL) { |
| 654 |
348 |
AZ(oc->flags & OC_F_HFM); |
| 655 |
348 |
CHECK_OBJ_NOTNULL(busy->boc, BOC_MAGIC); |
| 656 |
|
// XXX: shouldn't we go to miss? |
| 657 |
348 |
VBF_Fetch(wrk, req, busy, oc, VBF_BACKGROUND); |
| 658 |
348 |
wrk->stats->s_fetch++; |
| 659 |
348 |
wrk->stats->s_bgfetch++; |
| 660 |
348 |
} else { |
| 661 |
4206 |
(void)VRB_Ignore(req);// XXX: handle err |
| 662 |
|
} |
| 663 |
4554 |
wrk->stats->cache_hit++; |
| 664 |
4554 |
req->is_hit = 1; |
| 665 |
4554 |
if (lr == HSH_GRACE) |
| 666 |
360 |
wrk->stats->cache_hit_grace++; |
| 667 |
4554 |
req->req_step = R_STP_DELIVER; |
| 668 |
4554 |
return (REQ_FSM_MORE); |
| 669 |
|
case VCL_RET_RESTART: |
| 670 |
58 |
req->req_step = R_STP_RESTART; |
| 671 |
58 |
break; |
| 672 |
|
case VCL_RET_FAIL: |
| 673 |
20 |
req->req_step = R_STP_VCLFAIL; |
| 674 |
20 |
break; |
| 675 |
|
case VCL_RET_SYNTH: |
| 676 |
36 |
req->req_step = R_STP_SYNTH; |
| 677 |
36 |
break; |
| 678 |
|
case VCL_RET_PASS: |
| 679 |
8 |
wrk->stats->cache_hit++; |
| 680 |
8 |
req->is_hit = 1; |
| 681 |
8 |
req->req_step = R_STP_PASS; |
| 682 |
8 |
break; |
| 683 |
|
default: |
| 684 |
0 |
WRONG("Illegal return from vcl_hit{}"); |
| 685 |
0 |
} |
| 686 |
|
|
| 687 |
|
/* Drop our object, we won't need it */ |
| 688 |
122 |
(void)HSH_DerefObjCore(wrk, &req->objcore); |
| 689 |
|
|
| 690 |
122 |
if (busy != NULL) { |
| 691 |
28 |
HSH_Withdraw(wrk, &busy); |
| 692 |
28 |
VRY_Clear(req); |
| 693 |
28 |
} |
| 694 |
|
|
| 695 |
122 |
return (REQ_FSM_MORE); |
| 696 |
10652 |
} |
| 697 |
|
|
| 698 |
|
/*-------------------------------------------------------------------- |
| 699 |
|
* Cache miss. |
| 700 |
|
*/ |
| 701 |
|
|
| 702 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 703 |
5707 |
cnt_miss(struct worker *wrk, struct req *req) |
| 704 |
|
{ |
| 705 |
|
|
| 706 |
5707 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 707 |
5707 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 708 |
5707 |
AN(req->vcl); |
| 709 |
5707 |
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); |
| 710 |
5707 |
CHECK_OBJ_ORNULL(req->stale_oc, OBJCORE_MAGIC); |
| 711 |
|
|
| 712 |
5707 |
VCL_miss_method(req->vcl, wrk, req, NULL, NULL); |
| 713 |
5707 |
switch (wrk->vpi->handling) { |
| 714 |
|
case VCL_RET_FETCH: |
| 715 |
5632 |
wrk->stats->cache_miss++; |
| 716 |
5632 |
VBF_Fetch(wrk, req, req->objcore, req->stale_oc, VBF_NORMAL); |
| 717 |
5632 |
if (req->stale_oc != NULL) |
| 718 |
284 |
(void)HSH_DerefObjCore(wrk, &req->stale_oc); |
| 719 |
5632 |
req->req_step = R_STP_FETCH; |
| 720 |
5632 |
return (REQ_FSM_MORE); |
| 721 |
|
case VCL_RET_FAIL: |
| 722 |
15 |
req->req_step = R_STP_VCLFAIL; |
| 723 |
15 |
break; |
| 724 |
|
case VCL_RET_SYNTH: |
| 725 |
32 |
req->req_step = R_STP_SYNTH; |
| 726 |
32 |
break; |
| 727 |
|
case VCL_RET_RESTART: |
| 728 |
16 |
req->req_step = R_STP_RESTART; |
| 729 |
16 |
break; |
| 730 |
|
case VCL_RET_PASS: |
| 731 |
12 |
req->req_step = R_STP_PASS; |
| 732 |
12 |
break; |
| 733 |
|
default: |
| 734 |
0 |
WRONG("Illegal return from vcl_miss{}"); |
| 735 |
0 |
} |
| 736 |
75 |
VRY_Clear(req); |
| 737 |
75 |
if (req->stale_oc != NULL) |
| 738 |
4 |
(void)HSH_DerefObjCore(wrk, &req->stale_oc); |
| 739 |
75 |
HSH_Withdraw(wrk, &req->objcore); |
| 740 |
75 |
return (REQ_FSM_MORE); |
| 741 |
5707 |
} |
| 742 |
|
|
| 743 |
|
/*-------------------------------------------------------------------- |
| 744 |
|
* Pass processing |
| 745 |
|
*/ |
| 746 |
|
|
| 747 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 748 |
3148 |
cnt_pass(struct worker *wrk, struct req *req) |
| 749 |
|
{ |
| 750 |
|
|
| 751 |
3148 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 752 |
3148 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 753 |
3148 |
AN(req->vcl); |
| 754 |
3148 |
AZ(req->objcore); |
| 755 |
3148 |
AZ(req->stale_oc); |
| 756 |
|
|
| 757 |
3148 |
VCL_pass_method(req->vcl, wrk, req, NULL, NULL); |
| 758 |
3148 |
switch (wrk->vpi->handling) { |
| 759 |
|
case VCL_RET_FAIL: |
| 760 |
4 |
req->req_step = R_STP_VCLFAIL; |
| 761 |
4 |
break; |
| 762 |
|
case VCL_RET_SYNTH: |
| 763 |
4 |
req->req_step = R_STP_SYNTH; |
| 764 |
4 |
break; |
| 765 |
|
case VCL_RET_RESTART: |
| 766 |
0 |
req->req_step = R_STP_RESTART; |
| 767 |
0 |
break; |
| 768 |
|
case VCL_RET_FETCH: |
| 769 |
3140 |
wrk->stats->s_pass++; |
| 770 |
3140 |
req->objcore = HSH_Private(wrk); |
| 771 |
3140 |
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); |
| 772 |
3140 |
VBF_Fetch(wrk, req, req->objcore, NULL, VBF_PASS); |
| 773 |
3140 |
req->req_step = R_STP_FETCH; |
| 774 |
3140 |
break; |
| 775 |
|
default: |
| 776 |
0 |
WRONG("Illegal return from cnt_pass{}"); |
| 777 |
0 |
} |
| 778 |
3148 |
return (REQ_FSM_MORE); |
| 779 |
|
} |
| 780 |
|
|
| 781 |
|
/*-------------------------------------------------------------------- |
| 782 |
|
* Pipe mode |
| 783 |
|
*/ |
| 784 |
|
|
| 785 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 786 |
120 |
cnt_pipe(struct worker *wrk, struct req *req) |
| 787 |
|
{ |
| 788 |
|
struct busyobj *bo; |
| 789 |
|
enum req_fsm_nxt nxt; |
| 790 |
|
|
| 791 |
120 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 792 |
120 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 793 |
120 |
AZ(req->objcore); |
| 794 |
120 |
AZ(req->stale_oc); |
| 795 |
120 |
AN(req->vcl); |
| 796 |
|
|
| 797 |
120 |
wrk->stats->s_pipe++; |
| 798 |
120 |
bo = VBO_GetBusyObj(wrk, req); |
| 799 |
120 |
CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); |
| 800 |
120 |
VSLb(bo->vsl, SLT_Begin, "bereq %ju pipe", VXID(req->vsl->wid)); |
| 801 |
120 |
VSLb(req->vsl, SLT_Link, "bereq %ju pipe", VXID(bo->vsl->wid)); |
| 802 |
120 |
VSLb_ts_busyobj(bo, "Start", W_TIM_real(wrk)); |
| 803 |
120 |
THR_SetBusyobj(bo); |
| 804 |
120 |
bo->sp = req->sp; |
| 805 |
120 |
SES_Ref(bo->sp); |
| 806 |
|
|
| 807 |
120 |
HTTP_Setup(bo->bereq, req->ws, bo->vsl, SLT_BereqMethod); |
| 808 |
120 |
http_FilterReq(bo->bereq, req->http, 0); // XXX: 0 ? |
| 809 |
120 |
http_PrintfHeader(bo->bereq, "X-Varnish: %ju", VXID(req->vsl->wid)); |
| 810 |
120 |
http_ForceHeader(bo->bereq, H_Connection, "close"); |
| 811 |
|
|
| 812 |
120 |
if (req->want100cont) { |
| 813 |
0 |
http_SetHeader(bo->bereq, "Expect: 100-continue"); |
| 814 |
0 |
req->want100cont = 0; |
| 815 |
0 |
} |
| 816 |
|
|
| 817 |
120 |
bo->wrk = wrk; |
| 818 |
120 |
bo->task_deadline = NAN; /* XXX: copy req->task_deadline */ |
| 819 |
120 |
if (WS_Overflowed(req->ws)) |
| 820 |
4 |
wrk->vpi->handling = VCL_RET_FAIL; |
| 821 |
|
else |
| 822 |
116 |
VCL_pipe_method(req->vcl, wrk, req, bo, NULL); |
| 823 |
|
|
| 824 |
120 |
switch (wrk->vpi->handling) { |
| 825 |
|
case VCL_RET_SYNTH: |
| 826 |
12 |
req->req_step = R_STP_SYNTH; |
| 827 |
12 |
nxt = REQ_FSM_MORE; |
| 828 |
12 |
break; |
| 829 |
|
case VCL_RET_PIPE: |
| 830 |
100 |
VSLb_ts_req(req, "Process", W_TIM_real(wrk)); |
| 831 |
100 |
VSLb_ts_busyobj(bo, "Process", wrk->lastused); |
| 832 |
100 |
if (V1P_Enter() == 0) { |
| 833 |
96 |
AZ(bo->req); |
| 834 |
96 |
bo->req = req; |
| 835 |
96 |
bo->wrk = wrk; |
| 836 |
|
/* Unless cached, reqbody is not our job */ |
| 837 |
96 |
if (req->req_body_status != BS_CACHED) |
| 838 |
92 |
req->req_body_status = BS_NONE; |
| 839 |
96 |
SES_Close(req->sp, VDI_Http1Pipe(req, bo)); |
| 840 |
96 |
nxt = REQ_FSM_DONE; |
| 841 |
96 |
V1P_Leave(); |
| 842 |
96 |
break; |
| 843 |
|
} |
| 844 |
4 |
wrk->stats->pipe_limited++; |
| 845 |
|
/* fall through */ |
| 846 |
|
case VCL_RET_FAIL: |
| 847 |
12 |
req->req_step = R_STP_VCLFAIL; |
| 848 |
12 |
nxt = REQ_FSM_MORE; |
| 849 |
12 |
break; |
| 850 |
|
default: |
| 851 |
0 |
WRONG("Illegal return from vcl_pipe{}"); |
| 852 |
0 |
} |
| 853 |
120 |
http_Teardown(bo->bereq); |
| 854 |
120 |
SES_Rel(bo->sp); |
| 855 |
120 |
VBO_ReleaseBusyObj(wrk, &bo); |
| 856 |
120 |
THR_SetBusyobj(NULL); |
| 857 |
120 |
return (nxt); |
| 858 |
|
} |
| 859 |
|
|
| 860 |
|
/*-------------------------------------------------------------------- |
| 861 |
|
* Handle restart events |
| 862 |
|
*/ |
| 863 |
|
|
| 864 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 865 |
290 |
cnt_restart(struct worker *wrk, struct req *req) |
| 866 |
|
{ |
| 867 |
|
|
| 868 |
290 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 869 |
290 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 870 |
290 |
AZ(req->objcore); |
| 871 |
290 |
AZ(req->stale_oc); |
| 872 |
|
|
| 873 |
290 |
if (++req->restarts > req->max_restarts) { |
| 874 |
40 |
VSLb(req->vsl, SLT_VCL_Error, "Too many restarts"); |
| 875 |
40 |
req->err_code = 503; |
| 876 |
40 |
req->req_step = R_STP_SYNTH; |
| 877 |
40 |
} else { |
| 878 |
|
// XXX: ReqEnd + ReqAcct ? |
| 879 |
250 |
VSLb_ts_req(req, "Restart", W_TIM_real(wrk)); |
| 880 |
500 |
VSL_ChgId(req->vsl, "req", "restart", |
| 881 |
250 |
VXID_Get(wrk, VSL_CLIENTMARKER)); |
| 882 |
250 |
VSLb_ts_req(req, "Start", req->t_prev); |
| 883 |
250 |
req->err_code = 0; |
| 884 |
250 |
req->req_step = R_STP_RECV; |
| 885 |
|
} |
| 886 |
290 |
return (REQ_FSM_MORE); |
| 887 |
|
} |
| 888 |
|
|
| 889 |
|
/* |
| 890 |
|
* prepare the request for vcl_recv, either initially or after a reset |
| 891 |
|
* e.g. due to vcl switching |
| 892 |
|
* |
| 893 |
|
* TODO |
| 894 |
|
* - make restarts == 0 bit re-usable for rollback |
| 895 |
|
* - remove duplication with Req_Cleanup() |
| 896 |
|
*/ |
| 897 |
|
|
| 898 |
|
static void v_matchproto_(req_state_f) |
| 899 |
15338 |
cnt_recv_prep(struct req *req, const char *ci) |
| 900 |
|
{ |
| 901 |
|
|
| 902 |
15338 |
if (req->restarts == 0) { |
| 903 |
|
/* |
| 904 |
|
* This really should be done earlier, but we want to capture |
| 905 |
|
* it in the VSL log. |
| 906 |
|
*/ |
| 907 |
15088 |
http_AppendHeader(req->http, H_X_Forwarded_For, ci); |
| 908 |
15088 |
http_AppendHeader(req->http, H_Via, http_ViaHeader()); |
| 909 |
15088 |
http_CollectHdr(req->http, H_Cache_Control); |
| 910 |
|
|
| 911 |
|
/* By default we use the first backend */ |
| 912 |
30176 |
VRT_Assign_Backend(&req->director_hint, |
| 913 |
15088 |
VCL_DefaultDirector(req->vcl)); |
| 914 |
|
|
| 915 |
15088 |
req->d_ttl = -1; |
| 916 |
15088 |
req->d_grace = -1; |
| 917 |
15088 |
req->disable_esi = 0; |
| 918 |
15088 |
req->hash_always_miss = 0; |
| 919 |
15088 |
req->hash_ignore_busy = 0; |
| 920 |
15088 |
req->hash_ignore_vary = 0; |
| 921 |
15088 |
req->client_identity = NULL; |
| 922 |
15088 |
req->storage = NULL; |
| 923 |
15088 |
req->trace = FEATURE(FEATURE_TRACE); |
| 924 |
15088 |
} |
| 925 |
|
|
| 926 |
15338 |
req->is_hit = 0; |
| 927 |
15338 |
req->is_hitmiss = 0; |
| 928 |
15338 |
req->is_hitpass = 0; |
| 929 |
15338 |
req->err_code = 0; |
| 930 |
15338 |
req->err_reason = NULL; |
| 931 |
|
|
| 932 |
15338 |
req->vfp_filter_list = NULL; |
| 933 |
15338 |
} |
| 934 |
|
|
| 935 |
|
/*-------------------------------------------------------------------- |
| 936 |
|
* We have a complete request, set everything up and start it. |
| 937 |
|
* We can come here both with a request from the client and with |
| 938 |
|
* a interior request during ESI delivery. |
| 939 |
|
*/ |
| 940 |
|
|
| 941 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 942 |
15278 |
cnt_recv(struct worker *wrk, struct req *req) |
| 943 |
|
{ |
| 944 |
|
unsigned recv_handling; |
| 945 |
|
struct VSHA256Context sha256ctx; |
| 946 |
|
const char *ci; |
| 947 |
|
|
| 948 |
15278 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 949 |
15278 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 950 |
15278 |
AN(req->vcl); |
| 951 |
15278 |
AZ(req->objcore); |
| 952 |
15278 |
AZ(req->stale_oc); |
| 953 |
15278 |
AZ(req->err_code); |
| 954 |
|
|
| 955 |
15278 |
AZ(isnan(req->t_first)); |
| 956 |
15278 |
AZ(isnan(req->t_prev)); |
| 957 |
15278 |
AZ(isnan(req->t_req)); |
| 958 |
|
|
| 959 |
15278 |
ci = Req_LogStart(wrk, req); |
| 960 |
15278 |
http_VSL_log(req->http); |
| 961 |
|
|
| 962 |
15278 |
if (http_CountHdr(req->http0, H_Host) > 1) { |
| 963 |
8 |
VSLb(req->vsl, SLT_BogoHeader, "Multiple Host: headers"); |
| 964 |
8 |
wrk->stats->client_req_400++; |
| 965 |
8 |
req->doclose = SC_RX_BAD; |
| 966 |
8 |
(void)req->transport->minimal_response(req, 400); |
| 967 |
8 |
return (REQ_FSM_DONE); |
| 968 |
|
} |
| 969 |
|
|
| 970 |
15270 |
if (http_CountHdr(req->http0, H_Content_Length) > 1) { |
| 971 |
4 |
VSLb(req->vsl, SLT_BogoHeader, "Multiple Content-Length: headers"); |
| 972 |
4 |
wrk->stats->client_req_400++; |
| 973 |
4 |
req->doclose = SC_RX_BAD; |
| 974 |
4 |
(void)req->transport->minimal_response(req, 400); |
| 975 |
4 |
return (REQ_FSM_DONE); |
| 976 |
|
} |
| 977 |
|
|
| 978 |
15266 |
cnt_recv_prep(req, ci); |
| 979 |
|
|
| 980 |
15266 |
if (req->req_body_status == BS_ERROR) { |
| 981 |
0 |
req->doclose = SC_OVERLOAD; |
| 982 |
0 |
return (REQ_FSM_DONE); |
| 983 |
|
} |
| 984 |
|
|
| 985 |
15266 |
VCL_recv_method(req->vcl, wrk, req, NULL, NULL); |
| 986 |
|
|
| 987 |
15266 |
if (wrk->vpi->handling == VCL_RET_FAIL) { |
| 988 |
188 |
req->req_step = R_STP_VCLFAIL; |
| 989 |
188 |
return (REQ_FSM_MORE); |
| 990 |
|
} |
| 991 |
|
|
| 992 |
15078 |
if (wrk->vpi->handling == VCL_RET_VCL && req->restarts == 0) { |
| 993 |
|
// Req_Rollback has happened in VPI_vcl_select |
| 994 |
52 |
assert(WS_Snapshot(req->ws) == req->ws_req); |
| 995 |
52 |
cnt_recv_prep(req, ci); |
| 996 |
52 |
VCL_recv_method(req->vcl, wrk, req, NULL, NULL); |
| 997 |
52 |
} |
| 998 |
|
|
| 999 |
15078 |
if (req->want100cont && !req->late100cont) { |
| 1000 |
16 |
req->want100cont = 0; |
| 1001 |
16 |
if (req->transport->minimal_response(req, 100)) { |
| 1002 |
0 |
req->doclose = SC_REM_CLOSE; |
| 1003 |
0 |
return (REQ_FSM_DONE); |
| 1004 |
|
} |
| 1005 |
16 |
} |
| 1006 |
|
|
| 1007 |
|
/* Attempts to cache req.body may fail */ |
| 1008 |
15078 |
if (req->req_body_status == BS_ERROR) { |
| 1009 |
20 |
req->doclose = SC_RX_BODY; |
| 1010 |
20 |
(void)req->transport->minimal_response(req, 400); |
| 1011 |
20 |
return (REQ_FSM_DONE); |
| 1012 |
|
} |
| 1013 |
|
|
| 1014 |
15058 |
recv_handling = wrk->vpi->handling; |
| 1015 |
|
|
| 1016 |
|
/* We wash the A-E header here for the sake of VRY */ |
| 1017 |
29971 |
if (cache_param->http_gzip_support && |
| 1018 |
15041 |
(recv_handling != VCL_RET_PIPE) && |
| 1019 |
14913 |
(recv_handling != VCL_RET_PASS)) { |
| 1020 |
11834 |
if (RFC2616_Req_Gzip(req->http)) { |
| 1021 |
880 |
http_ForceHeader(req->http, H_Accept_Encoding, "gzip"); |
| 1022 |
880 |
} else { |
| 1023 |
10954 |
http_Unset(req->http, H_Accept_Encoding); |
| 1024 |
|
} |
| 1025 |
11834 |
} |
| 1026 |
|
|
| 1027 |
15058 |
VSHA256_Init(&sha256ctx); |
| 1028 |
15058 |
VCL_hash_method(req->vcl, wrk, req, NULL, &sha256ctx); |
| 1029 |
15058 |
if (wrk->vpi->handling == VCL_RET_FAIL) |
| 1030 |
37 |
recv_handling = wrk->vpi->handling; |
| 1031 |
|
else |
| 1032 |
15021 |
assert(wrk->vpi->handling == VCL_RET_LOOKUP); |
| 1033 |
15058 |
VSHA256_Final(req->digest, &sha256ctx); |
| 1034 |
|
|
| 1035 |
15058 |
switch (recv_handling) { |
| 1036 |
|
case VCL_RET_VCL: |
| 1037 |
32 |
VSLb(req->vsl, SLT_VCL_Error, |
| 1038 |
|
"Illegal return(vcl): %s", |
| 1039 |
16 |
req->restarts ? "Not after restarts" : |
| 1040 |
|
"Only from active VCL"); |
| 1041 |
16 |
req->err_code = 503; |
| 1042 |
16 |
req->req_step = R_STP_SYNTH; |
| 1043 |
16 |
break; |
| 1044 |
|
case VCL_RET_PURGE: |
| 1045 |
40 |
req->req_step = R_STP_PURGE; |
| 1046 |
40 |
break; |
| 1047 |
|
case VCL_RET_HASH: |
| 1048 |
10428 |
req->req_step = R_STP_LOOKUP; |
| 1049 |
10428 |
break; |
| 1050 |
|
case VCL_RET_PIPE: |
| 1051 |
128 |
if (!IS_TOPREQ(req)) { |
| 1052 |
0 |
VSLb(req->vsl, SLT_VCL_Error, |
| 1053 |
|
"vcl_recv{} returns pipe for ESI included object." |
| 1054 |
|
" Doing pass."); |
| 1055 |
0 |
req->req_step = R_STP_PASS; |
| 1056 |
128 |
} else if (req->http0->protover > 11) { |
| 1057 |
8 |
VSLb(req->vsl, SLT_VCL_Error, |
| 1058 |
|
"vcl_recv{} returns pipe for HTTP/2 request." |
| 1059 |
|
" Doing pass."); |
| 1060 |
8 |
req->req_step = R_STP_PASS; |
| 1061 |
8 |
} else { |
| 1062 |
120 |
req->req_step = R_STP_PIPE; |
| 1063 |
|
} |
| 1064 |
128 |
break; |
| 1065 |
|
case VCL_RET_PASS: |
| 1066 |
3075 |
req->req_step = R_STP_PASS; |
| 1067 |
3075 |
break; |
| 1068 |
|
case VCL_RET_SYNTH: |
| 1069 |
1296 |
req->req_step = R_STP_SYNTH; |
| 1070 |
1296 |
break; |
| 1071 |
|
case VCL_RET_RESTART: |
| 1072 |
36 |
req->req_step = R_STP_RESTART; |
| 1073 |
36 |
break; |
| 1074 |
|
case VCL_RET_FAIL: |
| 1075 |
39 |
req->req_step = R_STP_VCLFAIL; |
| 1076 |
39 |
break; |
| 1077 |
|
default: |
| 1078 |
0 |
WRONG("Illegal return from vcl_recv{}"); |
| 1079 |
0 |
} |
| 1080 |
15058 |
return (REQ_FSM_MORE); |
| 1081 |
15278 |
} |
| 1082 |
|
|
| 1083 |
|
/*-------------------------------------------------------------------- |
| 1084 |
|
* Find the objhead, purge it. |
| 1085 |
|
* |
| 1086 |
|
* In VCL, a restart is necessary to get a new object |
| 1087 |
|
*/ |
| 1088 |
|
|
| 1089 |
|
static enum req_fsm_nxt v_matchproto_(req_state_f) |
| 1090 |
40 |
cnt_purge(struct worker *wrk, struct req *req) |
| 1091 |
|
{ |
| 1092 |
|
struct objcore *oc, *boc; |
| 1093 |
|
enum lookup_e lr; |
| 1094 |
|
|
| 1095 |
40 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 1096 |
40 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 1097 |
40 |
AZ(req->objcore); |
| 1098 |
40 |
AZ(req->stale_oc); |
| 1099 |
|
|
| 1100 |
40 |
AN(req->vcl); |
| 1101 |
|
|
| 1102 |
40 |
VRY_Prep(req); |
| 1103 |
|
|
| 1104 |
40 |
AZ(req->objcore); |
| 1105 |
40 |
req->hash_always_miss = 1; |
| 1106 |
40 |
lr = HSH_Lookup(req, &oc, &boc); |
| 1107 |
40 |
assert (lr == HSH_MISS); |
| 1108 |
40 |
AZ(oc); |
| 1109 |
40 |
CHECK_OBJ_NOTNULL(boc, OBJCORE_MAGIC); |
| 1110 |
40 |
VRY_Finish(req, DISCARD); |
| 1111 |
|
|
| 1112 |
40 |
(void)HSH_Purge(wrk, boc->objhead, req->t_req, 0, 0, 0); |
| 1113 |
|
|
| 1114 |
40 |
HSH_Withdraw(wrk, &boc); |
| 1115 |
|
|
| 1116 |
40 |
VCL_purge_method(req->vcl, wrk, req, NULL, NULL); |
| 1117 |
40 |
switch (wrk->vpi->handling) { |
| 1118 |
|
case VCL_RET_RESTART: |
| 1119 |
12 |
req->req_step = R_STP_RESTART; |
| 1120 |
12 |
break; |
| 1121 |
|
case VCL_RET_FAIL: |
| 1122 |
4 |
req->req_step = R_STP_VCLFAIL; |
| 1123 |
4 |
break; |
| 1124 |
|
case VCL_RET_SYNTH: |
| 1125 |
24 |
req->req_step = R_STP_SYNTH; |
| 1126 |
24 |
break; |
| 1127 |
|
default: |
| 1128 |
0 |
WRONG("Illegal return from vcl_purge{}"); |
| 1129 |
0 |
} |
| 1130 |
40 |
return (REQ_FSM_MORE); |
| 1131 |
|
} |
| 1132 |
|
|
| 1133 |
|
/*-------------------------------------------------------------------- |
| 1134 |
|
* Central state engine dispatcher. |
| 1135 |
|
* |
| 1136 |
|
* Kick the session around until it has had enough. |
| 1137 |
|
* |
| 1138 |
|
*/ |
| 1139 |
|
|
| 1140 |
|
static void v_matchproto_(req_state_f) |
| 1141 |
1787 |
cnt_diag(struct req *req, const char *state) |
| 1142 |
|
{ |
| 1143 |
|
|
| 1144 |
1787 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 1145 |
|
|
| 1146 |
3574 |
VSLb(req->vsl, SLT_Debug, "vxid %ju STP_%s sp %p vcl %p", |
| 1147 |
1787 |
VXID(req->vsl->wid), state, req->sp, req->vcl); |
| 1148 |
1787 |
VSL_Flush(req->vsl, 0); |
| 1149 |
1787 |
} |
| 1150 |
|
|
| 1151 |
|
void |
| 1152 |
15866 |
CNT_Embark(struct worker *wrk, struct req *req) |
| 1153 |
|
{ |
| 1154 |
|
|
| 1155 |
15866 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 1156 |
15866 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 1157 |
|
|
| 1158 |
|
/* wrk can have changed for restarts */ |
| 1159 |
15866 |
req->vfc->wrk = req->wrk = wrk; |
| 1160 |
15866 |
wrk->vsl = req->vsl; |
| 1161 |
15866 |
if (req->req_step == R_STP_TRANSPORT && req->vcl == NULL) { |
| 1162 |
13601 |
VCL_Refresh(&wrk->wpriv->vcl); |
| 1163 |
13601 |
req->vcl = wrk->wpriv->vcl; |
| 1164 |
13601 |
wrk->wpriv->vcl = NULL; |
| 1165 |
13601 |
VSLbs(req->vsl, SLT_VCL_use, TOSTRAND(VCL_Name(req->vcl))); |
| 1166 |
13601 |
} |
| 1167 |
|
|
| 1168 |
15866 |
AN(req->vcl); |
| 1169 |
15866 |
} |
| 1170 |
|
|
| 1171 |
|
enum req_fsm_nxt |
| 1172 |
15578 |
CNT_Request(struct req *req) |
| 1173 |
|
{ |
| 1174 |
|
struct vrt_ctx ctx[1]; |
| 1175 |
|
struct worker *wrk; |
| 1176 |
|
enum req_fsm_nxt nxt; |
| 1177 |
|
|
| 1178 |
15578 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 1179 |
|
|
| 1180 |
15578 |
wrk = req->wrk; |
| 1181 |
15578 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 1182 |
|
|
| 1183 |
15578 |
CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC); |
| 1184 |
15578 |
AN(req->transport->deliver); |
| 1185 |
15578 |
AN(req->transport->minimal_response); |
| 1186 |
|
|
| 1187 |
|
/* |
| 1188 |
|
* Possible entrance states |
| 1189 |
|
*/ |
| 1190 |
15578 |
assert( |
| 1191 |
|
req->req_step == R_STP_LOOKUP || |
| 1192 |
|
req->req_step == R_STP_FINISH || |
| 1193 |
|
req->req_step == R_STP_TRANSPORT); |
| 1194 |
|
|
| 1195 |
15578 |
AN(VXID_TAG(req->vsl->wid) & VSL_CLIENTMARKER); |
| 1196 |
15578 |
AN(req->vcl); |
| 1197 |
|
|
| 1198 |
120294 |
for (nxt = REQ_FSM_MORE; nxt == REQ_FSM_MORE; ) { |
| 1199 |
|
/* |
| 1200 |
|
* This is a good place to be paranoid about the various |
| 1201 |
|
* pointers still pointing to the things we expect. |
| 1202 |
|
*/ |
| 1203 |
104716 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 1204 |
104716 |
CHECK_OBJ_NOTNULL(wrk->wpriv, WORKER_PRIV_MAGIC); |
| 1205 |
104716 |
CHECK_OBJ_ORNULL(wrk->wpriv->nobjhead, OBJHEAD_MAGIC); |
| 1206 |
104716 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
| 1207 |
104716 |
CHECK_OBJ_NOTNULL(req->doclose, STREAM_CLOSE_MAGIC); |
| 1208 |
|
|
| 1209 |
104716 |
AN(req->req_step); |
| 1210 |
104716 |
AN(req->req_step->name); |
| 1211 |
104716 |
AN(req->req_step->func); |
| 1212 |
104716 |
if (DO_DEBUG(DBG_REQ_STATE)) |
| 1213 |
1787 |
cnt_diag(req, req->req_step->name); |
| 1214 |
104716 |
nxt = req->req_step->func(wrk, req); |
| 1215 |
104716 |
CHECK_OBJ_ORNULL(wrk->wpriv->nobjhead, OBJHEAD_MAGIC); |
| 1216 |
|
} |
| 1217 |
15578 |
wrk->vsl = NULL; |
| 1218 |
15578 |
if (nxt == REQ_FSM_DONE) { |
| 1219 |
15038 |
INIT_OBJ(ctx, VRT_CTX_MAGIC); |
| 1220 |
15038 |
VCL_Req2Ctx(ctx, req); |
| 1221 |
15038 |
if (IS_TOPREQ(req)) { |
| 1222 |
13574 |
VCL_TaskLeave(ctx, req->top->privs); |
| 1223 |
13574 |
if (req->top->vcl0 != NULL) |
| 1224 |
48 |
VCL_Recache(wrk, &req->top->vcl0); |
| 1225 |
13574 |
} |
| 1226 |
15038 |
VCL_TaskLeave(ctx, req->privs); |
| 1227 |
15038 |
assert(!IS_NO_VXID(req->vsl->wid)); |
| 1228 |
15038 |
VRB_Free(req); |
| 1229 |
15038 |
VRT_Assign_Backend(&req->director_hint, NULL); |
| 1230 |
15038 |
req->wrk = NULL; |
| 1231 |
15038 |
} |
| 1232 |
15578 |
assert(nxt == REQ_FSM_DISEMBARK || !WS_IsReserved(req->ws)); |
| 1233 |
15578 |
return (nxt); |
| 1234 |
|
} |