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