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