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