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