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