3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
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.
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.
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.
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.
34 #include "acl/FilledChecklist.h"
35 #include "acl/Gadgets.h"
36 #include "base/TextException.h"
37 #include "comm/Connection.h"
38 #include "comm/forward.h"
39 #include "comm/Write.h"
40 #include "err_detail_type.h"
41 #include "errorpage.h"
43 #include "HttpHdrContRange.h"
44 #include "HttpReply.h"
45 #include "HttpRequest.h"
47 #include "SquidConfig.h"
48 #include "SquidTime.h"
49 #include "StatCounters.h"
55 #include "adaptation/AccessCheck.h"
56 #include "adaptation/Answer.h"
57 #include "adaptation/Iterator.h"
58 #include "base/AsyncCall.h"
61 // implemented in client_side_reply.cc until sides have a common parent
62 void purgeEntriesByUrl(HttpRequest
* req
, const char *url
);
64 ServerStateData::ServerStateData(FwdState
*theFwdState
): AsyncJob("ServerStateData"),
67 adaptedHeadSource(NULL
),
68 adaptationAccessCheckPending(false),
69 startedAdaptation(false),
71 receivedWholeRequestBody(false),
78 entry
->lock("ServerStateData");
80 request
= fwd
->request
;
84 ServerStateData::~ServerStateData()
86 // paranoid: check that swanSong has been called
87 assert(!requestBodySource
);
89 assert(!virginBodyDestination
);
90 assert(!adaptedBodySource
);
93 entry
->unlock("ServerStateData");
95 HTTPMSGUNLOCK(request
);
96 HTTPMSGUNLOCK(theVirginReply
);
97 HTTPMSGUNLOCK(theFinalReply
);
99 fwd
= NULL
; // refcounted
101 if (responseBodyBuffer
!= NULL
) {
102 delete responseBodyBuffer
;
103 responseBodyBuffer
= NULL
;
108 ServerStateData::swanSong()
110 // get rid of our piping obligations
111 if (requestBodySource
!= NULL
)
112 stopConsumingFrom(requestBodySource
);
118 BodyConsumer::swanSong();
120 Initiator::swanSong();
121 BodyProducer::swanSong();
124 // paranoid: check that swanSong has been called
125 // extra paranoid: yeah, I really mean it. they MUST pass here.
126 assert(!requestBodySource
);
128 assert(!virginBodyDestination
);
129 assert(!adaptedBodySource
);
134 ServerStateData::virginReply()
136 assert(theVirginReply
);
137 return theVirginReply
;
141 ServerStateData::virginReply() const
143 assert(theVirginReply
);
144 return theVirginReply
;
148 ServerStateData::setVirginReply(HttpReply
*rep
)
150 debugs(11,5, HERE
<< this << " setting virgin reply to " << rep
);
151 assert(!theVirginReply
);
153 theVirginReply
= rep
;
154 HTTPMSGLOCK(theVirginReply
);
155 return theVirginReply
;
159 ServerStateData::finalReply()
161 assert(theFinalReply
);
162 return theFinalReply
;
166 ServerStateData::setFinalReply(HttpReply
*rep
)
168 debugs(11,5, HERE
<< this << " setting final reply to " << rep
);
170 assert(!theFinalReply
);
173 HTTPMSGLOCK(theFinalReply
);
175 // give entry the reply because haveParsedReplyHeaders() expects it there
176 entry
->replaceHttpReply(theFinalReply
, false); // but do not write yet
177 haveParsedReplyHeaders(); // update the entry/reply (e.g., set timestamps)
178 if (!EBIT_TEST(entry
->flags
, RELEASE_REQUEST
) && blockCaching())
180 entry
->startWriting(); // write the updated entry to store
182 return theFinalReply
;
185 // called when no more server communication is expected; may quit
187 ServerStateData::serverComplete()
189 debugs(11,5,HERE
<< "serverComplete " << this);
191 if (!doneWithServer()) {
193 assert(doneWithServer());
198 HttpRequest
*r
= originalRequest();
199 r
->hier
.total_response_time
= r
->hier
.first_conn_start
.tv_sec
?
200 tvSubMsec(r
->hier
.first_conn_start
, current_time
) : -1;
202 if (requestBodySource
!= NULL
)
203 stopConsumingFrom(requestBodySource
);
205 if (responseBodyBuffer
!= NULL
)
212 ServerStateData::serverComplete2()
214 debugs(11,5,HERE
<< "serverComplete2 " << this);
217 if (virginBodyDestination
!= NULL
)
218 stopProducingFor(virginBodyDestination
, true);
220 if (!doneWithAdaptation())
224 completeForwarding();
227 bool ServerStateData::doneAll() const
229 return doneWithServer() &&
231 doneWithAdaptation() &&
232 Adaptation::Initiator::doneAll() &&
233 BodyProducer::doneAll() &&
235 BodyConsumer::doneAll();
238 // FTP side overloads this to work around multiple calls to fwd->complete
240 ServerStateData::completeForwarding()
242 debugs(11,5, HERE
<< "completing forwarding for " << fwd
);
247 // Register to receive request body
248 bool ServerStateData::startRequestBodyFlow()
250 HttpRequest
*r
= originalRequest();
251 assert(r
->body_pipe
!= NULL
);
252 requestBodySource
= r
->body_pipe
;
253 if (requestBodySource
->setConsumerIfNotLate(this)) {
254 debugs(11,3, HERE
<< "expecting request body from " <<
255 requestBodySource
->status());
259 debugs(11,3, HERE
<< "aborting on partially consumed request body: " <<
260 requestBodySource
->status());
261 requestBodySource
= NULL
;
265 // Entry-dependent callbacks use this check to quit if the entry went bad
267 ServerStateData::abortOnBadEntry(const char *abortReason
)
269 if (entry
->isAccepting())
272 debugs(11,5, HERE
<< "entry is not Accepting!");
273 abortTransaction(abortReason
);
277 // more request or adapted response body is available
279 ServerStateData::noteMoreBodyDataAvailable(BodyPipe::Pointer bp
)
282 if (adaptedBodySource
== bp
) {
283 handleMoreAdaptedBodyAvailable();
287 if (requestBodySource
== bp
)
288 handleMoreRequestBodyAvailable();
291 // the entire request or adapted response body was provided, successfully
293 ServerStateData::noteBodyProductionEnded(BodyPipe::Pointer bp
)
296 if (adaptedBodySource
== bp
) {
297 handleAdaptedBodyProductionEnded();
301 if (requestBodySource
== bp
)
302 handleRequestBodyProductionEnded();
305 // premature end of the request or adapted response body production
307 ServerStateData::noteBodyProducerAborted(BodyPipe::Pointer bp
)
310 if (adaptedBodySource
== bp
) {
311 handleAdaptedBodyProducerAborted();
315 if (requestBodySource
== bp
)
316 handleRequestBodyProducerAborted();
319 // more origin request body data is available
321 ServerStateData::handleMoreRequestBodyAvailable()
324 sendMoreRequestBody();
326 debugs(9,3, HERE
<< "waiting for request body write to complete");
329 // there will be no more handleMoreRequestBodyAvailable calls
331 ServerStateData::handleRequestBodyProductionEnded()
333 receivedWholeRequestBody
= true;
335 doneSendingRequestBody();
337 debugs(9,3, HERE
<< "waiting for request body write to complete");
340 // called when we are done sending request body; kids extend this
342 ServerStateData::doneSendingRequestBody()
344 debugs(9,3, HERE
<< "done sending request body");
345 assert(requestBodySource
!= NULL
);
346 stopConsumingFrom(requestBodySource
);
351 // called when body producers aborts; kids extend this
353 ServerStateData::handleRequestBodyProducerAborted()
355 if (requestSender
!= NULL
)
356 debugs(9,3, HERE
<< "fyi: request body aborted while we were sending");
358 fwd
->dontRetry(true); // the problem is not with the server
359 stopConsumingFrom(requestBodySource
); // requestSender, if any, will notice
364 // called when we wrote request headers(!) or a part of the body
366 ServerStateData::sentRequestBody(const CommIoCbParams
&io
)
368 debugs(11, 5, "sentRequestBody: FD " << io
.fd
<< ": size " << io
.size
<< ": errflag " << io
.flag
<< ".");
369 debugs(32,3,HERE
<< "sentRequestBody called");
371 requestSender
= NULL
;
374 fd_bytes(io
.fd
, io
.size
, FD_WRITE
);
375 kb_incr(&(statCounter
.server
.all
.kbytes_out
), io
.size
);
376 // kids should increment their counters
379 if (io
.flag
== COMM_ERR_CLOSING
)
382 if (!requestBodySource
) {
383 debugs(9,3, HERE
<< "detected while-we-were-sending abort");
384 return; // do nothing;
388 debugs(11, DBG_IMPORTANT
, "sentRequestBody error: FD " << io
.fd
<< ": " << xstrerr(io
.xerrno
));
390 err
= new ErrorState(ERR_WRITE_ERROR
, Http::scBadGateway
, fwd
->request
);
391 err
->xerrno
= io
.xerrno
;
393 abortTransaction("I/O error while sending request body");
397 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
398 abortTransaction("store entry aborted while sending request body");
402 if (!requestBodySource
->exhausted())
403 sendMoreRequestBody();
404 else if (receivedWholeRequestBody
)
405 doneSendingRequestBody();
407 debugs(9,3, HERE
<< "waiting for body production end or abort");
411 ServerStateData::sendMoreRequestBody()
413 assert(requestBodySource
!= NULL
);
414 assert(!requestSender
);
416 const Comm::ConnectionPointer conn
= dataConnection();
418 if (!Comm::IsConnOpen(conn
)) {
419 debugs(9,3, HERE
<< "cannot send request body to closing " << conn
);
420 return; // wait for the kid's close handler; TODO: assert(closer);
424 if (getMoreRequestBody(buf
) && buf
.contentSize() > 0) {
425 debugs(9,3, HERE
<< "will write " << buf
.contentSize() << " request body bytes");
426 typedef CommCbMemFunT
<ServerStateData
, CommIoCbParams
> Dialer
;
427 requestSender
= JobCallback(93,3, Dialer
, this, ServerStateData::sentRequestBody
);
428 Comm::Write(conn
, &buf
, requestSender
);
430 debugs(9,3, HERE
<< "will wait for more request body bytes or eof");
431 requestSender
= NULL
;
435 /// either fill buf with available [encoded] request body bytes or return false
437 ServerStateData::getMoreRequestBody(MemBuf
&buf
)
439 // default implementation does not encode request body content
440 Must(requestBodySource
!= NULL
);
441 return requestBodySource
->getMoreData(buf
);
444 // Compares hosts in urls, returns false if different, no sheme, or no host.
446 sameUrlHosts(const char *url1
, const char *url2
)
448 // XXX: Want urlHostname() here, but it uses static storage and copying
449 const char *host1
= strchr(url1
, ':');
450 const char *host2
= strchr(url2
, ':');
452 if (host1
&& host2
) {
453 // skip scheme slashes
457 } while (*host1
== '/' && *host2
== '/');
460 return false; // no host
462 // increment while the same until we reach the end of the URL/host
463 while (*host1
&& *host1
!= '/' && *host1
== *host2
) {
467 return *host1
== *host2
;
470 return false; // no URL scheme
473 // purges entries that match the value of a given HTTP [response] header
475 purgeEntriesByHeader(HttpRequest
*req
, const char *reqUrl
, HttpMsg
*rep
, http_hdr_type hdr
)
477 const char *hdrUrl
, *absUrl
;
480 hdrUrl
= rep
->header
.getStr(hdr
);
481 if (hdrUrl
== NULL
) {
486 * If the URL is relative, make it absolute so we can find it.
487 * If it's absolute, make sure the host parts match to avoid DOS attacks
488 * as per RFC 2616 13.10.
490 if (urlIsRelative(hdrUrl
)) {
491 absUrl
= urlMakeAbsolute(req
, hdrUrl
);
492 if (absUrl
!= NULL
) {
495 } else if (!sameUrlHosts(reqUrl
, hdrUrl
)) {
499 purgeEntriesByUrl(req
, hdrUrl
);
501 if (absUrl
!= NULL
) {
506 // some HTTP methods should purge matching cache entries
508 ServerStateData::maybePurgeOthers()
510 // only some HTTP methods should purge matching cache entries
511 if (!request
->method
.purgesOthers())
514 // and probably only if the response was successful
515 if (theFinalReply
->sline
.status() >= 400)
518 // XXX: should we use originalRequest() here?
519 const char *reqUrl
= urlCanonical(request
);
520 debugs(88, 5, "maybe purging due to " << RequestMethodStr(request
->method
) << ' ' << reqUrl
);
521 purgeEntriesByUrl(request
, reqUrl
);
522 purgeEntriesByHeader(request
, reqUrl
, theFinalReply
, HDR_LOCATION
);
523 purgeEntriesByHeader(request
, reqUrl
, theFinalReply
, HDR_CONTENT_LOCATION
);
526 /// called when we have final (possibly adapted) reply headers; kids extend
528 ServerStateData::haveParsedReplyHeaders()
533 // adaptation may overwrite old offset computed using the virgin response
534 const bool partial
= theFinalReply
->content_range
&&
535 theFinalReply
->sline
.status() == Http::scPartialContent
;
536 currentOffset
= partial
? theFinalReply
->content_range
->spec
.offset
: 0;
539 /// whether to prevent caching of an otherwise cachable response
541 ServerStateData::blockCaching()
543 if (const Acl::Tree
*acl
= Config
.accessList
.storeMiss
) {
544 // This relatively expensive check is not in StoreEntry::checkCachable:
545 // That method lacks HttpRequest and may be called too many times.
546 ACLFilledChecklist
ch(acl
, originalRequest(), NULL
);
547 ch
.reply
= const_cast<HttpReply
*>(entry
->getReply()); // ACLFilledChecklist API bug
548 HTTPMSGLOCK(ch
.reply
);
549 if (ch
.fastCheck() != ACCESS_ALLOWED
) { // when in doubt, block
550 debugs(20, 3, "store_miss prohibits caching");
558 ServerStateData::originalRequest()
564 /// Initiate an asynchronous adaptation transaction which will call us back.
566 ServerStateData::startAdaptation(const Adaptation::ServiceGroupPointer
&group
, HttpRequest
*cause
)
568 debugs(11, 5, "ServerStateData::startAdaptation() called");
569 // check whether we should be sending a body as well
570 // start body pipe to feed ICAP transaction if needed
571 assert(!virginBodyDestination
);
572 HttpReply
*vrep
= virginReply();
573 assert(!vrep
->body_pipe
);
575 if (vrep
->expectingBody(cause
->method
, size
) && size
) {
576 virginBodyDestination
= new BodyPipe(this);
577 vrep
->body_pipe
= virginBodyDestination
;
578 debugs(93, 6, HERE
<< "will send virgin reply body to " <<
579 virginBodyDestination
<< "; size: " << size
);
581 virginBodyDestination
->setBodySize(size
);
584 adaptedHeadSource
= initiateAdaptation(
585 new Adaptation::Iterator(vrep
, cause
, fwd
->al
, group
));
586 startedAdaptation
= initiated(adaptedHeadSource
);
587 Must(startedAdaptation
);
590 // properly cleans up ICAP-related state
591 // may be called multiple times
592 void ServerStateData::cleanAdaptation()
594 debugs(11,5, HERE
<< "cleaning ICAP; ACL: " << adaptationAccessCheckPending
);
596 if (virginBodyDestination
!= NULL
)
597 stopProducingFor(virginBodyDestination
, false);
599 announceInitiatorAbort(adaptedHeadSource
);
601 if (adaptedBodySource
!= NULL
)
602 stopConsumingFrom(adaptedBodySource
);
604 if (!adaptationAccessCheckPending
) // we cannot cancel a pending callback
605 assert(doneWithAdaptation()); // make sure the two methods are in sync
609 ServerStateData::doneWithAdaptation() const
611 return !adaptationAccessCheckPending
&&
612 !virginBodyDestination
&& !adaptedHeadSource
&& !adaptedBodySource
;
615 // sends virgin reply body to ICAP, buffering excesses if needed
617 ServerStateData::adaptVirginReplyBody(const char *data
, ssize_t len
)
619 assert(startedAdaptation
);
621 if (!virginBodyDestination
) {
622 debugs(11,3, HERE
<< "ICAP does not want more virgin body");
626 // grow overflow area if already overflowed
627 if (responseBodyBuffer
) {
628 responseBodyBuffer
->append(data
, len
);
629 data
= responseBodyBuffer
->content();
630 len
= responseBodyBuffer
->contentSize();
633 const ssize_t putSize
= virginBodyDestination
->putMoreData(data
, len
);
637 // if we had overflow area, shrink it as necessary
638 if (responseBodyBuffer
) {
639 if (putSize
== responseBodyBuffer
->contentSize()) {
640 delete responseBodyBuffer
;
641 responseBodyBuffer
= NULL
;
643 responseBodyBuffer
->consume(putSize
);
648 // if we did not have an overflow area, create it as needed
650 assert(!responseBodyBuffer
);
651 responseBodyBuffer
= new MemBuf
;
652 responseBodyBuffer
->init(4096, SQUID_TCP_SO_RCVBUF
* 10);
653 responseBodyBuffer
->append(data
, len
);
657 // can supply more virgin response body data
659 ServerStateData::noteMoreBodySpaceAvailable(BodyPipe::Pointer
)
661 if (responseBodyBuffer
) {
662 addVirginReplyBody(NULL
, 0); // kick the buffered fragment alive again
663 if (completed
&& !responseBodyBuffer
) {
668 maybeReadVirginBody();
671 // the consumer of our virgin response body aborted
673 ServerStateData::noteBodyConsumerAborted(BodyPipe::Pointer
)
675 stopProducingFor(virginBodyDestination
, false);
677 // do not force closeServer here in case we need to bypass AdaptationQueryAbort
679 if (doneWithAdaptation()) // we may still be receiving adapted response
680 handleAdaptationCompleted();
683 // received adapted response headers (body may follow)
685 ServerStateData::noteAdaptationAnswer(const Adaptation::Answer
&answer
)
687 clearAdaptation(adaptedHeadSource
); // we do not expect more messages
689 switch (answer
.kind
) {
690 case Adaptation::Answer::akForward
:
691 handleAdaptedHeader(const_cast<HttpMsg
*>(answer
.message
.getRaw()));
694 case Adaptation::Answer::akBlock
:
695 handleAdaptationBlocked(answer
);
698 case Adaptation::Answer::akError
:
699 handleAdaptationAborted(!answer
.final
);
705 ServerStateData::handleAdaptedHeader(HttpMsg
*msg
)
707 if (abortOnBadEntry("entry went bad while waiting for adapted headers")) {
708 // If the adapted response has a body, the ICAP side needs to know
709 // that nobody will consume that body. We will be destroyed upon
710 // return. Tell the ICAP side that it is on its own.
711 HttpReply
*rep
= dynamic_cast<HttpReply
*>(msg
);
713 if (rep
->body_pipe
!= NULL
)
714 rep
->body_pipe
->expectNoConsumption();
719 HttpReply
*rep
= dynamic_cast<HttpReply
*>(msg
);
721 debugs(11,5, HERE
<< this << " setting adapted reply to " << rep
);
724 assert(!adaptedBodySource
);
725 if (rep
->body_pipe
!= NULL
) {
726 // subscribe to receive adapted body
727 adaptedBodySource
= rep
->body_pipe
;
728 // assume that ICAP does not auto-consume on failures
729 const bool result
= adaptedBodySource
->setConsumerIfNotLate(this);
733 if (doneWithAdaptation()) // we may still be sending virgin response
734 handleAdaptationCompleted();
739 ServerStateData::resumeBodyStorage()
741 if (abortOnBadEntry("store entry aborted while kick producer callback"))
744 if (!adaptedBodySource
)
747 handleMoreAdaptedBodyAvailable();
749 if (adaptedBodySource
!= NULL
&& adaptedBodySource
->exhausted())
750 endAdaptedBodyConsumption();
753 // more adapted response body is available
755 ServerStateData::handleMoreAdaptedBodyAvailable()
757 if (abortOnBadEntry("entry refuses adapted body"))
762 size_t contentSize
= adaptedBodySource
->buf().contentSize();
765 return; // XXX: bytesWanted asserts on zero-size ranges
767 const size_t spaceAvailable
= entry
->bytesWanted(Range
<size_t>(0, contentSize
), true);
769 if (spaceAvailable
< contentSize
) {
770 // No or partial body data consuming
771 typedef NullaryMemFunT
<ServerStateData
> Dialer
;
772 AsyncCall::Pointer call
= asyncCall(93, 5, "ServerStateData::resumeBodyStorage",
773 Dialer(this, &ServerStateData::resumeBodyStorage
));
774 entry
->deferProducer(call
);
777 if (!spaceAvailable
) {
778 debugs(11, 5, HERE
<< "NOT storing " << contentSize
<< " bytes of adapted " <<
779 "response body at offset " << adaptedBodySource
->consumedSize());
783 if (spaceAvailable
< contentSize
) {
784 debugs(11, 5, HERE
<< "postponing storage of " <<
785 (contentSize
- spaceAvailable
) << " body bytes");
786 contentSize
= spaceAvailable
;
789 debugs(11,5, HERE
<< "storing " << contentSize
<< " bytes of adapted " <<
790 "response body at offset " << adaptedBodySource
->consumedSize());
792 BodyPipeCheckout
bpc(*adaptedBodySource
);
793 const StoreIOBuffer
ioBuf(&bpc
.buf
, currentOffset
, contentSize
);
794 currentOffset
+= ioBuf
.length
;
796 bpc
.buf
.consume(contentSize
);
800 // the entire adapted response body was produced, successfully
802 ServerStateData::handleAdaptedBodyProductionEnded()
804 if (abortOnBadEntry("entry went bad while waiting for adapted body eof"))
807 // end consumption if we consumed everything
808 if (adaptedBodySource
!= NULL
&& adaptedBodySource
->exhausted())
809 endAdaptedBodyConsumption();
810 // else resumeBodyStorage() will eventually consume the rest
814 ServerStateData::endAdaptedBodyConsumption()
816 stopConsumingFrom(adaptedBodySource
);
817 handleAdaptationCompleted();
820 // premature end of the adapted response body
821 void ServerStateData::handleAdaptedBodyProducerAborted()
823 stopConsumingFrom(adaptedBodySource
);
824 handleAdaptationAborted();
827 // common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
829 ServerStateData::handleAdaptationCompleted()
831 debugs(11,5, HERE
<< "handleAdaptationCompleted");
834 // We stop reading origin response because we have no place to put it and
835 // cannot use it. If some origin servers do not like that or if we want to
836 // reuse more pconns, we can add code to discard unneeded origin responses.
837 if (!doneWithServer()) {
838 debugs(11,3, HERE
<< "closing origin conn due to ICAP completion");
842 completeForwarding();
845 // common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods
847 ServerStateData::handleAdaptationAborted(bool bypassable
)
849 debugs(11,5, HERE
<< "handleAdaptationAborted; bypassable: " << bypassable
<<
850 ", entry empty: " << entry
->isEmpty());
852 if (abortOnBadEntry("entry went bad while ICAP aborted"))
855 // TODO: bypass if possible
857 if (entry
->isEmpty()) {
858 debugs(11,9, HERE
<< "creating ICAP error entry after ICAP failure");
859 ErrorState
*err
= new ErrorState(ERR_ICAP_FAILURE
, Http::scInternalServerError
, request
);
860 err
->detailError(ERR_DETAIL_ICAP_RESPMOD_EARLY
);
862 fwd
->dontRetry(true);
863 } else if (request
) { // update logged info directly
864 request
->detailError(ERR_ICAP_FAILURE
, ERR_DETAIL_ICAP_RESPMOD_LATE
);
867 abortTransaction("ICAP failure");
870 // adaptation service wants us to deny HTTP client access to this response
872 ServerStateData::handleAdaptationBlocked(const Adaptation::Answer
&answer
)
874 debugs(11,5, HERE
<< answer
.ruleId
);
876 if (abortOnBadEntry("entry went bad while ICAP aborted"))
879 if (!entry
->isEmpty()) { // too late to block (should not really happen)
881 request
->detailError(ERR_ICAP_FAILURE
, ERR_DETAIL_RESPMOD_BLOCK_LATE
);
882 abortTransaction("late adaptation block");
886 debugs(11,7, HERE
<< "creating adaptation block response");
889 aclGetDenyInfoPage(&Config
.denyInfoList
, answer
.ruleId
.termedBuf(), 1);
890 if (page_id
== ERR_NONE
)
891 page_id
= ERR_ACCESS_DENIED
;
893 ErrorState
*err
= new ErrorState(page_id
, Http::scForbidden
, request
);
894 err
->detailError(ERR_DETAIL_RESPMOD_BLOCK_EARLY
);
896 fwd
->dontRetry(true);
898 abortTransaction("timely adaptation block");
902 ServerStateData::noteAdaptationAclCheckDone(Adaptation::ServiceGroupPointer group
)
904 adaptationAccessCheckPending
= false;
906 if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
909 // TODO: Should nonICAP and postICAP path check this on the server-side?
910 // That check now only happens on client-side, in processReplyAccess().
911 if (virginReply()->expectedBodyTooLarge(*request
)) {
912 sendBodyIsTooLargeError();
915 // TODO: Should we check receivedBodyTooLarge on the server-side as well?
918 debugs(11,3, HERE
<< "no adapation needed");
919 setFinalReply(virginReply());
924 startAdaptation(group
, originalRequest());
930 ServerStateData::sendBodyIsTooLargeError()
932 ErrorState
*err
= new ErrorState(ERR_TOO_BIG
, Http::scForbidden
, request
);
934 fwd
->dontRetry(true);
935 abortTransaction("Virgin body too large.");
938 // TODO: when HttpStateData sends all errors to ICAP,
939 // we should be able to move this at the end of setVirginReply().
941 ServerStateData::adaptOrFinalizeReply()
944 // TODO: merge with client side and return void to hide the on/off logic?
945 // The callback can be called with a NULL service if adaptation is off.
946 adaptationAccessCheckPending
= Adaptation::AccessCheck::Start(
947 Adaptation::methodRespmod
, Adaptation::pointPreCache
,
948 originalRequest(), virginReply(), fwd
->al
, this);
949 debugs(11,5, HERE
<< "adaptationAccessCheckPending=" << adaptationAccessCheckPending
);
950 if (adaptationAccessCheckPending
)
954 setFinalReply(virginReply());
957 /// initializes bodyBytesRead stats if needed and applies delta
959 ServerStateData::adjustBodyBytesRead(const int64_t delta
)
961 int64_t &bodyBytesRead
= originalRequest()->hier
.bodyBytesRead
;
963 // if we got here, do not log a dash even if we got nothing from the server
964 if (bodyBytesRead
< 0)
967 bodyBytesRead
+= delta
; // supports negative and zero deltas
969 // check for overflows ("infinite" response?) and undeflows (a bug)
970 Must(bodyBytesRead
>= 0);
974 ServerStateData::addVirginReplyBody(const char *data
, ssize_t len
)
976 adjustBodyBytesRead(len
);
979 assert(!adaptationAccessCheckPending
); // or would need to buffer while waiting
980 if (startedAdaptation
) {
981 adaptVirginReplyBody(data
, len
);
985 storeReplyBody(data
, len
);
988 // writes virgin or adapted reply body to store
990 ServerStateData::storeReplyBody(const char *data
, ssize_t len
)
992 // write even if len is zero to push headers towards the client side
993 entry
->write (StoreIOBuffer(len
, currentOffset
, (char*)data
));
995 currentOffset
+= len
;
998 size_t ServerStateData::replyBodySpace(const MemBuf
&readBuf
,
999 const size_t minSpace
) const
1001 size_t space
= readBuf
.spaceSize(); // available space w/o heroic measures
1002 if (space
< minSpace
) {
1003 const size_t maxSpace
= readBuf
.potentialSpaceSize(); // absolute best
1004 space
= min(minSpace
, maxSpace
); // do not promise more than asked
1008 if (responseBodyBuffer
) {
1009 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
1012 if (virginBodyDestination
!= NULL
) {
1014 * BodyPipe buffer has a finite size limit. We
1015 * should not read more data from the network than will fit
1016 * into the pipe buffer or we _lose_ what did not fit if
1017 * the response ends sooner that BodyPipe frees up space:
1018 * There is no code to keep pumping data into the pipe once
1019 * response ends and serverComplete() is called.
1021 * If the pipe is totally full, don't register the read handler.
1022 * The BodyPipe will call our noteMoreBodySpaceAvailable() method
1023 * when it has free space again.
1025 size_t adaptation_space
=
1026 virginBodyDestination
->buf().potentialSpaceSize();
1028 debugs(11,9, "ServerStateData may read up to min(" <<
1029 adaptation_space
<< ", " << space
<< ") bytes");
1031 if (adaptation_space
< space
)
1032 space
= adaptation_space
;