]>
Commit | Line | Data |
---|---|---|
924f73bc | 1 | /* |
bbc27441 | 2 | * Copyright (C) 1996-2014 The Squid Software Foundation and contributors |
26ac0430 | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
924f73bc | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 86 ESI processing */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
454e8283 | 12 | |
13 | /* MS Visual Studio Projects are monolithic, so we need the following | |
14 | * #if to exclude the ESI code from compile process when not needed. | |
15 | */ | |
16 | #if (USE_SQUID_ESI == 1) | |
17 | ||
93da1f99 | 18 | #include "client_side.h" |
602d9612 | 19 | #include "client_side_request.h" |
f99c2cfe AR |
20 | #include "esi/Include.h" |
21 | #include "esi/VarState.h" | |
924f73bc | 22 | #include "HttpReply.h" |
1c7ae5ff | 23 | #include "log/access_log.h" |
924f73bc | 24 | |
25 | CBDATA_CLASS_INIT (ESIStreamContext); | |
26 | ||
924f73bc | 27 | /* other */ |
28 | static CSCB esiBufferRecipient; | |
29 | static CSD esiBufferDetach; | |
30 | /* esiStreamContext */ | |
31 | static ESIStreamContext *ESIStreamContextNew (ESIIncludePtr); | |
32 | ||
33 | /* ESI TO CONSIDER: | |
34 | * 1. retry failed upstream requests | |
35 | */ | |
36 | ||
37 | /* Detach from a buffering stream | |
38 | */ | |
39 | void | |
59a1efb2 | 40 | esiBufferDetach (clientStreamNode *node, ClientHttpRequest *http) |
924f73bc | 41 | { |
42 | /* Detach ourselves */ | |
43 | clientStreamDetach (node, http); | |
44 | } | |
45 | ||
24cf4917 AJ |
46 | /** |
47 | * Write a chunk of data to a client 'socket'. | |
48 | * If the reply is present, send the reply headers down the wire too. | |
49 | * | |
50 | * Pre-condition: | |
924f73bc | 51 | * The request is an internal ESI subrequest. |
52 | * data context is not NULL | |
53 | * There are no more entries in the stream chain. | |
24cf4917 | 54 | * The caller is responsible for creation and deletion of the Reply headers. |
26ac0430 | 55 | * |
24cf4917 AJ |
56 | \note |
57 | * Bug 975, bug 1566 : delete rep; 2006/09/02: TS, #975 | |
26ac0430 | 58 | * |
24cf4917 AJ |
59 | * This was causing double-deletes. Its possible that not deleting |
60 | * it here will cause memory leaks, but if so, this delete should | |
61 | * not be reinstated or it will trigger bug #975 again - RBC 20060903 | |
924f73bc | 62 | */ |
63 | void | |
2324cda2 | 64 | esiBufferRecipient (clientStreamNode *node, ClientHttpRequest *http, HttpReply *rep, StoreIOBuffer receivedData) |
924f73bc | 65 | { |
66 | /* Test preconditions */ | |
67 | assert (node != NULL); | |
68 | /* ESI TODO: handle thisNode rather than asserting | |
26ac0430 AJ |
69 | * - it should only ever happen if we cause an |
70 | * abort and the callback chain loops back to | |
71 | * here, so we can simply return. However, that | |
72 | * itself shouldn't happen, so it stays as an | |
924f73bc | 73 | * assert for now. */ |
74 | assert (cbdataReferenceValid (node)); | |
75 | assert (node->node.next == NULL); | |
9bd63d11 | 76 | assert (http->getConn() == NULL); |
924f73bc | 77 | |
78 | ESIStreamContext::Pointer esiStream = dynamic_cast<ESIStreamContext *>(node->data.getRaw()); | |
79 | assert (esiStream.getRaw() != NULL); | |
80 | /* If segments become more flexible, ignore thisNode */ | |
2324cda2 | 81 | assert (receivedData.length <= sizeof(esiStream->localbuffer->buf)); |
924f73bc | 82 | assert (!esiStream->finished); |
83 | ||
24cf4917 | 84 | debugs (86,5, HERE << "rep " << rep << " body " << receivedData.data << " len " << receivedData.length); |
2324cda2 | 85 | assert (node->readBuffer.offset == receivedData.offset || receivedData.length == 0); |
924f73bc | 86 | |
87 | /* trivial case */ | |
88 | ||
89 | if (http->out.offset != 0) { | |
90 | assert(rep == NULL); | |
91 | } else { | |
92 | if (rep) { | |
9b769c67 | 93 | if (rep->sline.status() != Http::scOkay) { |
924f73bc | 94 | rep = NULL; |
59a09b98 | 95 | esiStream->include->includeFail (esiStream); |
924f73bc | 96 | esiStream->finished = 1; |
97 | httpRequestFree (http); | |
98 | return; | |
99 | } | |
100 | ||
101 | #if HEADERS_LOG | |
102 | /* should be done in the store rather than every recipient? */ | |
103 | headersLog(0, 0, http->request->method, rep); | |
104 | ||
105 | #endif | |
924f73bc | 106 | rep = NULL; |
107 | } | |
108 | } | |
109 | ||
2324cda2 | 110 | if (receivedData.data && receivedData.length) { |
111 | http->out.offset += receivedData.length; | |
924f73bc | 112 | |
2324cda2 | 113 | if (receivedData.data >= esiStream->localbuffer->buf && |
114 | receivedData.data < &esiStream->localbuffer->buf[sizeof(esiStream->localbuffer->buf)]) { | |
924f73bc | 115 | /* original static buffer */ |
116 | ||
2324cda2 | 117 | if (receivedData.data != esiStream->localbuffer->buf) { |
924f73bc | 118 | /* But not the start of it */ |
41d00cd3 | 119 | memmove(esiStream->localbuffer->buf, receivedData.data, receivedData.length); |
924f73bc | 120 | } |
121 | ||
2324cda2 | 122 | esiStream->localbuffer->len = receivedData.length; |
924f73bc | 123 | } else { |
124 | assert (esiStream->buffer.getRaw() != NULL); | |
2324cda2 | 125 | esiStream->buffer->len = receivedData.length; |
924f73bc | 126 | } |
127 | } | |
128 | ||
129 | /* EOF / Read error / aborted entry */ | |
2324cda2 | 130 | if (rep == NULL && receivedData.data == NULL && receivedData.length == 0) { |
924f73bc | 131 | /* TODO: get stream status to test the entry for aborts */ |
24cf4917 | 132 | debugs(86, 5, HERE << "Finished reading upstream data in subrequest"); |
924f73bc | 133 | esiStream->include->subRequestDone (esiStream, true); |
134 | esiStream->finished = 1; | |
135 | httpRequestFree (http); | |
136 | return; | |
137 | } | |
138 | ||
924f73bc | 139 | /* after the write to the user occurs, (ie here, or in a callback) |
140 | * we call */ | |
141 | if (clientHttpRequestStatus(-1, http)) { | |
142 | /* TODO: Does thisNode if block leak htto ? */ | |
143 | /* XXX when reviewing ESI this is the first place to look */ | |
144 | node->data = NULL; | |
145 | esiStream->finished = 1; | |
59a09b98 | 146 | esiStream->include->includeFail (esiStream); |
924f73bc | 147 | return; |
148 | }; | |
149 | ||
150 | switch (clientStreamStatus (node, http)) { | |
151 | ||
152 | case STREAM_UNPLANNED_COMPLETE: /* fallthru ok */ | |
153 | ||
154 | case STREAM_COMPLETE: /* ok */ | |
bf8fe701 | 155 | debugs(86, 3, "ESI subrequest finished OK"); |
924f73bc | 156 | esiStream->include->subRequestDone (esiStream, true); |
157 | esiStream->finished = 1; | |
158 | httpRequestFree (http); | |
159 | return; | |
160 | ||
161 | case STREAM_FAILED: | |
e0236918 | 162 | debugs(86, DBG_IMPORTANT, "ESI subrequest failed transfer"); |
59a09b98 | 163 | esiStream->include->includeFail (esiStream); |
924f73bc | 164 | esiStream->finished = 1; |
165 | httpRequestFree (http); | |
166 | return; | |
167 | ||
168 | case STREAM_NONE: { | |
26ac0430 | 169 | StoreIOBuffer tempBuffer; |
924f73bc | 170 | |
26ac0430 AJ |
171 | if (!esiStream->buffer.getRaw()) { |
172 | esiStream->buffer = esiStream->localbuffer; | |
173 | } | |
924f73bc | 174 | |
26ac0430 | 175 | esiStream->buffer = esiStream->buffer->tail(); |
924f73bc | 176 | |
26ac0430 AJ |
177 | if (esiStream->buffer->len) { |
178 | esiStream->buffer->next = new ESISegment; | |
179 | esiStream->buffer = esiStream->buffer->next; | |
924f73bc | 180 | } |
181 | ||
26ac0430 AJ |
182 | tempBuffer.offset = http->out.offset; |
183 | tempBuffer.length = sizeof (esiStream->buffer->buf); | |
184 | tempBuffer.data = esiStream->buffer->buf; | |
185 | /* now just read into 'buffer' */ | |
186 | clientStreamRead (node, http, tempBuffer); | |
187 | debugs(86, 5, HERE << "Requested more data for ESI subrequest"); | |
188 | } | |
189 | ||
190 | break; | |
924f73bc | 191 | |
192 | default: | |
193 | fatal ("Hit unreachable code in esiBufferRecipient\n"); | |
194 | } | |
195 | ||
196 | } | |
197 | ||
198 | /* esiStream functions */ | |
199 | ESIStreamContext::~ESIStreamContext() | |
200 | { | |
201 | assert (this); | |
202 | freeResources(); | |
203 | } | |
204 | ||
205 | void | |
206 | ESIStreamContext::freeResources() | |
207 | { | |
bf8fe701 | 208 | debugs(86, 5, "Freeing stream context resources."); |
924f73bc | 209 | buffer = NULL; |
210 | localbuffer = NULL; | |
211 | include = NULL; | |
212 | } | |
213 | ||
924f73bc | 214 | ESIStreamContext * |
215 | ESIStreamContextNew (ESIIncludePtr include) | |
216 | { | |
217 | ESIStreamContext *rv = new ESIStreamContext; | |
218 | rv->include = include; | |
219 | return rv; | |
220 | } | |
221 | ||
924f73bc | 222 | /* ESIInclude */ |
223 | ESIInclude::~ESIInclude() | |
224 | { | |
bf8fe701 | 225 | debugs(86, 5, "ESIInclude::Free " << this); |
924f73bc | 226 | ESISegmentFreeList (srccontent); |
227 | ESISegmentFreeList (altcontent); | |
228 | cbdataReferenceDone (varState); | |
229 | safe_free (srcurl); | |
230 | safe_free (alturl); | |
231 | } | |
232 | ||
233 | void | |
234 | ESIInclude::finish() | |
235 | { | |
236 | parent = NULL; | |
237 | } | |
238 | ||
924f73bc | 239 | ESIElement::Pointer |
240 | ESIInclude::makeCacheable() const | |
241 | { | |
242 | return new ESIInclude (*this); | |
243 | } | |
244 | ||
245 | ESIElement::Pointer | |
246 | ESIInclude::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const | |
247 | { | |
248 | ESIInclude *resultI = new ESIInclude (*this); | |
249 | ESIElement::Pointer result = resultI; | |
250 | resultI->parent = newParent; | |
251 | resultI->varState = cbdataReference (&newVarState); | |
252 | ||
253 | if (resultI->srcurl) | |
254 | resultI->src = ESIStreamContextNew (resultI); | |
255 | ||
256 | if (resultI->alturl) | |
257 | resultI->alt = ESIStreamContextNew (resultI); | |
258 | ||
259 | return result; | |
260 | } | |
261 | ||
a5488e5d | 262 | ESIInclude::ESIInclude(ESIInclude const &old) : |
a5488e5d AJ |
263 | varState(NULL), |
264 | srcurl(NULL), | |
265 | alturl(NULL), | |
429202a2 | 266 | parent(NULL), |
a5488e5d AJ |
267 | started(false), |
268 | sent(false) | |
924f73bc | 269 | { |
a5488e5d | 270 | memset(&flags, 0, sizeof(flags)); |
924f73bc | 271 | flags.onerrorcontinue = old.flags.onerrorcontinue; |
272 | ||
273 | if (old.srcurl) | |
86c63190 | 274 | srcurl = xstrdup(old.srcurl); |
924f73bc | 275 | |
276 | if (old.alturl) | |
86c63190 | 277 | alturl = xstrdup(old.alturl); |
924f73bc | 278 | } |
279 | ||
280 | void | |
281 | ESIInclude::prepareRequestHeaders(HttpHeader &tempheaders, ESIVarState *vars) | |
282 | { | |
924f73bc | 283 | tempheaders.update (&vars->header(), NULL); |
3b6b6092 | 284 | tempheaders.removeHopByHopEntries(); |
924f73bc | 285 | } |
286 | ||
924f73bc | 287 | void |
288 | ESIInclude::Start (ESIStreamContext::Pointer stream, char const *url, ESIVarState *vars) | |
289 | { | |
924f73bc | 290 | if (!stream.getRaw()) |
291 | return; | |
292 | ||
818c6c9e | 293 | HttpHeader tempheaders(hoRequest); |
294 | ||
924f73bc | 295 | prepareRequestHeaders(tempheaders, vars); |
296 | ||
297 | /* Ensure variable state is clean */ | |
298 | vars->feedData(url, strlen (url)); | |
299 | ||
300 | /* tempUrl is eaten by the request */ | |
301 | char const *tempUrl = vars->extractChar (); | |
302 | ||
c2a7cefd | 303 | debugs(86, 5, "ESIIncludeStart: Starting subrequest with url '" << tempUrl << "'"); |
924f73bc | 304 | |
c2a7cefd | 305 | if (clientBeginRequest(Http::METHOD_GET, tempUrl, esiBufferRecipient, esiBufferDetach, stream.getRaw(), &tempheaders, stream->localbuffer->buf, HTTP_REQBUF_SZ)) { |
fa84c01d | 306 | debugs(86, DBG_CRITICAL, "starting new ESI subrequest failed"); |
924f73bc | 307 | } |
308 | ||
519e0948 | 309 | tempheaders.clean(); |
924f73bc | 310 | } |
311 | ||
a5488e5d | 312 | ESIInclude::ESIInclude(esiTreeParentPtr aParent, int attrcount, char const **attr, ESIContext *aContext) : |
a5488e5d AJ |
313 | varState(NULL), |
314 | srcurl(NULL), | |
315 | alturl(NULL), | |
429202a2 | 316 | parent(aParent), |
a5488e5d AJ |
317 | started(false), |
318 | sent(false) | |
924f73bc | 319 | { |
924f73bc | 320 | assert (aContext); |
a5488e5d | 321 | memset(&flags, 0, sizeof(flags)); |
924f73bc | 322 | |
a5488e5d | 323 | for (int i = 0; i < attrcount && attr[i]; i += 2) { |
924f73bc | 324 | if (!strcmp(attr[i],"src")) { |
325 | /* Start a request for thisNode url */ | |
bf8fe701 | 326 | debugs(86, 5, "ESIIncludeNew: Requesting source '" << attr[i+1] << "'"); |
327 | ||
924f73bc | 328 | /* TODO: don't assert on thisNode, ignore the duplicate */ |
329 | assert (src.getRaw() == NULL); | |
330 | src = ESIStreamContextNew (this); | |
331 | assert (src.getRaw() != NULL); | |
86c63190 | 332 | srcurl = xstrdup(attr[i+1]); |
924f73bc | 333 | } else if (!strcmp(attr[i],"alt")) { |
334 | /* Start a secondary request for thisNode url */ | |
335 | /* TODO: make a config parameter to wait on requesting alt's | |
336 | * for the src to fail | |
337 | */ | |
bf8fe701 | 338 | debugs(86, 5, "ESIIncludeNew: Requesting alternate '" << attr[i+1] << "'"); |
339 | ||
924f73bc | 340 | assert (alt.getRaw() == NULL); /* TODO: FIXME */ |
341 | alt = ESIStreamContextNew (this); | |
342 | assert (alt.getRaw() != NULL); | |
86c63190 | 343 | alturl = xstrdup(attr[i+1]); |
924f73bc | 344 | } else if (!strcmp(attr[i],"onerror")) { |
345 | if (!strcmp(attr[i+1], "continue")) { | |
346 | flags.onerrorcontinue = 1; | |
347 | } else { | |
348 | /* ignore mistyped attributes */ | |
e0236918 | 349 | debugs(86, DBG_IMPORTANT, "invalid value for onerror='" << attr[i+1] << "'"); |
924f73bc | 350 | } |
351 | } else { | |
352 | /* ignore mistyped attributes. TODO:? error on these for user feedback - config parameter needed | |
353 | */ | |
354 | } | |
355 | } | |
356 | ||
357 | varState = cbdataReference(aContext->varState); | |
358 | } | |
359 | ||
360 | void | |
361 | ESIInclude::start() | |
362 | { | |
363 | /* prevent freeing ourselves */ | |
364 | ESIIncludePtr foo(this); | |
365 | ||
366 | if (started) | |
367 | return; | |
368 | ||
369 | started = true; | |
370 | ||
371 | if (src.getRaw()) { | |
372 | Start (src, srcurl, varState); | |
373 | Start (alt, alturl, varState); | |
374 | } else { | |
375 | alt = NULL; | |
376 | ||
e0236918 | 377 | debugs(86, DBG_IMPORTANT, "ESIIncludeNew: esi:include with no src attributes"); |
924f73bc | 378 | |
379 | flags.failed = 1; | |
380 | } | |
381 | } | |
382 | ||
383 | void | |
384 | ESIInclude::render(ESISegment::Pointer output) | |
385 | { | |
386 | if (sent) | |
387 | return; | |
388 | ||
389 | ESISegment::Pointer myout; | |
390 | ||
bf8fe701 | 391 | debugs(86, 5, "ESIIncludeRender: Rendering include " << this); |
924f73bc | 392 | |
393 | assert (flags.finished || (flags.failed && flags.onerrorcontinue)); | |
394 | ||
395 | if (flags.failed && flags.onerrorcontinue) { | |
396 | return; | |
397 | } | |
398 | ||
399 | /* Render the content */ | |
400 | if (srccontent.getRaw()) { | |
401 | myout = srccontent; | |
402 | srccontent = NULL; | |
403 | } else if (altcontent.getRaw()) { | |
404 | myout = altcontent; | |
405 | altcontent = NULL; | |
406 | } else | |
407 | fatal ("ESIIncludeRender called with no content, and no failure!\n"); | |
408 | ||
409 | assert (output->next == NULL); | |
410 | ||
411 | output->next = myout; | |
412 | ||
413 | sent = true; | |
414 | } | |
415 | ||
416 | esiProcessResult_t | |
417 | ESIInclude::process (int dovars) | |
418 | { | |
419 | /* Prevent refcount race leading to free */ | |
420 | Pointer me (this); | |
421 | start(); | |
bf8fe701 | 422 | debugs(86, 5, "ESIIncludeRender: Processing include " << this); |
924f73bc | 423 | |
424 | if (flags.failed) { | |
425 | if (flags.onerrorcontinue) | |
426 | return ESI_PROCESS_COMPLETE; | |
427 | else | |
428 | return ESI_PROCESS_FAILED; | |
429 | } | |
430 | ||
431 | if (!flags.finished) { | |
432 | if (flags.onerrorcontinue) | |
433 | return ESI_PROCESS_PENDING_WONTFAIL; | |
434 | else | |
435 | return ESI_PROCESS_PENDING_MAYFAIL; | |
436 | } | |
437 | ||
438 | return ESI_PROCESS_COMPLETE; | |
439 | } | |
440 | ||
441 | void | |
59a09b98 | 442 | ESIInclude::includeFail (ESIStreamContext::Pointer stream) |
924f73bc | 443 | { |
444 | subRequestDone (stream, false); | |
445 | } | |
446 | ||
447 | bool | |
448 | ESIInclude::dataNeeded() const | |
449 | { | |
450 | return !(flags.finished || flags.failed); | |
451 | } | |
452 | ||
453 | void | |
454 | ESIInclude::subRequestDone (ESIStreamContext::Pointer stream, bool success) | |
455 | { | |
456 | assert (this); | |
457 | ||
458 | if (!dataNeeded()) | |
459 | return; | |
460 | ||
461 | if (stream == src) { | |
bf8fe701 | 462 | debugs(86, 3, "ESIInclude::subRequestDone: " << srcurl); |
924f73bc | 463 | |
464 | if (success) { | |
465 | /* copy the lead segment */ | |
bf8fe701 | 466 | debugs(86, 3, "ESIIncludeSubRequestDone: Src OK - include PASSED."); |
924f73bc | 467 | assert (!srccontent.getRaw()); |
468 | ESISegment::ListTransfer (stream->localbuffer, srccontent); | |
469 | /* we're done! */ | |
470 | flags.finished = 1; | |
471 | } else { | |
472 | /* Fail if there is no alt being retrieved */ | |
bf8fe701 | 473 | debugs(86, 3, "ESIIncludeSubRequestDone: Src FAILED"); |
924f73bc | 474 | |
475 | if (!(alt.getRaw() || altcontent.getRaw())) { | |
bf8fe701 | 476 | debugs(86, 3, "ESIIncludeSubRequestDone: Include FAILED - No ALT"); |
924f73bc | 477 | flags.failed = 1; |
478 | } else if (altcontent.getRaw()) { | |
bf8fe701 | 479 | debugs(86, 3, "ESIIncludeSubRequestDone: Include PASSED - ALT already Complete"); |
924f73bc | 480 | /* ALT was already retrieved, we are done */ |
481 | flags.finished = 1; | |
482 | } | |
483 | } | |
484 | ||
485 | src = NULL; | |
486 | } else if (stream == alt) { | |
bf8fe701 | 487 | debugs(86, 3, "ESIInclude::subRequestDone: " << alturl); |
924f73bc | 488 | |
489 | if (success) { | |
bf8fe701 | 490 | debugs(86, 3, "ESIIncludeSubRequestDone: ALT OK."); |
924f73bc | 491 | /* copy the lead segment */ |
492 | assert (!altcontent.getRaw()); | |
493 | ESISegment::ListTransfer (stream->localbuffer, altcontent); | |
494 | /* we're done! */ | |
495 | ||
496 | if (!(src.getRaw() || srccontent.getRaw())) { | |
497 | /* src already failed, kick ESI processor */ | |
bf8fe701 | 498 | debugs(86, 3, "ESIIncludeSubRequestDone: Include PASSED - SRC already failed."); |
924f73bc | 499 | flags.finished = 1; |
500 | } | |
501 | } else { | |
502 | if (!(src.getRaw() || srccontent.getRaw())) { | |
bf8fe701 | 503 | debugs(86, 3, "ESIIncludeSubRequestDone: ALT FAILED, Include FAILED - SRC already failed"); |
924f73bc | 504 | /* src already failed */ |
505 | flags.failed = 1; | |
506 | } | |
507 | } | |
508 | ||
509 | alt = NULL; | |
510 | } else { | |
511 | fatal ("ESIIncludeSubRequestDone: non-owned stream found!\n"); | |
512 | } | |
513 | ||
514 | if (flags.finished || flags.failed) { | |
515 | /* Kick ESI Processor */ | |
26ac0430 AJ |
516 | debugs (86, 5, "ESIInclude " << this << |
517 | " SubRequest " << stream.getRaw() << | |
2a2d937f | 518 | " completed, kicking processor , status " << |
519 | (flags.finished ? "OK" : "FAILED")); | |
520 | /* There is a race condition - and we have no reproducible test case - | |
26ac0430 | 521 | * during a subrequest the parent will get set to NULL, which is not |
2a2d937f | 522 | * meant to be possible. Rather than killing squid, we let it leak |
523 | * memory but complain in the log. | |
524 | * | |
525 | * Someone wanting to debug this could well start by running squid with | |
526 | * a hardware breakpoint set to this location. | |
527 | * Its probably due to parent being set to null - by a call to | |
528 | * 'this.finish' while the subrequest is still not completed. | |
529 | */ | |
530 | if (parent.getRaw() == NULL) { | |
531 | debugs (86, 0, "ESIInclude::subRequestDone: Sub request completed " | |
26ac0430 AJ |
532 | "after finish() called and parent unlinked. Unable to " |
533 | "continue handling the request, and may be memory leaking. " | |
534 | "See http://www.squid-cache.org/bugs/show_bug.cgi?id=951 - we " | |
535 | "are looking for a reproducible test case. This will require " | |
536 | "an ESI template with includes, probably with alt-options, " | |
537 | "and we're likely to need traffic dumps to allow us to " | |
538 | "reconstruct the exact tcp handling sequences to trigger this " | |
539 | "rather elusive bug."); | |
2a2d937f | 540 | return; |
541 | } | |
924f73bc | 542 | assert (parent.getRaw()); |
543 | ||
544 | if (!flags.failed) { | |
545 | sent = true; | |
546 | parent->provideData (srccontent.getRaw() ? srccontent:altcontent,this); | |
547 | ||
548 | if (srccontent.getRaw()) | |
549 | srccontent = NULL; | |
550 | else | |
551 | altcontent = NULL; | |
552 | } else if (flags.onerrorcontinue) { | |
553 | /* render nothing but inform of completion */ | |
554 | ||
555 | if (!sent) { | |
556 | sent = true; | |
557 | parent->provideData (new ESISegment, this); | |
558 | } else | |
559 | assert (0); | |
560 | } else | |
561 | parent->fail(this, "esi:include could not be completed."); | |
562 | } | |
563 | } | |
564 | ||
454e8283 | 565 | #endif /* USE_SQUID_ESI == 1 */ |