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