]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Server.cc
Bug 2384: Assertion failed on ESI page.
[thirdparty/squid.git] / src / Server.cc
CommitLineData
cd304fc2 1/*
7c70e7e5 2 * $Id: Server.cc,v 1.26 2008/02/18 22:51:21 rousskov Exp $
cd304fc2 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 "Server.h"
37#include "Store.h"
38#include "HttpRequest.h"
39#include "HttpReply.h"
5f8252d2 40#include "errorpage.h"
41
a83c6ed6 42#if USE_ADAPTATION
62c7f90e 43#include "adaptation/AccessCheck.h"
abd4b611 44#include "adaptation/Service.h"
0f283edf 45#endif
cd304fc2 46
dc56a9b1 47ServerStateData::ServerStateData(FwdState *theFwdState): AsyncJob("ServerStateData"),requestSender(NULL)
a83c6ed6 48#if USE_ADAPTATION
7c70e7e5 49 , adaptedHeadSource(NULL)
a83c6ed6
AR
50 , adaptationAccessCheckPending(false)
51 , startedAdaptation(false)
77089558 52#endif
cd304fc2 53{
54 fwd = theFwdState;
55 entry = fwd->entry;
34266cde 56
5f8252d2 57 entry->lock();
34266cde 58
6dd9f4bd 59 request = HTTPMSGLOCK(fwd->request);
cd304fc2 60}
61
62ServerStateData::~ServerStateData()
63{
97b5e68f 64 entry->unlock();
cd304fc2 65
6dd9f4bd 66 HTTPMSGUNLOCK(request);
585ab260 67 HTTPMSGUNLOCK(theVirginReply);
68 HTTPMSGUNLOCK(theFinalReply);
cd304fc2 69
70 fwd = NULL; // refcounted
71
5f8252d2 72 if (requestBodySource != NULL)
73 requestBodySource->clearConsumer();
74
a83c6ed6
AR
75#if USE_ADAPTATION
76 cleanAdaptation();
5f8252d2 77#endif
7dc79973 78
79 if (responseBodyBuffer != NULL) {
80 delete responseBodyBuffer;
81 responseBodyBuffer = NULL;
82 }
5f8252d2 83}
84
585ab260 85HttpReply *
86ServerStateData::virginReply() {
87 assert(theVirginReply);
88 return theVirginReply;
89}
90
91const HttpReply *
92ServerStateData::virginReply() const {
93 assert(theVirginReply);
94 return theVirginReply;
95}
96
97HttpReply *
98ServerStateData::setVirginReply(HttpReply *rep) {
99 debugs(11,5, HERE << this << " setting virgin reply to " << rep);
100 assert(!theVirginReply);
101 assert(rep);
102 theVirginReply = HTTPMSGLOCK(rep);
103 return theVirginReply;
104}
105
106HttpReply *
107ServerStateData::finalReply() {
108 assert(theFinalReply);
109 return theFinalReply;
110}
111
112HttpReply *
113ServerStateData::setFinalReply(HttpReply *rep) {
114 debugs(11,5, HERE << this << " setting final reply to " << rep);
115
116 assert(!theFinalReply);
117 assert(rep);
118 theFinalReply = HTTPMSGLOCK(rep);
119
120 entry->replaceHttpReply(theFinalReply);
121 haveParsedReplyHeaders();
122
123 return theFinalReply;
124}
125
5f8252d2 126// called when no more server communication is expected; may quit
127void
128ServerStateData::serverComplete()
129{
130 debugs(11,5,HERE << "serverComplete " << this);
131
132 if (!doneWithServer()) {
133 closeServer();
134 assert(doneWithServer());
135 }
136
7dc79973 137 completed = true;
138
5f8252d2 139 if (requestBodySource != NULL)
140 stopConsumingFrom(requestBodySource);
141
7dc79973 142 if (responseBodyBuffer != NULL)
143 return;
144
145 serverComplete2();
146}
147
148void
149ServerStateData::serverComplete2()
150{
151 debugs(11,5,HERE << "serverComplete2 " << this);
152
a83c6ed6 153#if USE_ADAPTATION
5f8252d2 154 if (virginBodyDestination != NULL)
155 stopProducingFor(virginBodyDestination, true);
156
a83c6ed6 157 if (!doneWithAdaptation())
5f8252d2 158 return;
159#endif
160
161 completeForwarding();
162 quitIfAllDone();
163}
164
165// When we are done talking to the primary server, we may be still talking
166// to the ICAP service. And vice versa. Here, we quit only if we are done
167// talking to both.
168void ServerStateData::quitIfAllDone() {
a83c6ed6
AR
169#if USE_ADAPTATION
170 if (!doneWithAdaptation()) {
5f8252d2 171 debugs(11,5, HERE << "transaction not done: still talking to ICAP");
172 return;
173 }
174#endif
175
176 if (!doneWithServer()) {
177 debugs(11,5, HERE << "transaction not done: still talking to server");
178 return;
179 }
180
181 debugs(11,3, HERE << "transaction done");
dc56a9b1 182
183 deleteThis("ServerStateData::quitIfAllDone");
5f8252d2 184}
185
186// FTP side overloads this to work around multiple calls to fwd->complete
187void
188ServerStateData::completeForwarding() {
189 debugs(11,5, HERE << "completing forwarding for " << fwd);
190 assert(fwd != NULL);
191 fwd->complete();
192}
193
123ec4de 194// Register to receive request body
195bool ServerStateData::startRequestBodyFlow()
196{
197 HttpRequest *r = originalRequest();
198 assert(r->body_pipe != NULL);
199 requestBodySource = r->body_pipe;
200 if (requestBodySource->setConsumerIfNotLate(this)) {
201 debugs(11,3, HERE << "expecting request body from " <<
202 requestBodySource->status());
203 return true;
204 }
205
206 debugs(11,3, HERE << "aborting on partially consumed request body: " <<
207 requestBodySource->status());
208 requestBodySource = NULL;
209 return false;
210}
211
5f8252d2 212// Entry-dependent callbacks use this check to quit if the entry went bad
213bool
214ServerStateData::abortOnBadEntry(const char *abortReason)
215{
216 if (entry->isAccepting())
217 return false;
218
219 debugs(11,5, HERE << "entry is not Accepting!");
220 abortTransaction(abortReason);
221 return true;
222}
223
224// more request or adapted response body is available
225void
dc56a9b1 226ServerStateData::noteMoreBodyDataAvailable(BodyPipe::Pointer bp)
5f8252d2 227{
a83c6ed6 228#if USE_ADAPTATION
dc56a9b1 229 if (adaptedBodySource == bp) {
5f8252d2 230 handleMoreAdaptedBodyAvailable();
231 return;
232 }
233#endif
234 handleMoreRequestBodyAvailable();
235}
236
237// the entire request or adapted response body was provided, successfully
238void
dc56a9b1 239ServerStateData::noteBodyProductionEnded(BodyPipe::Pointer bp)
5f8252d2 240{
a83c6ed6 241#if USE_ADAPTATION
dc56a9b1 242 if (adaptedBodySource == bp) {
5f8252d2 243 handleAdaptedBodyProductionEnded();
244 return;
c99de607 245 }
cd304fc2 246#endif
5f8252d2 247 handleRequestBodyProductionEnded();
248}
249
250// premature end of the request or adapted response body production
251void
dc56a9b1 252ServerStateData::noteBodyProducerAborted(BodyPipe::Pointer bp)
5f8252d2 253{
a83c6ed6 254#if USE_ADAPTATION
dc56a9b1 255 if (adaptedBodySource == bp) {
5f8252d2 256 handleAdaptedBodyProducerAborted();
257 return;
258 }
259#endif
260 handleRequestBodyProducerAborted();
261}
262
263
264// more origin request body data is available
265void
266ServerStateData::handleMoreRequestBodyAvailable()
267{
268 if (!requestSender)
269 sendMoreRequestBody();
270 else
271 debugs(9,3, HERE << "waiting for request body write to complete");
272}
273
274// there will be no more handleMoreRequestBodyAvailable calls
275void
276ServerStateData::handleRequestBodyProductionEnded()
277{
278 if (!requestSender)
279 doneSendingRequestBody();
280 else
281 debugs(9,3, HERE << "waiting for request body write to complete");
282}
283
284// called when we are done sending request body; kids extend this
285void
286ServerStateData::doneSendingRequestBody() {
287 debugs(9,3, HERE << "done sending request body");
288 assert(requestBodySource != NULL);
289 stopConsumingFrom(requestBodySource);
290
291 // kids extend this
292}
293
294// called when body producers aborts; kids extend this
295void
296ServerStateData::handleRequestBodyProducerAborted()
297{
298 if (requestSender != NULL)
299 debugs(9,3, HERE << "fyi: request body aborted while we were sending");
300
0919c51e 301 fwd->dontRetry(true); // the problem is not with the server
5f8252d2 302 stopConsumingFrom(requestBodySource); // requestSender, if any, will notice
303
304 // kids extend this
305}
306
5f8252d2 307// called when we wrote request headers(!) or a part of the body
308void
dc56a9b1 309ServerStateData::sentRequestBody(const CommIoCbParams &io)
5f8252d2 310{
dc56a9b1 311 debugs(11, 5, "sentRequestBody: FD " << io.fd << ": size " << io.size << ": errflag " << io.flag << ".");
5f8252d2 312 debugs(32,3,HERE << "sentRequestBody called");
313
314 requestSender = NULL;
315
dc56a9b1 316 if (io.size > 0) {
317 fd_bytes(io.fd, io.size, FD_WRITE);
318 kb_incr(&statCounter.server.all.kbytes_out, io.size);
5f8252d2 319 // kids should increment their counters
320 }
321
dc56a9b1 322 if (io.flag == COMM_ERR_CLOSING)
5f8252d2 323 return;
324
325 if (!requestBodySource) {
326 debugs(9,3, HERE << "detected while-we-were-sending abort");
327 return; // do nothing;
328 }
329
dc56a9b1 330 if (io.flag) {
331 debugs(11, 1, "sentRequestBody error: FD " << io.fd << ": " << xstrerr(errno));
5f8252d2 332 ErrorState *err;
333 err = errorCon(ERR_WRITE_ERROR, HTTP_BAD_GATEWAY, fwd->request);
334 err->xerrno = errno;
335 fwd->fail(err);
336 abortTransaction("I/O error while sending request body");
337 return;
338 }
339
340 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
341 abortTransaction("store entry aborted while sending request body");
342 return;
343 }
344
345 if (requestBodySource->exhausted())
346 doneSendingRequestBody();
347 else
348 sendMoreRequestBody();
cd304fc2 349}
350
5f8252d2 351void
352ServerStateData::sendMoreRequestBody()
353{
354 assert(requestBodySource != NULL);
355 assert(!requestSender);
356 MemBuf buf;
357 if (requestBodySource->getMoreData(buf)) {
358 debugs(9,3, HERE << "will write " << buf.contentSize() << " request body bytes");
dc56a9b1 359 typedef CommCbMemFunT<ServerStateData, CommIoCbParams> Dialer;
360 requestSender = asyncCall(93,3, "ServerStateData::sentRequestBody",
361 Dialer(this, &ServerStateData::sentRequestBody));
362 comm_write_mbuf(dataDescriptor(), &buf, requestSender);
5f8252d2 363 } else {
364 debugs(9,3, HERE << "will wait for more request body bytes or eof");
365 requestSender = NULL;
366 }
367}
368
a83c6ed6 369// called by noteAdaptationAnswer(), HTTP server overwrites this
5f8252d2 370void
371ServerStateData::haveParsedReplyHeaders()
372{
373 // default does nothing
374}
375
7dc79973 376HttpRequest *
377ServerStateData::originalRequest()
378{
379 return request;
380}
5f8252d2 381
a83c6ed6 382#if USE_ADAPTATION
cd304fc2 383/*
c99de607 384 * Initiate an ICAP transaction. Return true on success.
cd304fc2 385 * Caller will handle error condition by generating a Squid error message
386 * or take other action.
387 */
c99de607 388bool
a83c6ed6 389ServerStateData::startAdaptation(Adaptation::ServicePointer service, HttpRequest *cause)
cd304fc2 390{
a83c6ed6 391 debugs(11, 5, "ServerStateData::startAdaptation() called");
c99de607 392 if (!service) {
a83c6ed6 393 debugs(11, 3, "ServerStateData::startAdaptation fails: lack of service");
c99de607 394 return false;
395 }
396 if (service->broken()) {
a83c6ed6 397 debugs(11, 3, "ServerStateData::startAdaptation fails: broken service");
c99de607 398 return false;
399 }
5f8252d2 400
401 // check whether we should be sending a body as well
5f8252d2 402 // start body pipe to feed ICAP transaction if needed
585ab260 403 assert(!virginBodyDestination);
47f6e231 404 HttpReply *vrep = virginReply();
585ab260 405 assert(!vrep->body_pipe);
47f6e231 406 int64_t size = 0;
585ab260 407 if (vrep->expectingBody(cause->method, size) && size) {
5f8252d2 408 virginBodyDestination = new BodyPipe(this);
585ab260 409 vrep->body_pipe = virginBodyDestination;
5f8252d2 410 debugs(93, 6, HERE << "will send virgin reply body to " <<
411 virginBodyDestination << "; size: " << size);
c2eef5bd 412 if (size > 0)
413 virginBodyDestination->setBodySize(size);
5f8252d2 414 }
415
a83c6ed6
AR
416 adaptedHeadSource = initiateAdaptation(service->makeXactLauncher(
417 this, vrep, cause));
dc56a9b1 418 return adaptedHeadSource != NULL;
cd304fc2 419}
0c25e715 420
5f8252d2 421// properly cleans up ICAP-related state
422// may be called multiple times
a83c6ed6
AR
423void ServerStateData::cleanAdaptation() {
424 debugs(11,5, HERE << "cleaning ICAP; ACL: " << adaptationAccessCheckPending);
5f8252d2 425
426 if (virginBodyDestination != NULL)
427 stopProducingFor(virginBodyDestination, false);
428
0f283edf 429 announceInitiatorAbort(adaptedHeadSource);
5f8252d2 430
431 if (adaptedBodySource != NULL)
432 stopConsumingFrom(adaptedBodySource);
433
a83c6ed6
AR
434 if (!adaptationAccessCheckPending) // we cannot cancel a pending callback
435 assert(doneWithAdaptation()); // make sure the two methods are in sync
5f8252d2 436}
437
438bool
a83c6ed6
AR
439ServerStateData::doneWithAdaptation() const {
440 return !adaptationAccessCheckPending &&
ba82c452 441 !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource;
5f8252d2 442}
443
bc81cb2b 444// sends virgin reply body to ICAP, buffering excesses if needed
445void
446ServerStateData::adaptVirginReplyBody(const char *data, ssize_t len)
447{
a83c6ed6 448 assert(startedAdaptation);
bc81cb2b 449
450 if (!virginBodyDestination) {
451 debugs(11,3, HERE << "ICAP does not want more virgin body");
452 return;
453 }
454
455 // grow overflow area if already overflowed
456 if (responseBodyBuffer) {
457 responseBodyBuffer->append(data, len);
458 data = responseBodyBuffer->content();
459 len = responseBodyBuffer->contentSize();
460 }
461
462 const ssize_t putSize = virginBodyDestination->putMoreData(data, len);
463 data += putSize;
464 len -= putSize;
465
466 // if we had overflow area, shrink it as necessary
467 if (responseBodyBuffer) {
468 if (putSize == responseBodyBuffer->contentSize()) {
469 delete responseBodyBuffer;
470 responseBodyBuffer = NULL;
471 } else {
472 responseBodyBuffer->consume(putSize);
473 }
474 return;
475 }
476
477 // if we did not have an overflow area, create it as needed
478 if (len > 0) {
479 assert(!responseBodyBuffer);
480 responseBodyBuffer = new MemBuf;
481 responseBodyBuffer->init(4096, SQUID_TCP_SO_RCVBUF * 10);
482 responseBodyBuffer->append(data, len);
483 }
484}
485
5f8252d2 486// can supply more virgin response body data
487void
dc56a9b1 488ServerStateData::noteMoreBodySpaceAvailable(BodyPipe::Pointer)
5f8252d2 489{
7dc79973 490 if (responseBodyBuffer) {
bc81cb2b 491 addVirginReplyBody(NULL, 0); // kick the buffered fragment alive again
492 if (completed && !responseBodyBuffer) {
493 serverComplete2();
494 return;
495 }
7dc79973 496 }
5f8252d2 497 maybeReadVirginBody();
498}
499
bc81cb2b 500// the consumer of our virgin response body aborted
5f8252d2 501void
dc56a9b1 502ServerStateData::noteBodyConsumerAborted(BodyPipe::Pointer)
5f8252d2 503{
504 stopProducingFor(virginBodyDestination, false);
bc81cb2b 505
a83c6ed6 506 // do not force closeServer here in case we need to bypass AdaptationQueryAbort
bc81cb2b 507
a83c6ed6
AR
508 if (doneWithAdaptation()) // we may still be receiving adapted response
509 handleAdaptationCompleted();
5f8252d2 510}
511
512// received adapted response headers (body may follow)
513void
a83c6ed6 514ServerStateData::noteAdaptationAnswer(HttpMsg *msg)
5f8252d2 515{
a83c6ed6 516 clearAdaptation(adaptedHeadSource); // we do not expect more messages
5f8252d2 517
585ab260 518 if (abortOnBadEntry("entry went bad while waiting for adapted headers"))
5f8252d2 519 return;
5f8252d2 520
585ab260 521 HttpReply *rep = dynamic_cast<HttpReply*>(msg);
5f8252d2 522 assert(rep);
585ab260 523 debugs(11,5, HERE << this << " setting adapted reply to " << rep);
524 setFinalReply(rep);
5f8252d2 525
526 assert(!adaptedBodySource);
585ab260 527 if (rep->body_pipe != NULL) {
5f8252d2 528 // subscribe to receive adapted body
585ab260 529 adaptedBodySource = rep->body_pipe;
5f8252d2 530 // assume that ICAP does not auto-consume on failures
531 assert(adaptedBodySource->setConsumerIfNotLate(this));
532 } else {
533 // no body
a83c6ed6
AR
534 if (doneWithAdaptation()) // we may still be sending virgin response
535 handleAdaptationCompleted();
5f8252d2 536 }
5f8252d2 537}
538
539// will not receive adapted response headers (and, hence, body)
540void
a83c6ed6 541ServerStateData::noteAdaptationQueryAbort(bool final)
5f8252d2 542{
a83c6ed6
AR
543 clearAdaptation(adaptedHeadSource);
544 handleAdaptationAborted(!final);
5f8252d2 545}
546
547// more adapted response body is available
548void
549ServerStateData::handleMoreAdaptedBodyAvailable()
550{
551 const size_t contentSize = adaptedBodySource->buf().contentSize();
552
553 debugs(11,5, HERE << "consuming " << contentSize << " bytes of adapted " <<
554 "response body at offset " << adaptedBodySource->consumedSize());
555
556 if (abortOnBadEntry("entry refuses adapted body"))
557 return;
558
559 assert(entry);
560 BodyPipeCheckout bpc(*adaptedBodySource);
561 const StoreIOBuffer ioBuf(&bpc.buf, bpc.offset);
562 entry->write(ioBuf);
563 bpc.buf.consume(contentSize);
564 bpc.checkIn();
565}
566
567// the entire adapted response body was produced, successfully
568void
569ServerStateData::handleAdaptedBodyProductionEnded()
570{
571 stopConsumingFrom(adaptedBodySource);
572
573 if (abortOnBadEntry("entry went bad while waiting for adapted body eof"))
574 return;
575
a83c6ed6 576 handleAdaptationCompleted();
5f8252d2 577}
578
579// premature end of the adapted response body
580void ServerStateData::handleAdaptedBodyProducerAborted()
581{
582 stopConsumingFrom(adaptedBodySource);
a83c6ed6 583 handleAdaptationAborted();
5f8252d2 584}
585
a83c6ed6 586// common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
5f8252d2 587void
a83c6ed6 588ServerStateData::handleAdaptationCompleted()
5f8252d2 589{
a83c6ed6
AR
590 debugs(11,5, HERE << "handleAdaptationCompleted");
591 cleanAdaptation();
bc81cb2b 592
593 // We stop reading origin response because we have no place to put it and
594 // cannot use it. If some origin servers do not like that or if we want to
595 // reuse more pconns, we can add code to discard unneeded origin responses.
596 if (!doneWithServer()) {
597 debugs(11,3, HERE << "closing origin conn due to ICAP completion");
598 closeServer();
599 }
600
5f8252d2 601 completeForwarding();
602 quitIfAllDone();
603}
604
bc81cb2b 605
a83c6ed6 606// common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods
5f8252d2 607void
a83c6ed6 608ServerStateData::handleAdaptationAborted(bool bypassable)
5f8252d2 609{
a83c6ed6 610 debugs(11,5, HERE << "handleAdaptationAborted; bypassable: " << bypassable <<
0f283edf 611 ", entry empty: " << entry->isEmpty());
5f8252d2 612
613 if (abortOnBadEntry("entry went bad while ICAP aborted"))
614 return;
615
0f283edf 616 // TODO: bypass if possible
617
5f8252d2 618 if (entry->isEmpty()) {
619 debugs(11,9, HERE << "creating ICAP error entry after ICAP failure");
620 ErrorState *err =
621 errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
622 err->xerrno = errno;
623 fwd->fail(err);
624 fwd->dontRetry(true);
625 }
626
0f283edf 627 abortTransaction("ICAP failure");
5f8252d2 628}
629
7c4e4e7f 630void
a83c6ed6 631ServerStateData::adaptationAclCheckDone(Adaptation::ServicePointer service)
7c4e4e7f 632{
a83c6ed6 633 adaptationAccessCheckPending = false;
7c4e4e7f 634
635 if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
636 return;
637
47416555 638 // TODO: Should nonICAP and postICAP path check this on the server-side?
639 // That check now only happens on client-side, in processReplyAccess().
640 if (virginReply()->expectedBodyTooLarge(*request)) {
641 sendBodyIsTooLargeError();
642 return;
643 }
abd4b611 644 // TODO: Should we check receivedBodyTooLarge on the server-side as well?
47416555 645
a83c6ed6 646 startedAdaptation = startAdaptation(service, originalRequest());
7c4e4e7f 647
a83c6ed6 648 if (!startedAdaptation && (!service || service->cfg().bypass)) {
7c4e4e7f 649 // handle ICAP start failure when no service was selected
650 // or where the selected service was optional
585ab260 651 setFinalReply(virginReply());
7c4e4e7f 652 processReplyBody();
7c4e4e7f 653 return;
654 }
655
a83c6ed6 656 if (!startedAdaptation) {
7c4e4e7f 657 // handle start failure for an essential ICAP service
658 ErrorState *err = errorCon(ERR_ICAP_FAILURE,
659 HTTP_INTERNAL_SERVER_ERROR, originalRequest());
660 err->xerrno = errno;
661 errorAppendEntry(entry, err);
662 abortTransaction("ICAP start failure");
663 return;
664 }
665
666 processReplyBody();
667}
668
7dc79973 669void
a83c6ed6 670ServerStateData::adaptationAclCheckDoneWrapper(Adaptation::ServicePointer service, void *data)
7dc79973 671{
672 ServerStateData *state = (ServerStateData *)data;
a83c6ed6 673 state->adaptationAclCheckDone(service);
7dc79973 674}
0c25e715 675#endif
7dc79973 676
47416555 677void
678ServerStateData::sendBodyIsTooLargeError()
679{
680 ErrorState *err = errorCon(ERR_TOO_BIG, HTTP_FORBIDDEN, request);
681 err->xerrno = errno;
682 fwd->fail(err);
683 fwd->dontRetry(true);
684 abortTransaction("Virgin body too large.");
685}
686
585ab260 687// TODO: when HttpStateData sends all errors to ICAP,
688// we should be able to move this at the end of setVirginReply().
7dc79973 689void
585ab260 690ServerStateData::adaptOrFinalizeReply()
7dc79973 691{
a83c6ed6 692#if USE_ADAPTATION
abd4b611
AR
693 // TODO: merge with client side and return void to hide the on/off logic?
694 // The callback can be called with a NULL service if adaptation is off.
695 adaptationAccessCheckPending = Adaptation::AccessCheck::Start(
696 Adaptation::methodRespmod, Adaptation::pointPreCache,
697 request, virginReply(), adaptationAclCheckDoneWrapper, this);
698 if (adaptationAccessCheckPending)
7dc79973 699 return;
7dc79973 700#endif
701
585ab260 702 setFinalReply(virginReply());
7dc79973 703}
704
705void
bc81cb2b 706ServerStateData::addVirginReplyBody(const char *data, ssize_t len)
7dc79973 707{
a83c6ed6
AR
708#if USE_ADAPTATION
709 assert(!adaptationAccessCheckPending); // or would need to buffer while waiting
710 if (startedAdaptation) {
bc81cb2b 711 adaptVirginReplyBody(data, len);
7dc79973 712 return;
713 }
7dc79973 714#endif
bc81cb2b 715 storeReplyBody(data, len);
716}
7dc79973 717
bc81cb2b 718// writes virgin or adapted reply body to store
719void
720ServerStateData::storeReplyBody(const char *data, ssize_t len)
721{
2d1a172f 722 // write even if len is zero to push headers towards the client side
7dc79973 723 entry->write (StoreIOBuffer(len, currentOffset, (char*)data));
724
725 currentOffset += len;
726}
727
728size_t ServerStateData::replyBodySpace(size_t space)
729{
a83c6ed6 730#if USE_ADAPTATION
7dc79973 731 if (responseBodyBuffer) {
732 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
733 }
734
735 if (virginBodyDestination != NULL) {
736 /*
737 * BodyPipe buffer has a finite size limit. We
738 * should not read more data from the network than will fit
739 * into the pipe buffer or we _lose_ what did not fit if
740 * the response ends sooner that BodyPipe frees up space:
741 * There is no code to keep pumping data into the pipe once
742 * response ends and serverComplete() is called.
743 *
744 * If the pipe is totally full, don't register the read handler.
745 * The BodyPipe will call our noteMoreBodySpaceAvailable() method
746 * when it has free space again.
747 */
a83c6ed6
AR
748 size_t adaptation_space =
749 virginBodyDestination->buf().potentialSpaceSize();
7dc79973 750
a83c6ed6
AR
751 debugs(11,9, "ServerStateData may read up to min(" <<
752 adaptation_space << ", " << space << ") bytes");
7dc79973 753
a83c6ed6
AR
754 if (adaptation_space < space)
755 space = adaptation_space;
7dc79973 756 }
757#endif
758
759 return space;
760}