]>
Commit | Line | Data |
---|---|---|
cd304fc2 | 1 | /* |
262a0e14 | 2 | * $Id$ |
cd304fc2 | 3 | * |
4 | * DEBUG: | |
5 | * AUTHOR: Duane Wessels | |
6 | * | |
7 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
8 | * ---------------------------------------------------------- | |
9 | * | |
10 | * Squid is the result of efforts by numerous individuals from | |
11 | * the Internet community; see the CONTRIBUTORS file for full | |
12 | * details. Many organizations have provided support for Squid's | |
13 | * development; see the SPONSORS file for full details. Squid is | |
14 | * Copyrighted (C) 2001 by the Regents of the University of | |
15 | * California; see the COPYRIGHT file for full details. Squid | |
16 | * incorporates software developed and/or copyrighted by other | |
17 | * sources; see the CREDITS file for full details. | |
18 | * | |
19 | * This program is free software; you can redistribute it and/or modify | |
20 | * it under the terms of the GNU General Public License as published by | |
21 | * the Free Software Foundation; either version 2 of the License, or | |
22 | * (at your option) any later version. | |
9e008dda | 23 | * |
cd304fc2 | 24 | * This program is distributed in the hope that it will be useful, |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | * GNU General Public License for more details. | |
9e008dda | 28 | * |
cd304fc2 | 29 | * You should have received a copy of the GNU General Public License |
30 | * along with this program; if not, write to the Free Software | |
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
32 | * | |
33 | */ | |
34 | ||
35 | #include "squid.h" | |
36 | #include "Server.h" | |
37 | #include "Store.h" | |
38 | #include "HttpRequest.h" | |
39 | #include "HttpReply.h" | |
c1520b67 | 40 | #include "TextException.h" |
5f8252d2 | 41 | #include "errorpage.h" |
3ff65596 | 42 | #include "SquidTime.h" |
5f8252d2 | 43 | |
a83c6ed6 | 44 | #if USE_ADAPTATION |
62c7f90e | 45 | #include "adaptation/AccessCheck.h" |
a22e6cd3 | 46 | #include "adaptation/Iterator.h" |
0f283edf | 47 | #endif |
cd304fc2 | 48 | |
c1520b67 | 49 | // implemented in client_side_reply.cc until sides have a common parent |
90bd689c | 50 | extern void purgeEntriesByUrl(HttpRequest * req, const char *url); |
c1520b67 AJ |
51 | |
52 | ||
dc56a9b1 | 53 | ServerStateData::ServerStateData(FwdState *theFwdState): AsyncJob("ServerStateData"),requestSender(NULL) |
a83c6ed6 | 54 | #if USE_ADAPTATION |
9e008dda AJ |
55 | , adaptedHeadSource(NULL) |
56 | , adaptationAccessCheckPending(false) | |
57 | , startedAdaptation(false) | |
77089558 | 58 | #endif |
cd304fc2 | 59 | { |
60 | fwd = theFwdState; | |
61 | entry = fwd->entry; | |
34266cde | 62 | |
5f8252d2 | 63 | entry->lock(); |
34266cde | 64 | |
6dd9f4bd | 65 | request = HTTPMSGLOCK(fwd->request); |
cd304fc2 | 66 | } |
67 | ||
68 | ServerStateData::~ServerStateData() | |
69 | { | |
49918309 AR |
70 | // paranoid: check that swanSong has been called |
71 | assert(!requestBodySource); | |
72 | #if USE_ADAPTATION | |
73 | assert(!virginBodyDestination); | |
74 | assert(!adaptedBodySource); | |
75 | #endif | |
76 | ||
97b5e68f | 77 | entry->unlock(); |
cd304fc2 | 78 | |
6dd9f4bd | 79 | HTTPMSGUNLOCK(request); |
585ab260 | 80 | HTTPMSGUNLOCK(theVirginReply); |
81 | HTTPMSGUNLOCK(theFinalReply); | |
cd304fc2 | 82 | |
83 | fwd = NULL; // refcounted | |
84 | ||
49918309 | 85 | if (responseBodyBuffer != NULL) { |
9e008dda AJ |
86 | delete responseBodyBuffer; |
87 | responseBodyBuffer = NULL; | |
49918309 AR |
88 | } |
89 | } | |
90 | ||
91 | void | |
92 | ServerStateData::swanSong() | |
93 | { | |
94 | // get rid of our piping obligations | |
5f8252d2 | 95 | if (requestBodySource != NULL) |
279152e7 | 96 | stopConsumingFrom(requestBodySource); |
5f8252d2 | 97 | |
a83c6ed6 AR |
98 | #if USE_ADAPTATION |
99 | cleanAdaptation(); | |
5f8252d2 | 100 | #endif |
7dc79973 | 101 | |
49918309 AR |
102 | BodyConsumer::swanSong(); |
103 | #if USE_ADAPTATION | |
104 | Initiator::swanSong(); | |
105 | BodyProducer::swanSong(); | |
106 | #endif | |
b2c251cc AJ |
107 | |
108 | // paranoid: check that swanSong has been called | |
109 | // extra paranoid: yeah, I really mean it. they MUST pass here. | |
110 | assert(!requestBodySource); | |
111 | #if USE_ADAPTATION | |
112 | assert(!virginBodyDestination); | |
113 | assert(!adaptedBodySource); | |
114 | #endif | |
5f8252d2 | 115 | } |
116 | ||
49918309 | 117 | |
585ab260 | 118 | HttpReply * |
9e008dda AJ |
119 | ServerStateData::virginReply() |
120 | { | |
585ab260 | 121 | assert(theVirginReply); |
122 | return theVirginReply; | |
123 | } | |
124 | ||
125 | const HttpReply * | |
9e008dda AJ |
126 | ServerStateData::virginReply() const |
127 | { | |
585ab260 | 128 | assert(theVirginReply); |
129 | return theVirginReply; | |
130 | } | |
131 | ||
132 | HttpReply * | |
9e008dda AJ |
133 | ServerStateData::setVirginReply(HttpReply *rep) |
134 | { | |
585ab260 | 135 | debugs(11,5, HERE << this << " setting virgin reply to " << rep); |
136 | assert(!theVirginReply); | |
137 | assert(rep); | |
138 | theVirginReply = HTTPMSGLOCK(rep); | |
9e008dda | 139 | return theVirginReply; |
585ab260 | 140 | } |
141 | ||
142 | HttpReply * | |
9e008dda AJ |
143 | ServerStateData::finalReply() |
144 | { | |
585ab260 | 145 | assert(theFinalReply); |
146 | return theFinalReply; | |
147 | } | |
148 | ||
149 | HttpReply * | |
9e008dda AJ |
150 | ServerStateData::setFinalReply(HttpReply *rep) |
151 | { | |
585ab260 | 152 | debugs(11,5, HERE << this << " setting final reply to " << rep); |
153 | ||
154 | assert(!theFinalReply); | |
155 | assert(rep); | |
156 | theFinalReply = HTTPMSGLOCK(rep); | |
157 | ||
158 | entry->replaceHttpReply(theFinalReply); | |
159 | haveParsedReplyHeaders(); | |
160 | ||
161 | return theFinalReply; | |
162 | } | |
163 | ||
5f8252d2 | 164 | // called when no more server communication is expected; may quit |
165 | void | |
166 | ServerStateData::serverComplete() | |
167 | { | |
168 | debugs(11,5,HERE << "serverComplete " << this); | |
169 | ||
170 | if (!doneWithServer()) { | |
171 | closeServer(); | |
172 | assert(doneWithServer()); | |
173 | } | |
174 | ||
7dc79973 | 175 | completed = true; |
176 | ||
3ff65596 AR |
177 | HttpRequest *r = originalRequest(); |
178 | r->hier.total_response_time = r->hier.first_conn_start.tv_sec ? | |
e1381638 AJ |
179 | tvSubMsec(r->hier.first_conn_start, current_time) : -1; |
180 | ||
5f8252d2 | 181 | if (requestBodySource != NULL) |
182 | stopConsumingFrom(requestBodySource); | |
183 | ||
7dc79973 | 184 | if (responseBodyBuffer != NULL) |
9e008dda | 185 | return; |
7dc79973 | 186 | |
187 | serverComplete2(); | |
188 | } | |
189 | ||
190 | void | |
191 | ServerStateData::serverComplete2() | |
192 | { | |
193 | debugs(11,5,HERE << "serverComplete2 " << this); | |
194 | ||
a83c6ed6 | 195 | #if USE_ADAPTATION |
5f8252d2 | 196 | if (virginBodyDestination != NULL) |
197 | stopProducingFor(virginBodyDestination, true); | |
198 | ||
a83c6ed6 | 199 | if (!doneWithAdaptation()) |
5f8252d2 | 200 | return; |
201 | #endif | |
202 | ||
203 | completeForwarding(); | |
204 | quitIfAllDone(); | |
205 | } | |
206 | ||
9e008dda | 207 | // When we are done talking to the primary server, we may be still talking |
5f8252d2 | 208 | // to the ICAP service. And vice versa. Here, we quit only if we are done |
209 | // talking to both. | |
9e008dda AJ |
210 | void ServerStateData::quitIfAllDone() |
211 | { | |
a83c6ed6 AR |
212 | #if USE_ADAPTATION |
213 | if (!doneWithAdaptation()) { | |
5f8252d2 | 214 | debugs(11,5, HERE << "transaction not done: still talking to ICAP"); |
215 | return; | |
216 | } | |
217 | #endif | |
218 | ||
219 | if (!doneWithServer()) { | |
220 | debugs(11,5, HERE << "transaction not done: still talking to server"); | |
221 | return; | |
222 | } | |
223 | ||
224 | debugs(11,3, HERE << "transaction done"); | |
dc56a9b1 | 225 | |
226 | deleteThis("ServerStateData::quitIfAllDone"); | |
5f8252d2 | 227 | } |
228 | ||
229 | // FTP side overloads this to work around multiple calls to fwd->complete | |
230 | void | |
9e008dda AJ |
231 | ServerStateData::completeForwarding() |
232 | { | |
5f8252d2 | 233 | debugs(11,5, HERE << "completing forwarding for " << fwd); |
234 | assert(fwd != NULL); | |
235 | fwd->complete(); | |
236 | } | |
237 | ||
123ec4de | 238 | // Register to receive request body |
239 | bool ServerStateData::startRequestBodyFlow() | |
240 | { | |
241 | HttpRequest *r = originalRequest(); | |
242 | assert(r->body_pipe != NULL); | |
243 | requestBodySource = r->body_pipe; | |
244 | if (requestBodySource->setConsumerIfNotLate(this)) { | |
245 | debugs(11,3, HERE << "expecting request body from " << | |
9e008dda | 246 | requestBodySource->status()); |
123ec4de | 247 | return true; |
248 | } | |
249 | ||
250 | debugs(11,3, HERE << "aborting on partially consumed request body: " << | |
9e008dda | 251 | requestBodySource->status()); |
123ec4de | 252 | requestBodySource = NULL; |
253 | return false; | |
254 | } | |
255 | ||
5f8252d2 | 256 | // Entry-dependent callbacks use this check to quit if the entry went bad |
257 | bool | |
258 | ServerStateData::abortOnBadEntry(const char *abortReason) | |
259 | { | |
260 | if (entry->isAccepting()) | |
261 | return false; | |
262 | ||
263 | debugs(11,5, HERE << "entry is not Accepting!"); | |
264 | abortTransaction(abortReason); | |
265 | return true; | |
266 | } | |
267 | ||
268 | // more request or adapted response body is available | |
269 | void | |
dc56a9b1 | 270 | ServerStateData::noteMoreBodyDataAvailable(BodyPipe::Pointer bp) |
5f8252d2 | 271 | { |
a83c6ed6 | 272 | #if USE_ADAPTATION |
dc56a9b1 | 273 | if (adaptedBodySource == bp) { |
5f8252d2 | 274 | handleMoreAdaptedBodyAvailable(); |
275 | return; | |
276 | } | |
277 | #endif | |
278 | handleMoreRequestBodyAvailable(); | |
279 | } | |
280 | ||
281 | // the entire request or adapted response body was provided, successfully | |
282 | void | |
dc56a9b1 | 283 | ServerStateData::noteBodyProductionEnded(BodyPipe::Pointer bp) |
5f8252d2 | 284 | { |
a83c6ed6 | 285 | #if USE_ADAPTATION |
dc56a9b1 | 286 | if (adaptedBodySource == bp) { |
5f8252d2 | 287 | handleAdaptedBodyProductionEnded(); |
288 | return; | |
c99de607 | 289 | } |
cd304fc2 | 290 | #endif |
5f8252d2 | 291 | handleRequestBodyProductionEnded(); |
292 | } | |
293 | ||
294 | // premature end of the request or adapted response body production | |
295 | void | |
dc56a9b1 | 296 | ServerStateData::noteBodyProducerAborted(BodyPipe::Pointer bp) |
5f8252d2 | 297 | { |
a83c6ed6 | 298 | #if USE_ADAPTATION |
dc56a9b1 | 299 | if (adaptedBodySource == bp) { |
5f8252d2 | 300 | handleAdaptedBodyProducerAborted(); |
301 | return; | |
302 | } | |
303 | #endif | |
304 | handleRequestBodyProducerAborted(); | |
305 | } | |
306 | ||
307 | ||
308 | // more origin request body data is available | |
309 | void | |
310 | ServerStateData::handleMoreRequestBodyAvailable() | |
311 | { | |
312 | if (!requestSender) | |
313 | sendMoreRequestBody(); | |
314 | else | |
315 | debugs(9,3, HERE << "waiting for request body write to complete"); | |
316 | } | |
317 | ||
318 | // there will be no more handleMoreRequestBodyAvailable calls | |
319 | void | |
320 | ServerStateData::handleRequestBodyProductionEnded() | |
321 | { | |
322 | if (!requestSender) | |
323 | doneSendingRequestBody(); | |
324 | else | |
325 | debugs(9,3, HERE << "waiting for request body write to complete"); | |
326 | } | |
327 | ||
328 | // called when we are done sending request body; kids extend this | |
329 | void | |
9e008dda AJ |
330 | ServerStateData::doneSendingRequestBody() |
331 | { | |
5f8252d2 | 332 | debugs(9,3, HERE << "done sending request body"); |
333 | assert(requestBodySource != NULL); | |
334 | stopConsumingFrom(requestBodySource); | |
335 | ||
336 | // kids extend this | |
337 | } | |
338 | ||
339 | // called when body producers aborts; kids extend this | |
340 | void | |
341 | ServerStateData::handleRequestBodyProducerAborted() | |
342 | { | |
343 | if (requestSender != NULL) | |
344 | debugs(9,3, HERE << "fyi: request body aborted while we were sending"); | |
345 | ||
0919c51e | 346 | fwd->dontRetry(true); // the problem is not with the server |
5f8252d2 | 347 | stopConsumingFrom(requestBodySource); // requestSender, if any, will notice |
348 | ||
349 | // kids extend this | |
350 | } | |
351 | ||
5f8252d2 | 352 | // called when we wrote request headers(!) or a part of the body |
353 | void | |
dc56a9b1 | 354 | ServerStateData::sentRequestBody(const CommIoCbParams &io) |
5f8252d2 | 355 | { |
dc56a9b1 | 356 | debugs(11, 5, "sentRequestBody: FD " << io.fd << ": size " << io.size << ": errflag " << io.flag << "."); |
5f8252d2 | 357 | debugs(32,3,HERE << "sentRequestBody called"); |
358 | ||
359 | requestSender = NULL; | |
360 | ||
dc56a9b1 | 361 | if (io.size > 0) { |
362 | fd_bytes(io.fd, io.size, FD_WRITE); | |
363 | kb_incr(&statCounter.server.all.kbytes_out, io.size); | |
5f8252d2 | 364 | // kids should increment their counters |
365 | } | |
366 | ||
dc56a9b1 | 367 | if (io.flag == COMM_ERR_CLOSING) |
5f8252d2 | 368 | return; |
369 | ||
370 | if (!requestBodySource) { | |
371 | debugs(9,3, HERE << "detected while-we-were-sending abort"); | |
372 | return; // do nothing; | |
373 | } | |
374 | ||
dc56a9b1 | 375 | if (io.flag) { |
376 | debugs(11, 1, "sentRequestBody error: FD " << io.fd << ": " << xstrerr(errno)); | |
5f8252d2 | 377 | ErrorState *err; |
378 | err = errorCon(ERR_WRITE_ERROR, HTTP_BAD_GATEWAY, fwd->request); | |
379 | err->xerrno = errno; | |
380 | fwd->fail(err); | |
381 | abortTransaction("I/O error while sending request body"); | |
382 | return; | |
383 | } | |
384 | ||
385 | if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { | |
386 | abortTransaction("store entry aborted while sending request body"); | |
387 | return; | |
388 | } | |
389 | ||
390 | if (requestBodySource->exhausted()) | |
391 | doneSendingRequestBody(); | |
392 | else | |
393 | sendMoreRequestBody(); | |
cd304fc2 | 394 | } |
395 | ||
5f8252d2 | 396 | void |
397 | ServerStateData::sendMoreRequestBody() | |
398 | { | |
399 | assert(requestBodySource != NULL); | |
400 | assert(!requestSender); | |
401 | MemBuf buf; | |
402 | if (requestBodySource->getMoreData(buf)) { | |
403 | debugs(9,3, HERE << "will write " << buf.contentSize() << " request body bytes"); | |
9e008dda AJ |
404 | typedef CommCbMemFunT<ServerStateData, CommIoCbParams> Dialer; |
405 | requestSender = asyncCall(93,3, "ServerStateData::sentRequestBody", | |
406 | Dialer(this, &ServerStateData::sentRequestBody)); | |
dc56a9b1 | 407 | comm_write_mbuf(dataDescriptor(), &buf, requestSender); |
5f8252d2 | 408 | } else { |
409 | debugs(9,3, HERE << "will wait for more request body bytes or eof"); | |
410 | requestSender = NULL; | |
411 | } | |
412 | } | |
413 | ||
c1520b67 AJ |
414 | // Compares hosts in urls, returns false if different, no sheme, or no host. |
415 | static bool | |
416 | sameUrlHosts(const char *url1, const char *url2) | |
417 | { | |
418 | // XXX: Want urlHostname() here, but it uses static storage and copying | |
419 | const char *host1 = strchr(url1, ':'); | |
420 | const char *host2 = strchr(url2, ':'); | |
421 | ||
422 | if (host1 && host2) { | |
423 | // skip scheme slashes | |
424 | do { | |
425 | ++host1; | |
426 | ++host2; | |
427 | } while (*host1 == '/' && *host2 == '/'); | |
428 | ||
429 | if (!*host1) | |
430 | return false; // no host | |
431 | ||
432 | // increment while the same until we reach the end of the URL/host | |
433 | while (*host1 && *host1 != '/' && *host1 == *host2) { | |
434 | ++host1; | |
435 | ++host2; | |
436 | } | |
437 | return *host1 == *host2; | |
438 | } | |
439 | ||
440 | return false; // no URL scheme | |
441 | } | |
442 | ||
443 | // purges entries that match the value of a given HTTP [response] header | |
444 | static void | |
90bd689c | 445 | purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, HttpMsg *rep, http_hdr_type hdr) |
c1520b67 | 446 | { |
bf956b0a | 447 | const char *hdrUrl, *absUrl; |
9e008dda | 448 | |
71051277 BR |
449 | absUrl = NULL; |
450 | hdrUrl = rep->header.getStr(hdr); | |
451 | if (hdrUrl == NULL) { | |
452 | return; | |
453 | } | |
9e008dda | 454 | |
71051277 BR |
455 | /* |
456 | * If the URL is relative, make it absolute so we can find it. | |
457 | * If it's absolute, make sure the host parts match to avoid DOS attacks | |
458 | * as per RFC 2616 13.10. | |
459 | */ | |
460 | if (urlIsRelative(hdrUrl)) { | |
461 | absUrl = urlMakeAbsolute(req, hdrUrl); | |
3cbbd242 | 462 | if (absUrl != NULL) { |
71051277 | 463 | hdrUrl = absUrl; |
3cbbd242 | 464 | } |
71051277 BR |
465 | } else if (!sameUrlHosts(reqUrl, hdrUrl)) { |
466 | return; | |
467 | } | |
9e008dda | 468 | |
8dceeee3 | 469 | purgeEntriesByUrl(req, hdrUrl); |
9e008dda | 470 | |
71051277 BR |
471 | if (absUrl != NULL) { |
472 | safe_free(absUrl); | |
3cbbd242 | 473 | } |
c1520b67 AJ |
474 | } |
475 | ||
476 | // some HTTP methods should purge matching cache entries | |
477 | void | |
478 | ServerStateData::maybePurgeOthers() | |
479 | { | |
9e008dda AJ |
480 | // only some HTTP methods should purge matching cache entries |
481 | if (!request->method.purgesOthers()) | |
482 | return; | |
c1520b67 | 483 | |
9e008dda AJ |
484 | // and probably only if the response was successful |
485 | if (theFinalReply->sline.status >= 400) | |
486 | return; | |
c1520b67 | 487 | |
9e008dda AJ |
488 | // XXX: should we use originalRequest() here? |
489 | const char *reqUrl = urlCanonical(request); | |
490 | debugs(88, 5, "maybe purging due to " << RequestMethodStr(request->method) << ' ' << reqUrl); | |
491 | purgeEntriesByUrl(request, reqUrl); | |
492 | purgeEntriesByHeader(request, reqUrl, theFinalReply, HDR_LOCATION); | |
493 | purgeEntriesByHeader(request, reqUrl, theFinalReply, HDR_CONTENT_LOCATION); | |
c1520b67 AJ |
494 | } |
495 | ||
496 | // called (usually by kids) when we have final (possibly adapted) reply headers | |
5f8252d2 | 497 | void |
498 | ServerStateData::haveParsedReplyHeaders() | |
499 | { | |
9e008dda AJ |
500 | Must(theFinalReply); |
501 | maybePurgeOthers(); | |
5f8252d2 | 502 | } |
503 | ||
7dc79973 | 504 | HttpRequest * |
505 | ServerStateData::originalRequest() | |
506 | { | |
507 | return request; | |
508 | } | |
5f8252d2 | 509 | |
a83c6ed6 | 510 | #if USE_ADAPTATION |
a22e6cd3 AR |
511 | /// Initiate an asynchronous adaptation transaction which will call us back. |
512 | void | |
513 | ServerStateData::startAdaptation(const Adaptation::ServiceGroupPointer &group, HttpRequest *cause) | |
cd304fc2 | 514 | { |
a83c6ed6 | 515 | debugs(11, 5, "ServerStateData::startAdaptation() called"); |
5f8252d2 | 516 | // check whether we should be sending a body as well |
5f8252d2 | 517 | // start body pipe to feed ICAP transaction if needed |
585ab260 | 518 | assert(!virginBodyDestination); |
9e008dda | 519 | HttpReply *vrep = virginReply(); |
585ab260 | 520 | assert(!vrep->body_pipe); |
47f6e231 | 521 | int64_t size = 0; |
585ab260 | 522 | if (vrep->expectingBody(cause->method, size) && size) { |
5f8252d2 | 523 | virginBodyDestination = new BodyPipe(this); |
585ab260 | 524 | vrep->body_pipe = virginBodyDestination; |
9e008dda AJ |
525 | debugs(93, 6, HERE << "will send virgin reply body to " << |
526 | virginBodyDestination << "; size: " << size); | |
c2eef5bd | 527 | if (size > 0) |
528 | virginBodyDestination->setBodySize(size); | |
5f8252d2 | 529 | } |
530 | ||
a22e6cd3 | 531 | adaptedHeadSource = initiateAdaptation( |
e1381638 | 532 | new Adaptation::Iterator(this, vrep, cause, group)); |
a22e6cd3 AR |
533 | startedAdaptation = adaptedHeadSource != NULL; |
534 | Must(startedAdaptation); | |
cd304fc2 | 535 | } |
0c25e715 | 536 | |
5f8252d2 | 537 | // properly cleans up ICAP-related state |
538 | // may be called multiple times | |
9e008dda AJ |
539 | void ServerStateData::cleanAdaptation() |
540 | { | |
a83c6ed6 | 541 | debugs(11,5, HERE << "cleaning ICAP; ACL: " << adaptationAccessCheckPending); |
5f8252d2 | 542 | |
543 | if (virginBodyDestination != NULL) | |
544 | stopProducingFor(virginBodyDestination, false); | |
545 | ||
0f283edf | 546 | announceInitiatorAbort(adaptedHeadSource); |
5f8252d2 | 547 | |
548 | if (adaptedBodySource != NULL) | |
549 | stopConsumingFrom(adaptedBodySource); | |
550 | ||
a83c6ed6 AR |
551 | if (!adaptationAccessCheckPending) // we cannot cancel a pending callback |
552 | assert(doneWithAdaptation()); // make sure the two methods are in sync | |
5f8252d2 | 553 | } |
554 | ||
555 | bool | |
9e008dda AJ |
556 | ServerStateData::doneWithAdaptation() const |
557 | { | |
a83c6ed6 | 558 | return !adaptationAccessCheckPending && |
9e008dda | 559 | !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource; |
5f8252d2 | 560 | } |
561 | ||
bc81cb2b | 562 | // sends virgin reply body to ICAP, buffering excesses if needed |
563 | void | |
564 | ServerStateData::adaptVirginReplyBody(const char *data, ssize_t len) | |
565 | { | |
a83c6ed6 | 566 | assert(startedAdaptation); |
bc81cb2b | 567 | |
568 | if (!virginBodyDestination) { | |
569 | debugs(11,3, HERE << "ICAP does not want more virgin body"); | |
570 | return; | |
571 | } | |
572 | ||
573 | // grow overflow area if already overflowed | |
574 | if (responseBodyBuffer) { | |
575 | responseBodyBuffer->append(data, len); | |
576 | data = responseBodyBuffer->content(); | |
577 | len = responseBodyBuffer->contentSize(); | |
578 | } | |
579 | ||
580 | const ssize_t putSize = virginBodyDestination->putMoreData(data, len); | |
581 | data += putSize; | |
582 | len -= putSize; | |
583 | ||
584 | // if we had overflow area, shrink it as necessary | |
585 | if (responseBodyBuffer) { | |
586 | if (putSize == responseBodyBuffer->contentSize()) { | |
587 | delete responseBodyBuffer; | |
588 | responseBodyBuffer = NULL; | |
589 | } else { | |
590 | responseBodyBuffer->consume(putSize); | |
9e008dda | 591 | } |
bc81cb2b | 592 | return; |
593 | } | |
594 | ||
595 | // if we did not have an overflow area, create it as needed | |
596 | if (len > 0) { | |
597 | assert(!responseBodyBuffer); | |
598 | responseBodyBuffer = new MemBuf; | |
599 | responseBodyBuffer->init(4096, SQUID_TCP_SO_RCVBUF * 10); | |
600 | responseBodyBuffer->append(data, len); | |
601 | } | |
602 | } | |
603 | ||
5f8252d2 | 604 | // can supply more virgin response body data |
605 | void | |
dc56a9b1 | 606 | ServerStateData::noteMoreBodySpaceAvailable(BodyPipe::Pointer) |
5f8252d2 | 607 | { |
7dc79973 | 608 | if (responseBodyBuffer) { |
bc81cb2b | 609 | addVirginReplyBody(NULL, 0); // kick the buffered fragment alive again |
610 | if (completed && !responseBodyBuffer) { | |
611 | serverComplete2(); | |
612 | return; | |
613 | } | |
7dc79973 | 614 | } |
5f8252d2 | 615 | maybeReadVirginBody(); |
616 | } | |
617 | ||
bc81cb2b | 618 | // the consumer of our virgin response body aborted |
5f8252d2 | 619 | void |
dc56a9b1 | 620 | ServerStateData::noteBodyConsumerAborted(BodyPipe::Pointer) |
5f8252d2 | 621 | { |
622 | stopProducingFor(virginBodyDestination, false); | |
bc81cb2b | 623 | |
a83c6ed6 | 624 | // do not force closeServer here in case we need to bypass AdaptationQueryAbort |
bc81cb2b | 625 | |
a83c6ed6 AR |
626 | if (doneWithAdaptation()) // we may still be receiving adapted response |
627 | handleAdaptationCompleted(); | |
5f8252d2 | 628 | } |
629 | ||
630 | // received adapted response headers (body may follow) | |
631 | void | |
a83c6ed6 | 632 | ServerStateData::noteAdaptationAnswer(HttpMsg *msg) |
5f8252d2 | 633 | { |
a83c6ed6 | 634 | clearAdaptation(adaptedHeadSource); // we do not expect more messages |
5f8252d2 | 635 | |
585ab260 | 636 | if (abortOnBadEntry("entry went bad while waiting for adapted headers")) |
5f8252d2 | 637 | return; |
5f8252d2 | 638 | |
585ab260 | 639 | HttpReply *rep = dynamic_cast<HttpReply*>(msg); |
5f8252d2 | 640 | assert(rep); |
585ab260 | 641 | debugs(11,5, HERE << this << " setting adapted reply to " << rep); |
642 | setFinalReply(rep); | |
5f8252d2 | 643 | |
644 | assert(!adaptedBodySource); | |
585ab260 | 645 | if (rep->body_pipe != NULL) { |
5f8252d2 | 646 | // subscribe to receive adapted body |
585ab260 | 647 | adaptedBodySource = rep->body_pipe; |
5f8252d2 | 648 | // assume that ICAP does not auto-consume on failures |
649 | assert(adaptedBodySource->setConsumerIfNotLate(this)); | |
650 | } else { | |
651 | // no body | |
a83c6ed6 AR |
652 | if (doneWithAdaptation()) // we may still be sending virgin response |
653 | handleAdaptationCompleted(); | |
5f8252d2 | 654 | } |
5f8252d2 | 655 | } |
656 | ||
657 | // will not receive adapted response headers (and, hence, body) | |
658 | void | |
a83c6ed6 | 659 | ServerStateData::noteAdaptationQueryAbort(bool final) |
5f8252d2 | 660 | { |
a83c6ed6 AR |
661 | clearAdaptation(adaptedHeadSource); |
662 | handleAdaptationAborted(!final); | |
5f8252d2 | 663 | } |
664 | ||
665 | // more adapted response body is available | |
666 | void | |
667 | ServerStateData::handleMoreAdaptedBodyAvailable() | |
668 | { | |
669 | const size_t contentSize = adaptedBodySource->buf().contentSize(); | |
670 | ||
671 | debugs(11,5, HERE << "consuming " << contentSize << " bytes of adapted " << | |
672 | "response body at offset " << adaptedBodySource->consumedSize()); | |
673 | ||
674 | if (abortOnBadEntry("entry refuses adapted body")) | |
675 | return; | |
676 | ||
677 | assert(entry); | |
678 | BodyPipeCheckout bpc(*adaptedBodySource); | |
cdad887f AJ |
679 | const StoreIOBuffer ioBuf(&bpc.buf, currentOffset); |
680 | currentOffset += bpc.buf.size; | |
5f8252d2 | 681 | entry->write(ioBuf); |
682 | bpc.buf.consume(contentSize); | |
683 | bpc.checkIn(); | |
684 | } | |
685 | ||
686 | // the entire adapted response body was produced, successfully | |
687 | void | |
688 | ServerStateData::handleAdaptedBodyProductionEnded() | |
689 | { | |
690 | stopConsumingFrom(adaptedBodySource); | |
691 | ||
692 | if (abortOnBadEntry("entry went bad while waiting for adapted body eof")) | |
693 | return; | |
694 | ||
a83c6ed6 | 695 | handleAdaptationCompleted(); |
5f8252d2 | 696 | } |
697 | ||
698 | // premature end of the adapted response body | |
699 | void ServerStateData::handleAdaptedBodyProducerAborted() | |
700 | { | |
701 | stopConsumingFrom(adaptedBodySource); | |
a83c6ed6 | 702 | handleAdaptationAborted(); |
5f8252d2 | 703 | } |
704 | ||
a83c6ed6 | 705 | // common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded |
5f8252d2 | 706 | void |
a83c6ed6 | 707 | ServerStateData::handleAdaptationCompleted() |
5f8252d2 | 708 | { |
a83c6ed6 AR |
709 | debugs(11,5, HERE << "handleAdaptationCompleted"); |
710 | cleanAdaptation(); | |
bc81cb2b | 711 | |
712 | // We stop reading origin response because we have no place to put it and | |
713 | // cannot use it. If some origin servers do not like that or if we want to | |
714 | // reuse more pconns, we can add code to discard unneeded origin responses. | |
715 | if (!doneWithServer()) { | |
716 | debugs(11,3, HERE << "closing origin conn due to ICAP completion"); | |
717 | closeServer(); | |
718 | } | |
719 | ||
5f8252d2 | 720 | completeForwarding(); |
721 | quitIfAllDone(); | |
722 | } | |
723 | ||
bc81cb2b | 724 | |
a83c6ed6 | 725 | // common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods |
5f8252d2 | 726 | void |
a83c6ed6 | 727 | ServerStateData::handleAdaptationAborted(bool bypassable) |
5f8252d2 | 728 | { |
a83c6ed6 | 729 | debugs(11,5, HERE << "handleAdaptationAborted; bypassable: " << bypassable << |
9e008dda | 730 | ", entry empty: " << entry->isEmpty()); |
5f8252d2 | 731 | |
732 | if (abortOnBadEntry("entry went bad while ICAP aborted")) | |
733 | return; | |
734 | ||
0f283edf | 735 | // TODO: bypass if possible |
736 | ||
5f8252d2 | 737 | if (entry->isEmpty()) { |
738 | debugs(11,9, HERE << "creating ICAP error entry after ICAP failure"); | |
c70281f8 | 739 | ErrorState *err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request); |
5f8252d2 | 740 | err->xerrno = errno; |
741 | fwd->fail(err); | |
742 | fwd->dontRetry(true); | |
743 | } | |
744 | ||
0f283edf | 745 | abortTransaction("ICAP failure"); |
5f8252d2 | 746 | } |
747 | ||
7c4e4e7f | 748 | void |
a22e6cd3 | 749 | ServerStateData::adaptationAclCheckDone(Adaptation::ServiceGroupPointer group) |
7c4e4e7f | 750 | { |
a83c6ed6 | 751 | adaptationAccessCheckPending = false; |
7c4e4e7f | 752 | |
753 | if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check")) | |
754 | return; | |
755 | ||
47416555 | 756 | // TODO: Should nonICAP and postICAP path check this on the server-side? |
757 | // That check now only happens on client-side, in processReplyAccess(). | |
758 | if (virginReply()->expectedBodyTooLarge(*request)) { | |
759 | sendBodyIsTooLargeError(); | |
760 | return; | |
761 | } | |
abd4b611 | 762 | // TODO: Should we check receivedBodyTooLarge on the server-side as well? |
47416555 | 763 | |
a22e6cd3 AR |
764 | if (!group) { |
765 | debugs(11,3, HERE << "no adapation needed"); | |
585ab260 | 766 | setFinalReply(virginReply()); |
7c4e4e7f | 767 | processReplyBody(); |
7c4e4e7f | 768 | return; |
769 | } | |
770 | ||
a22e6cd3 | 771 | startAdaptation(group, originalRequest()); |
7c4e4e7f | 772 | processReplyBody(); |
773 | } | |
774 | ||
7dc79973 | 775 | void |
a22e6cd3 | 776 | ServerStateData::adaptationAclCheckDoneWrapper(Adaptation::ServiceGroupPointer group, void *data) |
7dc79973 | 777 | { |
778 | ServerStateData *state = (ServerStateData *)data; | |
a22e6cd3 | 779 | state->adaptationAclCheckDone(group); |
7dc79973 | 780 | } |
0c25e715 | 781 | #endif |
7dc79973 | 782 | |
47416555 | 783 | void |
784 | ServerStateData::sendBodyIsTooLargeError() | |
785 | { | |
786 | ErrorState *err = errorCon(ERR_TOO_BIG, HTTP_FORBIDDEN, request); | |
787 | err->xerrno = errno; | |
788 | fwd->fail(err); | |
789 | fwd->dontRetry(true); | |
790 | abortTransaction("Virgin body too large."); | |
791 | } | |
792 | ||
9e008dda | 793 | // TODO: when HttpStateData sends all errors to ICAP, |
585ab260 | 794 | // we should be able to move this at the end of setVirginReply(). |
7dc79973 | 795 | void |
585ab260 | 796 | ServerStateData::adaptOrFinalizeReply() |
7dc79973 | 797 | { |
a83c6ed6 | 798 | #if USE_ADAPTATION |
abd4b611 AR |
799 | // TODO: merge with client side and return void to hide the on/off logic? |
800 | // The callback can be called with a NULL service if adaptation is off. | |
801 | adaptationAccessCheckPending = Adaptation::AccessCheck::Start( | |
9e008dda AJ |
802 | Adaptation::methodRespmod, Adaptation::pointPreCache, |
803 | request, virginReply(), adaptationAclCheckDoneWrapper, this); | |
c30ac6ea | 804 | debugs(11,5, HERE << "adaptationAccessCheckPending=" << adaptationAccessCheckPending); |
abd4b611 | 805 | if (adaptationAccessCheckPending) |
7dc79973 | 806 | return; |
7dc79973 | 807 | #endif |
808 | ||
585ab260 | 809 | setFinalReply(virginReply()); |
7dc79973 | 810 | } |
811 | ||
812 | void | |
bc81cb2b | 813 | ServerStateData::addVirginReplyBody(const char *data, ssize_t len) |
7dc79973 | 814 | { |
a83c6ed6 AR |
815 | #if USE_ADAPTATION |
816 | assert(!adaptationAccessCheckPending); // or would need to buffer while waiting | |
817 | if (startedAdaptation) { | |
bc81cb2b | 818 | adaptVirginReplyBody(data, len); |
7dc79973 | 819 | return; |
820 | } | |
7dc79973 | 821 | #endif |
bc81cb2b | 822 | storeReplyBody(data, len); |
823 | } | |
7dc79973 | 824 | |
bc81cb2b | 825 | // writes virgin or adapted reply body to store |
826 | void | |
827 | ServerStateData::storeReplyBody(const char *data, ssize_t len) | |
828 | { | |
2d1a172f | 829 | // write even if len is zero to push headers towards the client side |
7dc79973 | 830 | entry->write (StoreIOBuffer(len, currentOffset, (char*)data)); |
831 | ||
832 | currentOffset += len; | |
833 | } | |
834 | ||
835 | size_t ServerStateData::replyBodySpace(size_t space) | |
836 | { | |
a83c6ed6 | 837 | #if USE_ADAPTATION |
7dc79973 | 838 | if (responseBodyBuffer) { |
9e008dda | 839 | return 0; // Stop reading if already overflowed waiting for ICAP to catch up |
7dc79973 | 840 | } |
841 | ||
842 | if (virginBodyDestination != NULL) { | |
843 | /* | |
844 | * BodyPipe buffer has a finite size limit. We | |
845 | * should not read more data from the network than will fit | |
846 | * into the pipe buffer or we _lose_ what did not fit if | |
847 | * the response ends sooner that BodyPipe frees up space: | |
848 | * There is no code to keep pumping data into the pipe once | |
849 | * response ends and serverComplete() is called. | |
850 | * | |
851 | * If the pipe is totally full, don't register the read handler. | |
852 | * The BodyPipe will call our noteMoreBodySpaceAvailable() method | |
853 | * when it has free space again. | |
854 | */ | |
a83c6ed6 AR |
855 | size_t adaptation_space = |
856 | virginBodyDestination->buf().potentialSpaceSize(); | |
7dc79973 | 857 | |
a83c6ed6 | 858 | debugs(11,9, "ServerStateData may read up to min(" << |
9e008dda | 859 | adaptation_space << ", " << space << ") bytes"); |
7dc79973 | 860 | |
a83c6ed6 AR |
861 | if (adaptation_space < space) |
862 | space = adaptation_space; | |
7dc79973 | 863 | } |
864 | #endif | |
865 | ||
866 | return space; | |
867 | } |