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