5 * AUTHOR: Duane Wessels
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
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.
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.
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.
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.
35 #include "squid-old.h"
36 #include "acl/Gadgets.h"
37 #include "base/TextException.h"
38 #include "comm/Connection.h"
39 #include "comm/forward.h"
40 #include "comm/Write.h"
43 #include "HttpRequest.h"
44 #include "HttpReply.h"
45 #include "errorpage.h"
46 #include "err_detail_type.h"
47 #include "StatCounters.h"
48 #include "SquidTime.h"
51 #include "adaptation/AccessCheck.h"
52 #include "adaptation/Answer.h"
53 #include "adaptation/Iterator.h"
54 #include "base/AsyncCall.h"
57 // implemented in client_side_reply.cc until sides have a common parent
58 extern void purgeEntriesByUrl(HttpRequest
* req
, const char *url
);
61 ServerStateData::ServerStateData(FwdState
*theFwdState
): AsyncJob("ServerStateData"),
64 adaptedHeadSource(NULL
),
65 adaptationAccessCheckPending(false),
66 startedAdaptation(false),
68 receivedWholeRequestBody(false),
77 request
= HTTPMSGLOCK(fwd
->request
);
80 ServerStateData::~ServerStateData()
82 // paranoid: check that swanSong has been called
83 assert(!requestBodySource
);
85 assert(!virginBodyDestination
);
86 assert(!adaptedBodySource
);
91 HTTPMSGUNLOCK(request
);
92 HTTPMSGUNLOCK(theVirginReply
);
93 HTTPMSGUNLOCK(theFinalReply
);
95 fwd
= NULL
; // refcounted
97 if (responseBodyBuffer
!= NULL
) {
98 delete responseBodyBuffer
;
99 responseBodyBuffer
= NULL
;
104 ServerStateData::swanSong()
106 // get rid of our piping obligations
107 if (requestBodySource
!= NULL
)
108 stopConsumingFrom(requestBodySource
);
114 BodyConsumer::swanSong();
116 Initiator::swanSong();
117 BodyProducer::swanSong();
120 // paranoid: check that swanSong has been called
121 // extra paranoid: yeah, I really mean it. they MUST pass here.
122 assert(!requestBodySource
);
124 assert(!virginBodyDestination
);
125 assert(!adaptedBodySource
);
131 ServerStateData::virginReply()
133 assert(theVirginReply
);
134 return theVirginReply
;
138 ServerStateData::virginReply() const
140 assert(theVirginReply
);
141 return theVirginReply
;
145 ServerStateData::setVirginReply(HttpReply
*rep
)
147 debugs(11,5, HERE
<< this << " setting virgin reply to " << rep
);
148 assert(!theVirginReply
);
150 theVirginReply
= HTTPMSGLOCK(rep
);
151 return theVirginReply
;
155 ServerStateData::finalReply()
157 assert(theFinalReply
);
158 return theFinalReply
;
162 ServerStateData::setFinalReply(HttpReply
*rep
)
164 debugs(11,5, HERE
<< this << " setting final reply to " << rep
);
166 assert(!theFinalReply
);
168 theFinalReply
= HTTPMSGLOCK(rep
);
170 // give entry the reply because haveParsedReplyHeaders() expects it there
171 entry
->replaceHttpReply(theFinalReply
, false); // but do not write yet
172 haveParsedReplyHeaders(); // update the entry/reply (e.g., set timestamps)
173 entry
->startWriting(); // write the updated entry to store
175 return theFinalReply
;
178 // called when no more server communication is expected; may quit
180 ServerStateData::serverComplete()
182 debugs(11,5,HERE
<< "serverComplete " << this);
184 if (!doneWithServer()) {
186 assert(doneWithServer());
191 HttpRequest
*r
= originalRequest();
192 r
->hier
.total_response_time
= r
->hier
.first_conn_start
.tv_sec
?
193 tvSubMsec(r
->hier
.first_conn_start
, current_time
) : -1;
195 if (requestBodySource
!= NULL
)
196 stopConsumingFrom(requestBodySource
);
198 if (responseBodyBuffer
!= NULL
)
205 ServerStateData::serverComplete2()
207 debugs(11,5,HERE
<< "serverComplete2 " << this);
210 if (virginBodyDestination
!= NULL
)
211 stopProducingFor(virginBodyDestination
, true);
213 if (!doneWithAdaptation())
217 completeForwarding();
220 bool ServerStateData::doneAll() const
222 return doneWithServer() &&
224 doneWithAdaptation() &&
225 Adaptation::Initiator::doneAll() &&
226 BodyProducer::doneAll() &&
228 BodyConsumer::doneAll();
231 // FTP side overloads this to work around multiple calls to fwd->complete
233 ServerStateData::completeForwarding()
235 debugs(11,5, HERE
<< "completing forwarding for " << fwd
);
240 // Register to receive request body
241 bool ServerStateData::startRequestBodyFlow()
243 HttpRequest
*r
= originalRequest();
244 assert(r
->body_pipe
!= NULL
);
245 requestBodySource
= r
->body_pipe
;
246 if (requestBodySource
->setConsumerIfNotLate(this)) {
247 debugs(11,3, HERE
<< "expecting request body from " <<
248 requestBodySource
->status());
252 debugs(11,3, HERE
<< "aborting on partially consumed request body: " <<
253 requestBodySource
->status());
254 requestBodySource
= NULL
;
258 // Entry-dependent callbacks use this check to quit if the entry went bad
260 ServerStateData::abortOnBadEntry(const char *abortReason
)
262 if (entry
->isAccepting())
265 debugs(11,5, HERE
<< "entry is not Accepting!");
266 abortTransaction(abortReason
);
270 // more request or adapted response body is available
272 ServerStateData::noteMoreBodyDataAvailable(BodyPipe::Pointer bp
)
275 if (adaptedBodySource
== bp
) {
276 handleMoreAdaptedBodyAvailable();
280 if (requestBodySource
== bp
)
281 handleMoreRequestBodyAvailable();
284 // the entire request or adapted response body was provided, successfully
286 ServerStateData::noteBodyProductionEnded(BodyPipe::Pointer bp
)
289 if (adaptedBodySource
== bp
) {
290 handleAdaptedBodyProductionEnded();
294 if (requestBodySource
== bp
)
295 handleRequestBodyProductionEnded();
298 // premature end of the request or adapted response body production
300 ServerStateData::noteBodyProducerAborted(BodyPipe::Pointer bp
)
303 if (adaptedBodySource
== bp
) {
304 handleAdaptedBodyProducerAborted();
308 if (requestBodySource
== bp
)
309 handleRequestBodyProducerAborted();
313 // more origin request body data is available
315 ServerStateData::handleMoreRequestBodyAvailable()
318 sendMoreRequestBody();
320 debugs(9,3, HERE
<< "waiting for request body write to complete");
323 // there will be no more handleMoreRequestBodyAvailable calls
325 ServerStateData::handleRequestBodyProductionEnded()
327 receivedWholeRequestBody
= true;
329 doneSendingRequestBody();
331 debugs(9,3, HERE
<< "waiting for request body write to complete");
334 // called when we are done sending request body; kids extend this
336 ServerStateData::doneSendingRequestBody()
338 debugs(9,3, HERE
<< "done sending request body");
339 assert(requestBodySource
!= NULL
);
340 stopConsumingFrom(requestBodySource
);
345 // called when body producers aborts; kids extend this
347 ServerStateData::handleRequestBodyProducerAborted()
349 if (requestSender
!= NULL
)
350 debugs(9,3, HERE
<< "fyi: request body aborted while we were sending");
352 fwd
->dontRetry(true); // the problem is not with the server
353 stopConsumingFrom(requestBodySource
); // requestSender, if any, will notice
358 // called when we wrote request headers(!) or a part of the body
360 ServerStateData::sentRequestBody(const CommIoCbParams
&io
)
362 debugs(11, 5, "sentRequestBody: FD " << io
.fd
<< ": size " << io
.size
<< ": errflag " << io
.flag
<< ".");
363 debugs(32,3,HERE
<< "sentRequestBody called");
365 requestSender
= NULL
;
368 fd_bytes(io
.fd
, io
.size
, FD_WRITE
);
369 kb_incr(&(statCounter
.server
.all
.kbytes_out
), io
.size
);
370 // kids should increment their counters
373 if (io
.flag
== COMM_ERR_CLOSING
)
376 if (!requestBodySource
) {
377 debugs(9,3, HERE
<< "detected while-we-were-sending abort");
378 return; // do nothing;
382 debugs(11, 1, "sentRequestBody error: FD " << io
.fd
<< ": " << xstrerr(io
.xerrno
));
384 err
= new ErrorState(ERR_WRITE_ERROR
, HTTP_BAD_GATEWAY
, fwd
->request
);
385 err
->xerrno
= io
.xerrno
;
387 abortTransaction("I/O error while sending request body");
391 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
392 abortTransaction("store entry aborted while sending request body");
396 if (!requestBodySource
->exhausted())
397 sendMoreRequestBody();
398 else if (receivedWholeRequestBody
)
399 doneSendingRequestBody();
401 debugs(9,3, HERE
<< "waiting for body production end or abort");
405 ServerStateData::sendMoreRequestBody()
407 assert(requestBodySource
!= NULL
);
408 assert(!requestSender
);
410 const Comm::ConnectionPointer conn
= dataConnection();
412 if (!Comm::IsConnOpen(conn
)) {
413 debugs(9,3, HERE
<< "cannot send request body to closing " << conn
);
414 return; // wait for the kid's close handler; TODO: assert(closer);
418 if (getMoreRequestBody(buf
) && buf
.contentSize() > 0) {
419 debugs(9,3, HERE
<< "will write " << buf
.contentSize() << " request body bytes");
420 typedef CommCbMemFunT
<ServerStateData
, CommIoCbParams
> Dialer
;
421 requestSender
= JobCallback(93,3, Dialer
, this, ServerStateData::sentRequestBody
);
422 Comm::Write(conn
, &buf
, requestSender
);
424 debugs(9,3, HERE
<< "will wait for more request body bytes or eof");
425 requestSender
= NULL
;
429 /// either fill buf with available [encoded] request body bytes or return false
431 ServerStateData::getMoreRequestBody(MemBuf
&buf
)
433 // default implementation does not encode request body content
434 Must(requestBodySource
!= NULL
);
435 return requestBodySource
->getMoreData(buf
);
438 // Compares hosts in urls, returns false if different, no sheme, or no host.
440 sameUrlHosts(const char *url1
, const char *url2
)
442 // XXX: Want urlHostname() here, but it uses static storage and copying
443 const char *host1
= strchr(url1
, ':');
444 const char *host2
= strchr(url2
, ':');
446 if (host1
&& host2
) {
447 // skip scheme slashes
451 } while (*host1
== '/' && *host2
== '/');
454 return false; // no host
456 // increment while the same until we reach the end of the URL/host
457 while (*host1
&& *host1
!= '/' && *host1
== *host2
) {
461 return *host1
== *host2
;
464 return false; // no URL scheme
467 // purges entries that match the value of a given HTTP [response] header
469 purgeEntriesByHeader(HttpRequest
*req
, const char *reqUrl
, HttpMsg
*rep
, http_hdr_type hdr
)
471 const char *hdrUrl
, *absUrl
;
474 hdrUrl
= rep
->header
.getStr(hdr
);
475 if (hdrUrl
== NULL
) {
480 * If the URL is relative, make it absolute so we can find it.
481 * If it's absolute, make sure the host parts match to avoid DOS attacks
482 * as per RFC 2616 13.10.
484 if (urlIsRelative(hdrUrl
)) {
485 absUrl
= urlMakeAbsolute(req
, hdrUrl
);
486 if (absUrl
!= NULL
) {
489 } else if (!sameUrlHosts(reqUrl
, hdrUrl
)) {
493 purgeEntriesByUrl(req
, hdrUrl
);
495 if (absUrl
!= NULL
) {
500 // some HTTP methods should purge matching cache entries
502 ServerStateData::maybePurgeOthers()
504 // only some HTTP methods should purge matching cache entries
505 if (!request
->method
.purgesOthers())
508 // and probably only if the response was successful
509 if (theFinalReply
->sline
.status
>= 400)
512 // XXX: should we use originalRequest() here?
513 const char *reqUrl
= urlCanonical(request
);
514 debugs(88, 5, "maybe purging due to " << RequestMethodStr(request
->method
) << ' ' << reqUrl
);
515 purgeEntriesByUrl(request
, reqUrl
);
516 purgeEntriesByHeader(request
, reqUrl
, theFinalReply
, HDR_LOCATION
);
517 purgeEntriesByHeader(request
, reqUrl
, theFinalReply
, HDR_CONTENT_LOCATION
);
520 /// called when we have final (possibly adapted) reply headers; kids extend
522 ServerStateData::haveParsedReplyHeaders()
529 ServerStateData::originalRequest()
535 /// Initiate an asynchronous adaptation transaction which will call us back.
537 ServerStateData::startAdaptation(const Adaptation::ServiceGroupPointer
&group
, HttpRequest
*cause
)
539 debugs(11, 5, "ServerStateData::startAdaptation() called");
540 // check whether we should be sending a body as well
541 // start body pipe to feed ICAP transaction if needed
542 assert(!virginBodyDestination
);
543 HttpReply
*vrep
= virginReply();
544 assert(!vrep
->body_pipe
);
546 if (vrep
->expectingBody(cause
->method
, size
) && size
) {
547 virginBodyDestination
= new BodyPipe(this);
548 vrep
->body_pipe
= virginBodyDestination
;
549 debugs(93, 6, HERE
<< "will send virgin reply body to " <<
550 virginBodyDestination
<< "; size: " << size
);
552 virginBodyDestination
->setBodySize(size
);
555 adaptedHeadSource
= initiateAdaptation(
556 new Adaptation::Iterator(vrep
, cause
, group
));
557 startedAdaptation
= initiated(adaptedHeadSource
);
558 Must(startedAdaptation
);
561 // properly cleans up ICAP-related state
562 // may be called multiple times
563 void ServerStateData::cleanAdaptation()
565 debugs(11,5, HERE
<< "cleaning ICAP; ACL: " << adaptationAccessCheckPending
);
567 if (virginBodyDestination
!= NULL
)
568 stopProducingFor(virginBodyDestination
, false);
570 announceInitiatorAbort(adaptedHeadSource
);
572 if (adaptedBodySource
!= NULL
)
573 stopConsumingFrom(adaptedBodySource
);
575 if (!adaptationAccessCheckPending
) // we cannot cancel a pending callback
576 assert(doneWithAdaptation()); // make sure the two methods are in sync
580 ServerStateData::doneWithAdaptation() const
582 return !adaptationAccessCheckPending
&&
583 !virginBodyDestination
&& !adaptedHeadSource
&& !adaptedBodySource
;
586 // sends virgin reply body to ICAP, buffering excesses if needed
588 ServerStateData::adaptVirginReplyBody(const char *data
, ssize_t len
)
590 assert(startedAdaptation
);
592 if (!virginBodyDestination
) {
593 debugs(11,3, HERE
<< "ICAP does not want more virgin body");
597 // grow overflow area if already overflowed
598 if (responseBodyBuffer
) {
599 responseBodyBuffer
->append(data
, len
);
600 data
= responseBodyBuffer
->content();
601 len
= responseBodyBuffer
->contentSize();
604 const ssize_t putSize
= virginBodyDestination
->putMoreData(data
, len
);
608 // if we had overflow area, shrink it as necessary
609 if (responseBodyBuffer
) {
610 if (putSize
== responseBodyBuffer
->contentSize()) {
611 delete responseBodyBuffer
;
612 responseBodyBuffer
= NULL
;
614 responseBodyBuffer
->consume(putSize
);
619 // if we did not have an overflow area, create it as needed
621 assert(!responseBodyBuffer
);
622 responseBodyBuffer
= new MemBuf
;
623 responseBodyBuffer
->init(4096, SQUID_TCP_SO_RCVBUF
* 10);
624 responseBodyBuffer
->append(data
, len
);
628 // can supply more virgin response body data
630 ServerStateData::noteMoreBodySpaceAvailable(BodyPipe::Pointer
)
632 if (responseBodyBuffer
) {
633 addVirginReplyBody(NULL
, 0); // kick the buffered fragment alive again
634 if (completed
&& !responseBodyBuffer
) {
639 maybeReadVirginBody();
642 // the consumer of our virgin response body aborted
644 ServerStateData::noteBodyConsumerAborted(BodyPipe::Pointer
)
646 stopProducingFor(virginBodyDestination
, false);
648 // do not force closeServer here in case we need to bypass AdaptationQueryAbort
650 if (doneWithAdaptation()) // we may still be receiving adapted response
651 handleAdaptationCompleted();
654 // received adapted response headers (body may follow)
656 ServerStateData::noteAdaptationAnswer(const Adaptation::Answer
&answer
)
658 clearAdaptation(adaptedHeadSource
); // we do not expect more messages
660 switch (answer
.kind
) {
661 case Adaptation::Answer::akForward
:
662 handleAdaptedHeader(answer
.message
);
665 case Adaptation::Answer::akBlock
:
666 handleAdaptationBlocked(answer
);
669 case Adaptation::Answer::akError
:
670 handleAdaptationAborted(!answer
.final
);
676 ServerStateData::handleAdaptedHeader(HttpMsg
*msg
)
678 if (abortOnBadEntry("entry went bad while waiting for adapted headers")) {
679 // If the adapted response has a body, the ICAP side needs to know
680 // that nobody will consume that body. We will be destroyed upon
681 // return. Tell the ICAP side that it is on its own.
682 HttpReply
*rep
= dynamic_cast<HttpReply
*>(msg
);
684 if (rep
->body_pipe
!= NULL
)
685 rep
->body_pipe
->expectNoConsumption();
690 HttpReply
*rep
= dynamic_cast<HttpReply
*>(msg
);
692 debugs(11,5, HERE
<< this << " setting adapted reply to " << rep
);
695 assert(!adaptedBodySource
);
696 if (rep
->body_pipe
!= NULL
) {
697 // subscribe to receive adapted body
698 adaptedBodySource
= rep
->body_pipe
;
699 // assume that ICAP does not auto-consume on failures
700 assert(adaptedBodySource
->setConsumerIfNotLate(this));
703 if (doneWithAdaptation()) // we may still be sending virgin response
704 handleAdaptationCompleted();
709 ServerStateData::resumeBodyStorage()
711 if (abortOnBadEntry("store entry aborted while kick producer callback"))
714 if (!adaptedBodySource
)
717 handleMoreAdaptedBodyAvailable();
719 if (adaptedBodySource
!= NULL
&& adaptedBodySource
->exhausted())
720 endAdaptedBodyConsumption();
723 // more adapted response body is available
725 ServerStateData::handleMoreAdaptedBodyAvailable()
727 if (abortOnBadEntry("entry refuses adapted body"))
732 size_t contentSize
= adaptedBodySource
->buf().contentSize();
735 return; // XXX: bytesWanted asserts on zero-size ranges
737 // XXX: entry->bytesWanted returns contentSize-1 if entry can accept data.
738 // We have to add 1 to avoid suspending forever.
739 const size_t bytesWanted
= entry
->bytesWanted(Range
<size_t>(0, contentSize
));
740 const size_t spaceAvailable
= bytesWanted
> 0 ? (bytesWanted
+ 1) : 0;
742 if (spaceAvailable
< contentSize
) {
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
);
750 // XXX: bytesWanted API does not allow us to write just one byte!
751 if (!spaceAvailable
&& contentSize
> 1) {
752 debugs(11, 5, HERE
<< "NOT storing " << contentSize
<< " bytes of adapted " <<
753 "response body at offset " << adaptedBodySource
->consumedSize());
757 if (spaceAvailable
< contentSize
) {
758 debugs(11, 5, HERE
<< "postponing storage of " <<
759 (contentSize
- spaceAvailable
) << " body bytes");
760 contentSize
= spaceAvailable
;
763 debugs(11,5, HERE
<< "storing " << contentSize
<< " bytes of adapted " <<
764 "response body at offset " << adaptedBodySource
->consumedSize());
766 BodyPipeCheckout
bpc(*adaptedBodySource
);
767 const StoreIOBuffer
ioBuf(&bpc
.buf
, currentOffset
, contentSize
);
768 currentOffset
+= ioBuf
.length
;
770 bpc
.buf
.consume(contentSize
);
774 // the entire adapted response body was produced, successfully
776 ServerStateData::handleAdaptedBodyProductionEnded()
778 if (abortOnBadEntry("entry went bad while waiting for adapted body eof"))
781 // end consumption if we consumed everything
782 if (adaptedBodySource
!= NULL
&& adaptedBodySource
->exhausted())
783 endAdaptedBodyConsumption();
784 // else resumeBodyStorage() will eventually consume the rest
788 ServerStateData::endAdaptedBodyConsumption()
790 stopConsumingFrom(adaptedBodySource
);
791 handleAdaptationCompleted();
794 // premature end of the adapted response body
795 void ServerStateData::handleAdaptedBodyProducerAborted()
797 stopConsumingFrom(adaptedBodySource
);
798 handleAdaptationAborted();
801 // common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
803 ServerStateData::handleAdaptationCompleted()
805 debugs(11,5, HERE
<< "handleAdaptationCompleted");
808 // We stop reading origin response because we have no place to put it and
809 // cannot use it. If some origin servers do not like that or if we want to
810 // reuse more pconns, we can add code to discard unneeded origin responses.
811 if (!doneWithServer()) {
812 debugs(11,3, HERE
<< "closing origin conn due to ICAP completion");
816 completeForwarding();
820 // common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods
822 ServerStateData::handleAdaptationAborted(bool bypassable
)
824 debugs(11,5, HERE
<< "handleAdaptationAborted; bypassable: " << bypassable
<<
825 ", entry empty: " << entry
->isEmpty());
827 if (abortOnBadEntry("entry went bad while ICAP aborted"))
830 // TODO: bypass if possible
832 if (entry
->isEmpty()) {
833 debugs(11,9, HERE
<< "creating ICAP error entry after ICAP failure");
834 ErrorState
*err
= new ErrorState(ERR_ICAP_FAILURE
, HTTP_INTERNAL_SERVER_ERROR
, request
);
835 err
->xerrno
= ERR_DETAIL_ICAP_RESPMOD_EARLY
;
837 fwd
->dontRetry(true);
838 } else if (request
) { // update logged info directly
839 request
->detailError(ERR_ICAP_FAILURE
, ERR_DETAIL_ICAP_RESPMOD_LATE
);
842 abortTransaction("ICAP failure");
845 // adaptation service wants us to deny HTTP client access to this response
847 ServerStateData::handleAdaptationBlocked(const Adaptation::Answer
&answer
)
849 debugs(11,5, HERE
<< answer
.ruleId
);
851 if (abortOnBadEntry("entry went bad while ICAP aborted"))
854 if (!entry
->isEmpty()) { // too late to block (should not really happen)
856 request
->detailError(ERR_ICAP_FAILURE
, ERR_DETAIL_RESPMOD_BLOCK_LATE
);
857 abortTransaction("late adaptation block");
861 debugs(11,7, HERE
<< "creating adaptation block response");
864 aclGetDenyInfoPage(&Config
.denyInfoList
, answer
.ruleId
.termedBuf(), 1);
865 if (page_id
== ERR_NONE
)
866 page_id
= ERR_ACCESS_DENIED
;
868 ErrorState
*err
= new ErrorState(page_id
, HTTP_FORBIDDEN
, request
);
869 err
->xerrno
= ERR_DETAIL_RESPMOD_BLOCK_EARLY
;
871 fwd
->dontRetry(true);
873 abortTransaction("timely adaptation block");
877 ServerStateData::noteAdaptationAclCheckDone(Adaptation::ServiceGroupPointer group
)
879 adaptationAccessCheckPending
= false;
881 if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
884 // TODO: Should nonICAP and postICAP path check this on the server-side?
885 // That check now only happens on client-side, in processReplyAccess().
886 if (virginReply()->expectedBodyTooLarge(*request
)) {
887 sendBodyIsTooLargeError();
890 // TODO: Should we check receivedBodyTooLarge on the server-side as well?
893 debugs(11,3, HERE
<< "no adapation needed");
894 setFinalReply(virginReply());
899 startAdaptation(group
, originalRequest());
905 ServerStateData::sendBodyIsTooLargeError()
907 ErrorState
*err
= new ErrorState(ERR_TOO_BIG
, HTTP_FORBIDDEN
, request
);
910 fwd
->dontRetry(true);
911 abortTransaction("Virgin body too large.");
914 // TODO: when HttpStateData sends all errors to ICAP,
915 // we should be able to move this at the end of setVirginReply().
917 ServerStateData::adaptOrFinalizeReply()
920 // TODO: merge with client side and return void to hide the on/off logic?
921 // The callback can be called with a NULL service if adaptation is off.
922 adaptationAccessCheckPending
= Adaptation::AccessCheck::Start(
923 Adaptation::methodRespmod
, Adaptation::pointPreCache
,
924 originalRequest(), virginReply(), this);
925 debugs(11,5, HERE
<< "adaptationAccessCheckPending=" << adaptationAccessCheckPending
);
926 if (adaptationAccessCheckPending
)
930 setFinalReply(virginReply());
933 /// initializes bodyBytesRead stats if needed and applies delta
935 ServerStateData::adjustBodyBytesRead(const int64_t delta
)
937 int64_t &bodyBytesRead
= originalRequest()->hier
.bodyBytesRead
;
939 // if we got here, do not log a dash even if we got nothing from the server
940 if (bodyBytesRead
< 0)
943 bodyBytesRead
+= delta
; // supports negative and zero deltas
945 // check for overflows ("infinite" response?) and undeflows (a bug)
946 Must(bodyBytesRead
>= 0);
950 ServerStateData::addVirginReplyBody(const char *data
, ssize_t len
)
952 adjustBodyBytesRead(len
);
955 assert(!adaptationAccessCheckPending
); // or would need to buffer while waiting
956 if (startedAdaptation
) {
957 adaptVirginReplyBody(data
, len
);
961 storeReplyBody(data
, len
);
964 // writes virgin or adapted reply body to store
966 ServerStateData::storeReplyBody(const char *data
, ssize_t len
)
968 // write even if len is zero to push headers towards the client side
969 entry
->write (StoreIOBuffer(len
, currentOffset
, (char*)data
));
971 currentOffset
+= len
;
974 size_t ServerStateData::replyBodySpace(const MemBuf
&readBuf
,
975 const size_t minSpace
) const
977 size_t space
= readBuf
.spaceSize(); // available space w/o heroic measures
978 if (space
< minSpace
) {
979 const size_t maxSpace
= readBuf
.potentialSpaceSize(); // absolute best
980 space
= min(minSpace
, maxSpace
); // do not promise more than asked
984 if (responseBodyBuffer
) {
985 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
988 if (virginBodyDestination
!= NULL
) {
990 * BodyPipe buffer has a finite size limit. We
991 * should not read more data from the network than will fit
992 * into the pipe buffer or we _lose_ what did not fit if
993 * the response ends sooner that BodyPipe frees up space:
994 * There is no code to keep pumping data into the pipe once
995 * response ends and serverComplete() is called.
997 * If the pipe is totally full, don't register the read handler.
998 * The BodyPipe will call our noteMoreBodySpaceAvailable() method
999 * when it has free space again.
1001 size_t adaptation_space
=
1002 virginBodyDestination
->buf().potentialSpaceSize();
1004 debugs(11,9, "ServerStateData may read up to min(" <<
1005 adaptation_space
<< ", " << space
<< ") bytes");
1007 if (adaptation_space
< space
)
1008 space
= adaptation_space
;