]> git.ipfire.org Git - thirdparty/squid.git/blob - src/Server.cc
Merge form trunk
[thirdparty/squid.git] / src / Server.cc
1 /*
2 * $Id$
3 *
4 * DEBUG:
5 * AUTHOR: Duane Wessels
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34
35 #include "squid.h"
36 #include "base/TextException.h"
37 #include "comm/Connection.h"
38 #include "comm/forward.h"
39 #include "Server.h"
40 #include "Store.h"
41 //#include "fde.h" /* for fd_table[fd].closing */
42 #include "HttpRequest.h"
43 #include "HttpReply.h"
44 #include "errorpage.h"
45 #include "err_detail_type.h"
46 #include "SquidTime.h"
47
48 #if USE_ADAPTATION
49 #include "adaptation/AccessCheck.h"
50 #include "adaptation/Iterator.h"
51 #endif
52
53 // implemented in client_side_reply.cc until sides have a common parent
54 extern void purgeEntriesByUrl(HttpRequest * req, const char *url);
55
56
57 ServerStateData::ServerStateData(FwdState *theFwdState): AsyncJob("ServerStateData"),
58 requestSender(NULL),
59 #if USE_ADAPTATION
60 adaptedHeadSource(NULL),
61 adaptationAccessCheckPending(false),
62 startedAdaptation(false),
63 #endif
64 receivedWholeRequestBody(false)
65 {
66 fwd = theFwdState;
67 entry = fwd->entry;
68
69 entry->lock();
70
71 request = HTTPMSGLOCK(fwd->request);
72 }
73
74 ServerStateData::~ServerStateData()
75 {
76 // paranoid: check that swanSong has been called
77 assert(!requestBodySource);
78 #if USE_ADAPTATION
79 assert(!virginBodyDestination);
80 assert(!adaptedBodySource);
81 #endif
82
83 entry->unlock();
84
85 HTTPMSGUNLOCK(request);
86 HTTPMSGUNLOCK(theVirginReply);
87 HTTPMSGUNLOCK(theFinalReply);
88
89 fwd = NULL; // refcounted
90
91 if (responseBodyBuffer != NULL) {
92 delete responseBodyBuffer;
93 responseBodyBuffer = NULL;
94 }
95 }
96
97 void
98 ServerStateData::swanSong()
99 {
100 // get rid of our piping obligations
101 if (requestBodySource != NULL)
102 stopConsumingFrom(requestBodySource);
103
104 #if USE_ADAPTATION
105 cleanAdaptation();
106 #endif
107
108 BodyConsumer::swanSong();
109 #if USE_ADAPTATION
110 Initiator::swanSong();
111 BodyProducer::swanSong();
112 #endif
113
114 // paranoid: check that swanSong has been called
115 // extra paranoid: yeah, I really mean it. they MUST pass here.
116 assert(!requestBodySource);
117 #if USE_ADAPTATION
118 assert(!virginBodyDestination);
119 assert(!adaptedBodySource);
120 #endif
121 }
122
123
124 HttpReply *
125 ServerStateData::virginReply()
126 {
127 assert(theVirginReply);
128 return theVirginReply;
129 }
130
131 const HttpReply *
132 ServerStateData::virginReply() const
133 {
134 assert(theVirginReply);
135 return theVirginReply;
136 }
137
138 HttpReply *
139 ServerStateData::setVirginReply(HttpReply *rep)
140 {
141 debugs(11,5, HERE << this << " setting virgin reply to " << rep);
142 assert(!theVirginReply);
143 assert(rep);
144 theVirginReply = HTTPMSGLOCK(rep);
145 return theVirginReply;
146 }
147
148 HttpReply *
149 ServerStateData::finalReply()
150 {
151 assert(theFinalReply);
152 return theFinalReply;
153 }
154
155 HttpReply *
156 ServerStateData::setFinalReply(HttpReply *rep)
157 {
158 debugs(11,5, HERE << this << " setting final reply to " << rep);
159
160 assert(!theFinalReply);
161 assert(rep);
162 theFinalReply = HTTPMSGLOCK(rep);
163
164 entry->replaceHttpReply(theFinalReply);
165 haveParsedReplyHeaders();
166
167 return theFinalReply;
168 }
169
170 // called when no more server communication is expected; may quit
171 void
172 ServerStateData::serverComplete()
173 {
174 debugs(11,5,HERE << "serverComplete " << this);
175
176 if (!doneWithServer()) {
177 closeServer();
178 assert(doneWithServer());
179 }
180
181 completed = true;
182
183 HttpRequest *r = originalRequest();
184 r->hier.total_response_time = r->hier.first_conn_start.tv_sec ?
185 tvSubMsec(r->hier.first_conn_start, current_time) : -1;
186
187 if (requestBodySource != NULL)
188 stopConsumingFrom(requestBodySource);
189
190 if (responseBodyBuffer != NULL)
191 return;
192
193 serverComplete2();
194 }
195
196 void
197 ServerStateData::serverComplete2()
198 {
199 debugs(11,5,HERE << "serverComplete2 " << this);
200
201 #if USE_ADAPTATION
202 if (virginBodyDestination != NULL)
203 stopProducingFor(virginBodyDestination, true);
204
205 if (!doneWithAdaptation())
206 return;
207 #endif
208
209 completeForwarding();
210 quitIfAllDone();
211 }
212
213 // When we are done talking to the primary server, we may be still talking
214 // to the ICAP service. And vice versa. Here, we quit only if we are done
215 // talking to both.
216 void ServerStateData::quitIfAllDone()
217 {
218 #if USE_ADAPTATION
219 if (!doneWithAdaptation()) {
220 debugs(11,5, HERE << "transaction not done: still talking to ICAP");
221 return;
222 }
223 #endif
224
225 if (!doneWithServer()) {
226 debugs(11,5, HERE << "transaction not done: still talking to server");
227 return;
228 }
229
230 debugs(11,3, HERE << "transaction done");
231
232 deleteThis("ServerStateData::quitIfAllDone");
233 }
234
235 // FTP side overloads this to work around multiple calls to fwd->complete
236 void
237 ServerStateData::completeForwarding()
238 {
239 debugs(11,5, HERE << "completing forwarding for " << fwd);
240 assert(fwd != NULL);
241 fwd->complete();
242 }
243
244 // Register to receive request body
245 bool ServerStateData::startRequestBodyFlow()
246 {
247 HttpRequest *r = originalRequest();
248 assert(r->body_pipe != NULL);
249 requestBodySource = r->body_pipe;
250 if (requestBodySource->setConsumerIfNotLate(this)) {
251 debugs(11,3, HERE << "expecting request body from " <<
252 requestBodySource->status());
253 return true;
254 }
255
256 debugs(11,3, HERE << "aborting on partially consumed request body: " <<
257 requestBodySource->status());
258 requestBodySource = NULL;
259 return false;
260 }
261
262 // Entry-dependent callbacks use this check to quit if the entry went bad
263 bool
264 ServerStateData::abortOnBadEntry(const char *abortReason)
265 {
266 if (entry->isAccepting())
267 return false;
268
269 debugs(11,5, HERE << "entry is not Accepting!");
270 abortTransaction(abortReason);
271 return true;
272 }
273
274 // more request or adapted response body is available
275 void
276 ServerStateData::noteMoreBodyDataAvailable(BodyPipe::Pointer bp)
277 {
278 #if USE_ADAPTATION
279 if (adaptedBodySource == bp) {
280 handleMoreAdaptedBodyAvailable();
281 return;
282 }
283 #endif
284 handleMoreRequestBodyAvailable();
285 }
286
287 // the entire request or adapted response body was provided, successfully
288 void
289 ServerStateData::noteBodyProductionEnded(BodyPipe::Pointer bp)
290 {
291 #if USE_ADAPTATION
292 if (adaptedBodySource == bp) {
293 handleAdaptedBodyProductionEnded();
294 return;
295 }
296 #endif
297 handleRequestBodyProductionEnded();
298 }
299
300 // premature end of the request or adapted response body production
301 void
302 ServerStateData::noteBodyProducerAborted(BodyPipe::Pointer bp)
303 {
304 #if USE_ADAPTATION
305 if (adaptedBodySource == bp) {
306 handleAdaptedBodyProducerAborted();
307 return;
308 }
309 #endif
310 handleRequestBodyProducerAborted();
311 }
312
313
314 // more origin request body data is available
315 void
316 ServerStateData::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
325 void
326 ServerStateData::handleRequestBodyProductionEnded()
327 {
328 receivedWholeRequestBody = true;
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
336 void
337 ServerStateData::doneSendingRequestBody()
338 {
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
347 void
348 ServerStateData::handleRequestBodyProducerAborted()
349 {
350 if (requestSender != NULL)
351 debugs(9,3, HERE << "fyi: request body aborted while we were sending");
352
353 fwd->dontRetry(true); // the problem is not with the server
354 stopConsumingFrom(requestBodySource); // requestSender, if any, will notice
355
356 // kids extend this
357 }
358
359 // called when we wrote request headers(!) or a part of the body
360 void
361 ServerStateData::sentRequestBody(const CommIoCbParams &io)
362 {
363 debugs(11, 5, "sentRequestBody: FD " << io.fd << ": size " << io.size << ": errflag " << io.flag << ".");
364 debugs(32,3,HERE << "sentRequestBody called");
365
366 requestSender = NULL;
367
368 if (io.size > 0) {
369 fd_bytes(io.fd, io.size, FD_WRITE);
370 kb_incr(&statCounter.server.all.kbytes_out, io.size);
371 // kids should increment their counters
372 }
373
374 if (io.flag == COMM_ERR_CLOSING)
375 return;
376
377 if (!requestBodySource) {
378 debugs(9,3, HERE << "detected while-we-were-sending abort");
379 return; // do nothing;
380 }
381
382 if (io.flag) {
383 debugs(11, 1, "sentRequestBody error: FD " << io.fd << ": " << xstrerr(errno));
384 ErrorState *err;
385 err = errorCon(ERR_WRITE_ERROR, HTTP_BAD_GATEWAY, fwd->request);
386 err->xerrno = errno;
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
397 if (!requestBodySource->exhausted())
398 sendMoreRequestBody();
399 else if (receivedWholeRequestBody)
400 doneSendingRequestBody();
401 else
402 debugs(9,3, HERE << "waiting for body production end or abort");
403 }
404
405 void
406 ServerStateData::sendMoreRequestBody()
407 {
408 assert(requestBodySource != NULL);
409 assert(!requestSender);
410
411 const Comm::ConnectionPointer conn = dataDescriptor();
412
413 if (!Comm::IsConnOpen(conn)) {
414 debugs(9,3, HERE << "cannot send request body to closing " << conn);
415 return; // wait for the kid's close handler; TODO: assert(closer);
416 }
417
418 MemBuf buf;
419 if (getMoreRequestBody(buf) && buf.contentSize() > 0) {
420 debugs(9,3, HERE << "will write " << buf.contentSize() << " request body bytes");
421 typedef CommCbMemFunT<ServerStateData, CommIoCbParams> Dialer;
422 requestSender = JobCallback(93,3, Dialer, this, ServerStateData::sentRequestBody);
423 comm_write_mbuf(conn, &buf, requestSender);
424 } else {
425 debugs(9,3, HERE << "will wait for more request body bytes or eof");
426 requestSender = NULL;
427 }
428 }
429
430 /// either fill buf with available [encoded] request body bytes or return false
431 bool
432 ServerStateData::getMoreRequestBody(MemBuf &buf)
433 {
434 // default implementation does not encode request body content
435 Must(requestBodySource != NULL);
436 return requestBodySource->getMoreData(buf);
437 }
438
439 // Compares hosts in urls, returns false if different, no sheme, or no host.
440 static bool
441 sameUrlHosts(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
469 static void
470 purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, HttpMsg *rep, http_hdr_type hdr)
471 {
472 const char *hdrUrl, *absUrl;
473
474 absUrl = NULL;
475 hdrUrl = rep->header.getStr(hdr);
476 if (hdrUrl == NULL) {
477 return;
478 }
479
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);
487 if (absUrl != NULL) {
488 hdrUrl = absUrl;
489 }
490 } else if (!sameUrlHosts(reqUrl, hdrUrl)) {
491 return;
492 }
493
494 purgeEntriesByUrl(req, hdrUrl);
495
496 if (absUrl != NULL) {
497 safe_free(absUrl);
498 }
499 }
500
501 // some HTTP methods should purge matching cache entries
502 void
503 ServerStateData::maybePurgeOthers()
504 {
505 // only some HTTP methods should purge matching cache entries
506 if (!request->method.purgesOthers())
507 return;
508
509 // and probably only if the response was successful
510 if (theFinalReply->sline.status >= 400)
511 return;
512
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);
519 }
520
521 // called (usually by kids) when we have final (possibly adapted) reply headers
522 void
523 ServerStateData::haveParsedReplyHeaders()
524 {
525 Must(theFinalReply);
526 maybePurgeOthers();
527 }
528
529 HttpRequest *
530 ServerStateData::originalRequest()
531 {
532 return request;
533 }
534
535 #if USE_ADAPTATION
536 /// Initiate an asynchronous adaptation transaction which will call us back.
537 void
538 ServerStateData::startAdaptation(const Adaptation::ServiceGroupPointer &group, HttpRequest *cause)
539 {
540 debugs(11, 5, "ServerStateData::startAdaptation() called");
541 // check whether we should be sending a body as well
542 // start body pipe to feed ICAP transaction if needed
543 assert(!virginBodyDestination);
544 HttpReply *vrep = virginReply();
545 assert(!vrep->body_pipe);
546 int64_t size = 0;
547 if (vrep->expectingBody(cause->method, size) && size) {
548 virginBodyDestination = new BodyPipe(this);
549 vrep->body_pipe = virginBodyDestination;
550 debugs(93, 6, HERE << "will send virgin reply body to " <<
551 virginBodyDestination << "; size: " << size);
552 if (size > 0)
553 virginBodyDestination->setBodySize(size);
554 }
555
556 adaptedHeadSource = initiateAdaptation(
557 new Adaptation::Iterator(vrep, cause, group));
558 startedAdaptation = initiated(adaptedHeadSource);
559 Must(startedAdaptation);
560 }
561
562 // properly cleans up ICAP-related state
563 // may be called multiple times
564 void ServerStateData::cleanAdaptation()
565 {
566 debugs(11,5, HERE << "cleaning ICAP; ACL: " << adaptationAccessCheckPending);
567
568 if (virginBodyDestination != NULL)
569 stopProducingFor(virginBodyDestination, false);
570
571 announceInitiatorAbort(adaptedHeadSource);
572
573 if (adaptedBodySource != NULL)
574 stopConsumingFrom(adaptedBodySource);
575
576 if (!adaptationAccessCheckPending) // we cannot cancel a pending callback
577 assert(doneWithAdaptation()); // make sure the two methods are in sync
578 }
579
580 bool
581 ServerStateData::doneWithAdaptation() const
582 {
583 return !adaptationAccessCheckPending &&
584 !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource;
585 }
586
587 // sends virgin reply body to ICAP, buffering excesses if needed
588 void
589 ServerStateData::adaptVirginReplyBody(const char *data, ssize_t len)
590 {
591 assert(startedAdaptation);
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);
616 }
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
629 // can supply more virgin response body data
630 void
631 ServerStateData::noteMoreBodySpaceAvailable(BodyPipe::Pointer)
632 {
633 if (responseBodyBuffer) {
634 addVirginReplyBody(NULL, 0); // kick the buffered fragment alive again
635 if (completed && !responseBodyBuffer) {
636 serverComplete2();
637 return;
638 }
639 }
640 maybeReadVirginBody();
641 }
642
643 // the consumer of our virgin response body aborted
644 void
645 ServerStateData::noteBodyConsumerAborted(BodyPipe::Pointer)
646 {
647 stopProducingFor(virginBodyDestination, false);
648
649 // do not force closeServer here in case we need to bypass AdaptationQueryAbort
650
651 if (doneWithAdaptation()) // we may still be receiving adapted response
652 handleAdaptationCompleted();
653 }
654
655 // received adapted response headers (body may follow)
656 void
657 ServerStateData::noteAdaptationAnswer(HttpMsg *msg)
658 {
659 clearAdaptation(adaptedHeadSource); // we do not expect more messages
660
661 if (abortOnBadEntry("entry went bad while waiting for adapted headers"))
662 return;
663
664 HttpReply *rep = dynamic_cast<HttpReply*>(msg);
665 assert(rep);
666 debugs(11,5, HERE << this << " setting adapted reply to " << rep);
667 setFinalReply(rep);
668
669 assert(!adaptedBodySource);
670 if (rep->body_pipe != NULL) {
671 // subscribe to receive adapted body
672 adaptedBodySource = rep->body_pipe;
673 // assume that ICAP does not auto-consume on failures
674 assert(adaptedBodySource->setConsumerIfNotLate(this));
675 } else {
676 // no body
677 if (doneWithAdaptation()) // we may still be sending virgin response
678 handleAdaptationCompleted();
679 }
680 }
681
682 // will not receive adapted response headers (and, hence, body)
683 void
684 ServerStateData::noteAdaptationQueryAbort(bool final)
685 {
686 clearAdaptation(adaptedHeadSource);
687 handleAdaptationAborted(!final);
688 }
689
690 // more adapted response body is available
691 void
692 ServerStateData::handleMoreAdaptedBodyAvailable()
693 {
694 const size_t contentSize = adaptedBodySource->buf().contentSize();
695
696 debugs(11,5, HERE << "consuming " << contentSize << " bytes of adapted " <<
697 "response body at offset " << adaptedBodySource->consumedSize());
698
699 if (abortOnBadEntry("entry refuses adapted body"))
700 return;
701
702 assert(entry);
703 BodyPipeCheckout bpc(*adaptedBodySource);
704 const StoreIOBuffer ioBuf(&bpc.buf, currentOffset);
705 currentOffset += bpc.buf.size;
706 entry->write(ioBuf);
707 bpc.buf.consume(contentSize);
708 bpc.checkIn();
709 }
710
711 // the entire adapted response body was produced, successfully
712 void
713 ServerStateData::handleAdaptedBodyProductionEnded()
714 {
715 stopConsumingFrom(adaptedBodySource);
716
717 if (abortOnBadEntry("entry went bad while waiting for adapted body eof"))
718 return;
719
720 handleAdaptationCompleted();
721 }
722
723 // premature end of the adapted response body
724 void ServerStateData::handleAdaptedBodyProducerAborted()
725 {
726 stopConsumingFrom(adaptedBodySource);
727 handleAdaptationAborted();
728 }
729
730 // common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
731 void
732 ServerStateData::handleAdaptationCompleted()
733 {
734 debugs(11,5, HERE << "handleAdaptationCompleted");
735 cleanAdaptation();
736
737 // We stop reading origin response because we have no place to put it and
738 // cannot use it. If some origin servers do not like that or if we want to
739 // reuse more pconns, we can add code to discard unneeded origin responses.
740 if (!doneWithServer()) {
741 debugs(11,3, HERE << "closing origin conn due to ICAP completion");
742 closeServer();
743 }
744
745 completeForwarding();
746 quitIfAllDone();
747 }
748
749
750 // common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods
751 void
752 ServerStateData::handleAdaptationAborted(bool bypassable)
753 {
754 debugs(11,5, HERE << "handleAdaptationAborted; bypassable: " << bypassable <<
755 ", entry empty: " << entry->isEmpty());
756
757 if (abortOnBadEntry("entry went bad while ICAP aborted"))
758 return;
759
760 // TODO: bypass if possible
761
762 if (entry->isEmpty()) {
763 debugs(11,9, HERE << "creating ICAP error entry after ICAP failure");
764 ErrorState *err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
765 err->xerrno = ERR_DETAIL_ICAP_RESPMOD_EARLY;
766 fwd->fail(err);
767 fwd->dontRetry(true);
768 } else if (request) { // update logged info directly
769 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_ICAP_RESPMOD_LATE);
770 }
771
772 abortTransaction("ICAP failure");
773 }
774
775 void
776 ServerStateData::adaptationAclCheckDone(Adaptation::ServiceGroupPointer group)
777 {
778 adaptationAccessCheckPending = false;
779
780 if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
781 return;
782
783 // TODO: Should nonICAP and postICAP path check this on the server-side?
784 // That check now only happens on client-side, in processReplyAccess().
785 if (virginReply()->expectedBodyTooLarge(*request)) {
786 sendBodyIsTooLargeError();
787 return;
788 }
789 // TODO: Should we check receivedBodyTooLarge on the server-side as well?
790
791 if (!group) {
792 debugs(11,3, HERE << "no adapation needed");
793 setFinalReply(virginReply());
794 processReplyBody();
795 return;
796 }
797
798 startAdaptation(group, originalRequest());
799 processReplyBody();
800 }
801
802 void
803 ServerStateData::adaptationAclCheckDoneWrapper(Adaptation::ServiceGroupPointer group, void *data)
804 {
805 ServerStateData *state = (ServerStateData *)data;
806 state->adaptationAclCheckDone(group);
807 }
808 #endif
809
810 void
811 ServerStateData::sendBodyIsTooLargeError()
812 {
813 ErrorState *err = errorCon(ERR_TOO_BIG, HTTP_FORBIDDEN, request);
814 err->xerrno = errno;
815 fwd->fail(err);
816 fwd->dontRetry(true);
817 abortTransaction("Virgin body too large.");
818 }
819
820 // TODO: when HttpStateData sends all errors to ICAP,
821 // we should be able to move this at the end of setVirginReply().
822 void
823 ServerStateData::adaptOrFinalizeReply()
824 {
825 #if USE_ADAPTATION
826 // TODO: merge with client side and return void to hide the on/off logic?
827 // The callback can be called with a NULL service if adaptation is off.
828 adaptationAccessCheckPending = Adaptation::AccessCheck::Start(
829 Adaptation::methodRespmod, Adaptation::pointPreCache,
830 request, virginReply(), adaptationAclCheckDoneWrapper, this);
831 debugs(11,5, HERE << "adaptationAccessCheckPending=" << adaptationAccessCheckPending);
832 if (adaptationAccessCheckPending)
833 return;
834 #endif
835
836 setFinalReply(virginReply());
837 }
838
839 /// initializes bodyBytesRead stats if needed and applies delta
840 void
841 ServerStateData::adjustBodyBytesRead(const int64_t delta)
842 {
843 int64_t &bodyBytesRead = originalRequest()->hier.bodyBytesRead;
844
845 // if we got here, do not log a dash even if we got nothing from the server
846 if (bodyBytesRead < 0)
847 bodyBytesRead = 0;
848
849 bodyBytesRead += delta; // supports negative and zero deltas
850
851 // check for overflows ("infinite" response?) and undeflows (a bug)
852 Must(bodyBytesRead >= 0);
853 }
854
855 void
856 ServerStateData::addVirginReplyBody(const char *data, ssize_t len)
857 {
858 adjustBodyBytesRead(len);
859
860 #if USE_ADAPTATION
861 assert(!adaptationAccessCheckPending); // or would need to buffer while waiting
862 if (startedAdaptation) {
863 adaptVirginReplyBody(data, len);
864 return;
865 }
866 #endif
867 storeReplyBody(data, len);
868 }
869
870 // writes virgin or adapted reply body to store
871 void
872 ServerStateData::storeReplyBody(const char *data, ssize_t len)
873 {
874 // write even if len is zero to push headers towards the client side
875 entry->write (StoreIOBuffer(len, currentOffset, (char*)data));
876
877 currentOffset += len;
878 }
879
880 size_t ServerStateData::replyBodySpace(const MemBuf &readBuf,
881 const size_t minSpace) const
882 {
883 size_t space = readBuf.spaceSize(); // available space w/o heroic measures
884 if (space < minSpace) {
885 const size_t maxSpace = readBuf.potentialSpaceSize(); // absolute best
886 space = min(minSpace, maxSpace); // do not promise more than asked
887 }
888
889 #if USE_ADAPTATION
890 if (responseBodyBuffer) {
891 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
892 }
893
894 if (virginBodyDestination != NULL) {
895 /*
896 * BodyPipe buffer has a finite size limit. We
897 * should not read more data from the network than will fit
898 * into the pipe buffer or we _lose_ what did not fit if
899 * the response ends sooner that BodyPipe frees up space:
900 * There is no code to keep pumping data into the pipe once
901 * response ends and serverComplete() is called.
902 *
903 * If the pipe is totally full, don't register the read handler.
904 * The BodyPipe will call our noteMoreBodySpaceAvailable() method
905 * when it has free space again.
906 */
907 size_t adaptation_space =
908 virginBodyDestination->buf().potentialSpaceSize();
909
910 debugs(11,9, "ServerStateData may read up to min(" <<
911 adaptation_space << ", " << space << ") bytes");
912
913 if (adaptation_space < space)
914 space = adaptation_space;
915 }
916 #endif
917
918 return space;
919 }