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