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