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