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