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