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