]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Server.cc
Minimize difference with parent (trunk).
[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"
ec41b64c 38#include "comm/Write.h"
cd304fc2 39#include "Server.h"
40#include "Store.h"
a0297974 41#include "fde.h" /* for fd_table[fd].closing */
cd304fc2 42#include "HttpRequest.h"
43#include "HttpReply.h"
5f8252d2 44#include "errorpage.h"
64b66b76 45#include "err_detail_type.h"
3ff65596 46#include "SquidTime.h"
5f8252d2 47
a83c6ed6 48#if USE_ADAPTATION
62c7f90e 49#include "adaptation/AccessCheck.h"
a22e6cd3 50#include "adaptation/Iterator.h"
0f283edf 51#endif
cd304fc2 52
c1520b67 53// implemented in client_side_reply.cc until sides have a common parent
90bd689c 54extern void purgeEntriesByUrl(HttpRequest * req, const char *url);
c1520b67
AJ
55
56
39cb8c41
AR
57ServerStateData::ServerStateData(FwdState *theFwdState): AsyncJob("ServerStateData"),
58 requestSender(NULL),
a83c6ed6 59#if USE_ADAPTATION
39cb8c41
AR
60 adaptedHeadSource(NULL),
61 adaptationAccessCheckPending(false),
62 startedAdaptation(false),
77089558 63#endif
39cb8c41 64 receivedWholeRequestBody(false)
cd304fc2 65{
66 fwd = theFwdState;
67 entry = fwd->entry;
34266cde 68
5f8252d2 69 entry->lock();
34266cde 70
6dd9f4bd 71 request = HTTPMSGLOCK(fwd->request);
cd304fc2 72}
73
74ServerStateData::~ServerStateData()
75{
49918309
AR
76 // paranoid: check that swanSong has been called
77 assert(!requestBodySource);
78#if USE_ADAPTATION
79 assert(!virginBodyDestination);
80 assert(!adaptedBodySource);
81#endif
82
97b5e68f 83 entry->unlock();
cd304fc2 84
6dd9f4bd 85 HTTPMSGUNLOCK(request);
585ab260 86 HTTPMSGUNLOCK(theVirginReply);
87 HTTPMSGUNLOCK(theFinalReply);
cd304fc2 88
89 fwd = NULL; // refcounted
90
49918309 91 if (responseBodyBuffer != NULL) {
9e008dda
AJ
92 delete responseBodyBuffer;
93 responseBodyBuffer = NULL;
49918309
AR
94 }
95}
96
97void
98ServerStateData::swanSong()
99{
100 // get rid of our piping obligations
5f8252d2 101 if (requestBodySource != NULL)
279152e7 102 stopConsumingFrom(requestBodySource);
5f8252d2 103
a83c6ed6
AR
104#if USE_ADAPTATION
105 cleanAdaptation();
5f8252d2 106#endif
7dc79973 107
49918309
AR
108 BodyConsumer::swanSong();
109#if USE_ADAPTATION
110 Initiator::swanSong();
111 BodyProducer::swanSong();
112#endif
b2c251cc
AJ
113
114 // paranoid: check that swanSong has been called
115 // extra paranoid: yeah, I really mean it. they MUST pass here.
116 assert(!requestBodySource);
117#if USE_ADAPTATION
118 assert(!virginBodyDestination);
119 assert(!adaptedBodySource);
120#endif
5f8252d2 121}
122
49918309 123
585ab260 124HttpReply *
9e008dda
AJ
125ServerStateData::virginReply()
126{
585ab260 127 assert(theVirginReply);
128 return theVirginReply;
129}
130
131const HttpReply *
9e008dda
AJ
132ServerStateData::virginReply() const
133{
585ab260 134 assert(theVirginReply);
135 return theVirginReply;
136}
137
138HttpReply *
9e008dda
AJ
139ServerStateData::setVirginReply(HttpReply *rep)
140{
585ab260 141 debugs(11,5, HERE << this << " setting virgin reply to " << rep);
142 assert(!theVirginReply);
143 assert(rep);
144 theVirginReply = HTTPMSGLOCK(rep);
9e008dda 145 return theVirginReply;
585ab260 146}
147
148HttpReply *
9e008dda
AJ
149ServerStateData::finalReply()
150{
585ab260 151 assert(theFinalReply);
152 return theFinalReply;
153}
154
155HttpReply *
9e008dda
AJ
156ServerStateData::setFinalReply(HttpReply *rep)
157{
585ab260 158 debugs(11,5, HERE << this << " setting final reply to " << rep);
159
160 assert(!theFinalReply);
161 assert(rep);
162 theFinalReply = HTTPMSGLOCK(rep);
163
164 entry->replaceHttpReply(theFinalReply);
165 haveParsedReplyHeaders();
166
167 return theFinalReply;
168}
169
5f8252d2 170// called when no more server communication is expected; may quit
171void
172ServerStateData::serverComplete()
173{
174 debugs(11,5,HERE << "serverComplete " << this);
175
176 if (!doneWithServer()) {
177 closeServer();
178 assert(doneWithServer());
179 }
180
7dc79973 181 completed = true;
182
3ff65596
AR
183 HttpRequest *r = originalRequest();
184 r->hier.total_response_time = r->hier.first_conn_start.tv_sec ?
e1381638
AJ
185 tvSubMsec(r->hier.first_conn_start, current_time) : -1;
186
5f8252d2 187 if (requestBodySource != NULL)
188 stopConsumingFrom(requestBodySource);
189
7dc79973 190 if (responseBodyBuffer != NULL)
9e008dda 191 return;
7dc79973 192
193 serverComplete2();
194}
195
196void
197ServerStateData::serverComplete2()
198{
199 debugs(11,5,HERE << "serverComplete2 " << this);
200
a83c6ed6 201#if USE_ADAPTATION
5f8252d2 202 if (virginBodyDestination != NULL)
203 stopProducingFor(virginBodyDestination, true);
204
a83c6ed6 205 if (!doneWithAdaptation())
5f8252d2 206 return;
207#endif
208
209 completeForwarding();
210 quitIfAllDone();
211}
212
9e008dda 213// When we are done talking to the primary server, we may be still talking
5f8252d2 214// to the ICAP service. And vice versa. Here, we quit only if we are done
215// talking to both.
9e008dda
AJ
216void ServerStateData::quitIfAllDone()
217{
a83c6ed6
AR
218#if USE_ADAPTATION
219 if (!doneWithAdaptation()) {
5f8252d2 220 debugs(11,5, HERE << "transaction not done: still talking to ICAP");
221 return;
222 }
223#endif
224
225 if (!doneWithServer()) {
226 debugs(11,5, HERE << "transaction not done: still talking to server");
227 return;
228 }
229
230 debugs(11,3, HERE << "transaction done");
dc56a9b1 231
232 deleteThis("ServerStateData::quitIfAllDone");
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
284 handleMoreRequestBodyAvailable();
285}
286
287// the entire request or adapted response body was provided, successfully
288void
dc56a9b1 289ServerStateData::noteBodyProductionEnded(BodyPipe::Pointer bp)
5f8252d2 290{
a83c6ed6 291#if USE_ADAPTATION
dc56a9b1 292 if (adaptedBodySource == bp) {
5f8252d2 293 handleAdaptedBodyProductionEnded();
294 return;
c99de607 295 }
cd304fc2 296#endif
5f8252d2 297 handleRequestBodyProductionEnded();
298}
299
300// premature end of the request or adapted response body production
301void
dc56a9b1 302ServerStateData::noteBodyProducerAborted(BodyPipe::Pointer bp)
5f8252d2 303{
a83c6ed6 304#if USE_ADAPTATION
dc56a9b1 305 if (adaptedBodySource == bp) {
5f8252d2 306 handleAdaptedBodyProducerAborted();
307 return;
308 }
309#endif
310 handleRequestBodyProducerAborted();
311}
312
313
314// more origin request body data is available
315void
316ServerStateData::handleMoreRequestBodyAvailable()
317{
318 if (!requestSender)
319 sendMoreRequestBody();
320 else
321 debugs(9,3, HERE << "waiting for request body write to complete");
322}
323
324// there will be no more handleMoreRequestBodyAvailable calls
325void
326ServerStateData::handleRequestBodyProductionEnded()
327{
39cb8c41 328 receivedWholeRequestBody = true;
5f8252d2 329 if (!requestSender)
330 doneSendingRequestBody();
331 else
332 debugs(9,3, HERE << "waiting for request body write to complete");
333}
334
335// called when we are done sending request body; kids extend this
336void
9e008dda
AJ
337ServerStateData::doneSendingRequestBody()
338{
5f8252d2 339 debugs(9,3, HERE << "done sending request body");
340 assert(requestBodySource != NULL);
341 stopConsumingFrom(requestBodySource);
342
343 // kids extend this
344}
345
346// called when body producers aborts; kids extend this
347void
348ServerStateData::handleRequestBodyProducerAborted()
349{
350 if (requestSender != NULL)
351 debugs(9,3, HERE << "fyi: request body aborted while we were sending");
352
0919c51e 353 fwd->dontRetry(true); // the problem is not with the server
5f8252d2 354 stopConsumingFrom(requestBodySource); // requestSender, if any, will notice
355
356 // kids extend this
357}
358
5f8252d2 359// called when we wrote request headers(!) or a part of the body
360void
dc56a9b1 361ServerStateData::sentRequestBody(const CommIoCbParams &io)
5f8252d2 362{
dc56a9b1 363 debugs(11, 5, "sentRequestBody: FD " << io.fd << ": size " << io.size << ": errflag " << io.flag << ".");
5f8252d2 364 debugs(32,3,HERE << "sentRequestBody called");
365
366 requestSender = NULL;
367
dc56a9b1 368 if (io.size > 0) {
369 fd_bytes(io.fd, io.size, FD_WRITE);
370 kb_incr(&statCounter.server.all.kbytes_out, io.size);
5f8252d2 371 // kids should increment their counters
372 }
373
dc56a9b1 374 if (io.flag == COMM_ERR_CLOSING)
5f8252d2 375 return;
376
377 if (!requestBodySource) {
378 debugs(9,3, HERE << "detected while-we-were-sending abort");
379 return; // do nothing;
380 }
381
dc56a9b1 382 if (io.flag) {
383 debugs(11, 1, "sentRequestBody error: FD " << io.fd << ": " << xstrerr(errno));
5f8252d2 384 ErrorState *err;
385 err = errorCon(ERR_WRITE_ERROR, HTTP_BAD_GATEWAY, fwd->request);
386 err->xerrno = errno;
387 fwd->fail(err);
388 abortTransaction("I/O error while sending request body");
389 return;
390 }
391
392 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
393 abortTransaction("store entry aborted while sending request body");
394 return;
395 }
396
39cb8c41
AR
397 if (!requestBodySource->exhausted())
398 sendMoreRequestBody();
de48b288 399 else if (receivedWholeRequestBody)
5f8252d2 400 doneSendingRequestBody();
401 else
39cb8c41 402 debugs(9,3, HERE << "waiting for body production end or abort");
cd304fc2 403}
404
a0297974
AR
405bool
406ServerStateData::canSend(int fd) const
407{
408 return fd >= 0 && !fd_table[fd].closing();
409}
410
5f8252d2 411void
412ServerStateData::sendMoreRequestBody()
413{
414 assert(requestBodySource != NULL);
415 assert(!requestSender);
a0297974
AR
416
417 const int fd = dataDescriptor();
418
419 if (!canSend(fd)) {
420 debugs(9,3, HERE << "cannot send request body to closing FD " << fd);
421 return; // wait for the kid's close handler; TODO: assert(closer);
422 }
423
5f8252d2 424 MemBuf buf;
39cb8c41 425 if (getMoreRequestBody(buf) && buf.contentSize() > 0) {
5f8252d2 426 debugs(9,3, HERE << "will write " << buf.contentSize() << " request body bytes");
9e008dda 427 typedef CommCbMemFunT<ServerStateData, CommIoCbParams> Dialer;
4299f876 428 requestSender = JobCallback(93,3,
4cb2536f 429 Dialer, this, ServerStateData::sentRequestBody);
ec41b64c 430 Comm::Write(fd, &buf, requestSender);
5f8252d2 431 } else {
432 debugs(9,3, HERE << "will wait for more request body bytes or eof");
433 requestSender = NULL;
434 }
435}
436
39cb8c41
AR
437/// either fill buf with available [encoded] request body bytes or return false
438bool
439ServerStateData::getMoreRequestBody(MemBuf &buf)
440{
441 // default implementation does not encode request body content
442 Must(requestBodySource != NULL);
443 return requestBodySource->getMoreData(buf);
444}
445
c1520b67
AJ
446// Compares hosts in urls, returns false if different, no sheme, or no host.
447static bool
448sameUrlHosts(const char *url1, const char *url2)
449{
450 // XXX: Want urlHostname() here, but it uses static storage and copying
451 const char *host1 = strchr(url1, ':');
452 const char *host2 = strchr(url2, ':');
453
454 if (host1 && host2) {
455 // skip scheme slashes
456 do {
457 ++host1;
458 ++host2;
459 } while (*host1 == '/' && *host2 == '/');
460
461 if (!*host1)
462 return false; // no host
463
464 // increment while the same until we reach the end of the URL/host
465 while (*host1 && *host1 != '/' && *host1 == *host2) {
466 ++host1;
467 ++host2;
468 }
469 return *host1 == *host2;
470 }
471
472 return false; // no URL scheme
473}
474
475// purges entries that match the value of a given HTTP [response] header
476static void
90bd689c 477purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, HttpMsg *rep, http_hdr_type hdr)
c1520b67 478{
bf956b0a 479 const char *hdrUrl, *absUrl;
9e008dda 480
71051277
BR
481 absUrl = NULL;
482 hdrUrl = rep->header.getStr(hdr);
483 if (hdrUrl == NULL) {
484 return;
485 }
9e008dda 486
71051277
BR
487 /*
488 * If the URL is relative, make it absolute so we can find it.
489 * If it's absolute, make sure the host parts match to avoid DOS attacks
490 * as per RFC 2616 13.10.
491 */
492 if (urlIsRelative(hdrUrl)) {
493 absUrl = urlMakeAbsolute(req, hdrUrl);
3cbbd242 494 if (absUrl != NULL) {
71051277 495 hdrUrl = absUrl;
3cbbd242 496 }
71051277
BR
497 } else if (!sameUrlHosts(reqUrl, hdrUrl)) {
498 return;
499 }
9e008dda 500
8dceeee3 501 purgeEntriesByUrl(req, hdrUrl);
9e008dda 502
71051277
BR
503 if (absUrl != NULL) {
504 safe_free(absUrl);
3cbbd242 505 }
c1520b67
AJ
506}
507
508// some HTTP methods should purge matching cache entries
509void
510ServerStateData::maybePurgeOthers()
511{
9e008dda
AJ
512 // only some HTTP methods should purge matching cache entries
513 if (!request->method.purgesOthers())
514 return;
c1520b67 515
9e008dda
AJ
516 // and probably only if the response was successful
517 if (theFinalReply->sline.status >= 400)
518 return;
c1520b67 519
9e008dda
AJ
520 // XXX: should we use originalRequest() here?
521 const char *reqUrl = urlCanonical(request);
522 debugs(88, 5, "maybe purging due to " << RequestMethodStr(request->method) << ' ' << reqUrl);
523 purgeEntriesByUrl(request, reqUrl);
524 purgeEntriesByHeader(request, reqUrl, theFinalReply, HDR_LOCATION);
525 purgeEntriesByHeader(request, reqUrl, theFinalReply, HDR_CONTENT_LOCATION);
c1520b67
AJ
526}
527
528// called (usually by kids) when we have final (possibly adapted) reply headers
5f8252d2 529void
530ServerStateData::haveParsedReplyHeaders()
531{
9e008dda
AJ
532 Must(theFinalReply);
533 maybePurgeOthers();
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:
670 handleAdaptedHeader(answer.message);
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{
585ab260 686 if (abortOnBadEntry("entry went bad while waiting for adapted headers"))
5f8252d2 687 return;
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 707// more adapted response body is available
708void
709ServerStateData::handleMoreAdaptedBodyAvailable()
710{
711 const size_t contentSize = adaptedBodySource->buf().contentSize();
712
713 debugs(11,5, HERE << "consuming " << contentSize << " bytes of adapted " <<
714 "response body at offset " << adaptedBodySource->consumedSize());
715
716 if (abortOnBadEntry("entry refuses adapted body"))
717 return;
718
719 assert(entry);
720 BodyPipeCheckout bpc(*adaptedBodySource);
cdad887f
AJ
721 const StoreIOBuffer ioBuf(&bpc.buf, currentOffset);
722 currentOffset += bpc.buf.size;
5f8252d2 723 entry->write(ioBuf);
724 bpc.buf.consume(contentSize);
725 bpc.checkIn();
726}
727
728// the entire adapted response body was produced, successfully
729void
730ServerStateData::handleAdaptedBodyProductionEnded()
731{
732 stopConsumingFrom(adaptedBodySource);
733
734 if (abortOnBadEntry("entry went bad while waiting for adapted body eof"))
735 return;
736
a83c6ed6 737 handleAdaptationCompleted();
5f8252d2 738}
739
740// premature end of the adapted response body
741void ServerStateData::handleAdaptedBodyProducerAborted()
742{
743 stopConsumingFrom(adaptedBodySource);
a83c6ed6 744 handleAdaptationAborted();
5f8252d2 745}
746
a83c6ed6 747// common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
5f8252d2 748void
a83c6ed6 749ServerStateData::handleAdaptationCompleted()
5f8252d2 750{
a83c6ed6
AR
751 debugs(11,5, HERE << "handleAdaptationCompleted");
752 cleanAdaptation();
bc81cb2b 753
754 // We stop reading origin response because we have no place to put it and
755 // cannot use it. If some origin servers do not like that or if we want to
756 // reuse more pconns, we can add code to discard unneeded origin responses.
757 if (!doneWithServer()) {
758 debugs(11,3, HERE << "closing origin conn due to ICAP completion");
759 closeServer();
760 }
761
5f8252d2 762 completeForwarding();
763 quitIfAllDone();
764}
765
bc81cb2b 766
a83c6ed6 767// common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods
5f8252d2 768void
a83c6ed6 769ServerStateData::handleAdaptationAborted(bool bypassable)
5f8252d2 770{
a83c6ed6 771 debugs(11,5, HERE << "handleAdaptationAborted; bypassable: " << bypassable <<
9e008dda 772 ", entry empty: " << entry->isEmpty());
5f8252d2 773
774 if (abortOnBadEntry("entry went bad while ICAP aborted"))
775 return;
776
0f283edf 777 // TODO: bypass if possible
778
5f8252d2 779 if (entry->isEmpty()) {
780 debugs(11,9, HERE << "creating ICAP error entry after ICAP failure");
c70281f8 781 ErrorState *err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
64b66b76 782 err->xerrno = ERR_DETAIL_ICAP_RESPMOD_EARLY;
5f8252d2 783 fwd->fail(err);
784 fwd->dontRetry(true);
b3c9f64a 785 } else if (request) { // update logged info directly
64b66b76
CT
786 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_ICAP_RESPMOD_LATE);
787 }
5f8252d2 788
0f283edf 789 abortTransaction("ICAP failure");
5f8252d2 790}
791
3af10ac0
AR
792// adaptation service wants us to deny HTTP client access to this response
793void
794ServerStateData::handleAdaptationBlocked(const Adaptation::Answer &answer)
795{
796 debugs(11,5, HERE << "handleAdaptationBlocked: " << answer.ruleId);
797
798 if (abortOnBadEntry("entry went bad while ICAP aborted"))
799 return;
800
801 if (!entry->isEmpty()) { // too late to block (should not really happen)
802 if (request)
803 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_RESPMOD_BLOCK_LATE);
804 abortTransaction("late adaptation block");
805 return;
806 }
807
808 debugs(11,7, HERE << "creating adaptation block response");
809
810 err_type page_id =
811 aclGetDenyInfoPage(&Config.denyInfoList, answer.ruleId.termedBuf(), 1);
812 if (page_id == ERR_NONE)
813 page_id = ERR_ACCESS_DENIED;
814
815 ErrorState *err = errorCon(page_id, HTTP_FORBIDDEN, request);
816 err->xerrno = ERR_DETAIL_RESPMOD_BLOCK_EARLY;
817 fwd->fail(err);
818 fwd->dontRetry(true);
819
820 abortTransaction("timely adaptation block");
821}
822
7c4e4e7f 823void
a22e6cd3 824ServerStateData::adaptationAclCheckDone(Adaptation::ServiceGroupPointer group)
7c4e4e7f 825{
a83c6ed6 826 adaptationAccessCheckPending = false;
7c4e4e7f 827
828 if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
829 return;
830
47416555 831 // TODO: Should nonICAP and postICAP path check this on the server-side?
832 // That check now only happens on client-side, in processReplyAccess().
833 if (virginReply()->expectedBodyTooLarge(*request)) {
834 sendBodyIsTooLargeError();
835 return;
836 }
abd4b611 837 // TODO: Should we check receivedBodyTooLarge on the server-side as well?
47416555 838
a22e6cd3
AR
839 if (!group) {
840 debugs(11,3, HERE << "no adapation needed");
585ab260 841 setFinalReply(virginReply());
7c4e4e7f 842 processReplyBody();
7c4e4e7f 843 return;
844 }
845
a22e6cd3 846 startAdaptation(group, originalRequest());
7c4e4e7f 847 processReplyBody();
848}
849
7dc79973 850void
a22e6cd3 851ServerStateData::adaptationAclCheckDoneWrapper(Adaptation::ServiceGroupPointer group, void *data)
7dc79973 852{
853 ServerStateData *state = (ServerStateData *)data;
a22e6cd3 854 state->adaptationAclCheckDone(group);
7dc79973 855}
0c25e715 856#endif
7dc79973 857
47416555 858void
859ServerStateData::sendBodyIsTooLargeError()
860{
861 ErrorState *err = errorCon(ERR_TOO_BIG, HTTP_FORBIDDEN, request);
862 err->xerrno = errno;
863 fwd->fail(err);
864 fwd->dontRetry(true);
865 abortTransaction("Virgin body too large.");
866}
867
9e008dda 868// TODO: when HttpStateData sends all errors to ICAP,
585ab260 869// we should be able to move this at the end of setVirginReply().
7dc79973 870void
585ab260 871ServerStateData::adaptOrFinalizeReply()
7dc79973 872{
a83c6ed6 873#if USE_ADAPTATION
abd4b611
AR
874 // TODO: merge with client side and return void to hide the on/off logic?
875 // The callback can be called with a NULL service if adaptation is off.
876 adaptationAccessCheckPending = Adaptation::AccessCheck::Start(
9e008dda
AJ
877 Adaptation::methodRespmod, Adaptation::pointPreCache,
878 request, virginReply(), adaptationAclCheckDoneWrapper, this);
c30ac6ea 879 debugs(11,5, HERE << "adaptationAccessCheckPending=" << adaptationAccessCheckPending);
abd4b611 880 if (adaptationAccessCheckPending)
7dc79973 881 return;
7dc79973 882#endif
883
585ab260 884 setFinalReply(virginReply());
7dc79973 885}
886
bae917ac
CT
887/// initializes bodyBytesRead stats if needed and applies delta
888void
889ServerStateData::adjustBodyBytesRead(const int64_t delta)
890{
891 int64_t &bodyBytesRead = originalRequest()->hier.bodyBytesRead;
892
893 // if we got here, do not log a dash even if we got nothing from the server
894 if (bodyBytesRead < 0)
895 bodyBytesRead = 0;
896
897 bodyBytesRead += delta; // supports negative and zero deltas
898
899 // check for overflows ("infinite" response?) and undeflows (a bug)
900 Must(bodyBytesRead >= 0);
901}
902
7dc79973 903void
bc81cb2b 904ServerStateData::addVirginReplyBody(const char *data, ssize_t len)
7dc79973 905{
bae917ac
CT
906 adjustBodyBytesRead(len);
907
a83c6ed6
AR
908#if USE_ADAPTATION
909 assert(!adaptationAccessCheckPending); // or would need to buffer while waiting
910 if (startedAdaptation) {
bc81cb2b 911 adaptVirginReplyBody(data, len);
7dc79973 912 return;
913 }
7dc79973 914#endif
bc81cb2b 915 storeReplyBody(data, len);
916}
7dc79973 917
bc81cb2b 918// writes virgin or adapted reply body to store
919void
920ServerStateData::storeReplyBody(const char *data, ssize_t len)
921{
2d1a172f 922 // write even if len is zero to push headers towards the client side
7dc79973 923 entry->write (StoreIOBuffer(len, currentOffset, (char*)data));
924
925 currentOffset += len;
926}
927
52edecde 928size_t ServerStateData::replyBodySpace(const MemBuf &readBuf,
1c9605c5 929 const size_t minSpace) const
7dc79973 930{
52edecde
AJ
931 size_t space = readBuf.spaceSize(); // available space w/o heroic measures
932 if (space < minSpace) {
933 const size_t maxSpace = readBuf.potentialSpaceSize(); // absolute best
934 space = min(minSpace, maxSpace); // do not promise more than asked
935 }
936
a83c6ed6 937#if USE_ADAPTATION
7dc79973 938 if (responseBodyBuffer) {
9e008dda 939 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
7dc79973 940 }
941
942 if (virginBodyDestination != NULL) {
943 /*
944 * BodyPipe buffer has a finite size limit. We
945 * should not read more data from the network than will fit
946 * into the pipe buffer or we _lose_ what did not fit if
947 * the response ends sooner that BodyPipe frees up space:
948 * There is no code to keep pumping data into the pipe once
949 * response ends and serverComplete() is called.
950 *
951 * If the pipe is totally full, don't register the read handler.
952 * The BodyPipe will call our noteMoreBodySpaceAvailable() method
953 * when it has free space again.
954 */
a83c6ed6
AR
955 size_t adaptation_space =
956 virginBodyDestination->buf().potentialSpaceSize();
7dc79973 957
a83c6ed6 958 debugs(11,9, "ServerStateData may read up to min(" <<
9e008dda 959 adaptation_space << ", " << space << ") bytes");
7dc79973 960
a83c6ed6
AR
961 if (adaptation_space < space)
962 space = adaptation_space;
7dc79973 963 }
964#endif
965
966 return space;
967}