]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Server.cc
Author: wessels & Christos Tsantilas
[thirdparty/squid.git] / src / Server.cc
CommitLineData
cd304fc2 1/*
47f6e231 2 * $Id: Server.cc,v 1.22 2007/08/13 17:20:51 hno Exp $
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.
23 *
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.
28 *
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"
36#include "Server.h"
37#include "Store.h"
38#include "HttpRequest.h"
39#include "HttpReply.h"
5f8252d2 40#include "errorpage.h"
41
0f283edf 42#if ICAP_CLIENT
43#include "ICAP/ICAPModXact.h"
7dc79973 44#include "ICAP/ICAPConfig.h"
45extern ICAPConfig TheICAPConfig;
0f283edf 46#endif
cd304fc2 47
77089558 48ServerStateData::ServerStateData(FwdState *theFwdState): requestSender(NULL)
49#if ICAP_CLIENT
50 , icapAccessCheckPending(false)
51#endif
cd304fc2 52{
53 fwd = theFwdState;
54 entry = fwd->entry;
34266cde 55
5f8252d2 56 entry->lock();
34266cde 57
6dd9f4bd 58 request = HTTPMSGLOCK(fwd->request);
cd304fc2 59}
60
61ServerStateData::~ServerStateData()
62{
97b5e68f 63 entry->unlock();
cd304fc2 64
6dd9f4bd 65 HTTPMSGUNLOCK(request);
585ab260 66 HTTPMSGUNLOCK(theVirginReply);
67 HTTPMSGUNLOCK(theFinalReply);
cd304fc2 68
69 fwd = NULL; // refcounted
70
5f8252d2 71 if (requestBodySource != NULL)
72 requestBodySource->clearConsumer();
73
74#if ICAP_CLIENT
75 cleanIcap();
76#endif
7dc79973 77
78 if (responseBodyBuffer != NULL) {
79 delete responseBodyBuffer;
80 responseBodyBuffer = NULL;
81 }
5f8252d2 82}
83
585ab260 84HttpReply *
85ServerStateData::virginReply() {
86 assert(theVirginReply);
87 return theVirginReply;
88}
89
90const HttpReply *
91ServerStateData::virginReply() const {
92 assert(theVirginReply);
93 return theVirginReply;
94}
95
96HttpReply *
97ServerStateData::setVirginReply(HttpReply *rep) {
98 debugs(11,5, HERE << this << " setting virgin reply to " << rep);
99 assert(!theVirginReply);
100 assert(rep);
101 theVirginReply = HTTPMSGLOCK(rep);
102 return theVirginReply;
103}
104
105HttpReply *
106ServerStateData::finalReply() {
107 assert(theFinalReply);
108 return theFinalReply;
109}
110
111HttpReply *
112ServerStateData::setFinalReply(HttpReply *rep) {
113 debugs(11,5, HERE << this << " setting final reply to " << rep);
114
115 assert(!theFinalReply);
116 assert(rep);
117 theFinalReply = HTTPMSGLOCK(rep);
118
119 entry->replaceHttpReply(theFinalReply);
120 haveParsedReplyHeaders();
121
122 return theFinalReply;
123}
124
5f8252d2 125// called when no more server communication is expected; may quit
126void
127ServerStateData::serverComplete()
128{
129 debugs(11,5,HERE << "serverComplete " << this);
130
131 if (!doneWithServer()) {
132 closeServer();
133 assert(doneWithServer());
134 }
135
7dc79973 136 completed = true;
137
5f8252d2 138 if (requestBodySource != NULL)
139 stopConsumingFrom(requestBodySource);
140
7dc79973 141 if (responseBodyBuffer != NULL)
142 return;
143
144 serverComplete2();
145}
146
147void
148ServerStateData::serverComplete2()
149{
150 debugs(11,5,HERE << "serverComplete2 " << this);
151
5f8252d2 152#if ICAP_CLIENT
153 if (virginBodyDestination != NULL)
154 stopProducingFor(virginBodyDestination, true);
155
156 if (!doneWithIcap())
157 return;
158#endif
159
160 completeForwarding();
161 quitIfAllDone();
162}
163
164// When we are done talking to the primary server, we may be still talking
165// to the ICAP service. And vice versa. Here, we quit only if we are done
166// talking to both.
167void ServerStateData::quitIfAllDone() {
168#if ICAP_CLIENT
169 if (!doneWithIcap()) {
170 debugs(11,5, HERE << "transaction not done: still talking to ICAP");
171 return;
172 }
173#endif
174
175 if (!doneWithServer()) {
176 debugs(11,5, HERE << "transaction not done: still talking to server");
177 return;
178 }
179
180 debugs(11,3, HERE << "transaction done");
181 delete this;
182}
183
184// FTP side overloads this to work around multiple calls to fwd->complete
185void
186ServerStateData::completeForwarding() {
187 debugs(11,5, HERE << "completing forwarding for " << fwd);
188 assert(fwd != NULL);
189 fwd->complete();
190}
191
192// Entry-dependent callbacks use this check to quit if the entry went bad
193bool
194ServerStateData::abortOnBadEntry(const char *abortReason)
195{
196 if (entry->isAccepting())
197 return false;
198
199 debugs(11,5, HERE << "entry is not Accepting!");
200 abortTransaction(abortReason);
201 return true;
202}
203
204// more request or adapted response body is available
205void
206ServerStateData::noteMoreBodyDataAvailable(BodyPipe &bp)
207{
208#if ICAP_CLIENT
209 if (adaptedBodySource == &bp) {
210 handleMoreAdaptedBodyAvailable();
211 return;
212 }
213#endif
214 handleMoreRequestBodyAvailable();
215}
216
217// the entire request or adapted response body was provided, successfully
218void
219ServerStateData::noteBodyProductionEnded(BodyPipe &bp)
220{
cd304fc2 221#if ICAP_CLIENT
5f8252d2 222 if (adaptedBodySource == &bp) {
223 handleAdaptedBodyProductionEnded();
224 return;
c99de607 225 }
cd304fc2 226#endif
5f8252d2 227 handleRequestBodyProductionEnded();
228}
229
230// premature end of the request or adapted response body production
231void
232ServerStateData::noteBodyProducerAborted(BodyPipe &bp)
233{
234#if ICAP_CLIENT
235 if (adaptedBodySource == &bp) {
236 handleAdaptedBodyProducerAborted();
237 return;
238 }
239#endif
240 handleRequestBodyProducerAborted();
241}
242
243
244// more origin request body data is available
245void
246ServerStateData::handleMoreRequestBodyAvailable()
247{
248 if (!requestSender)
249 sendMoreRequestBody();
250 else
251 debugs(9,3, HERE << "waiting for request body write to complete");
252}
253
254// there will be no more handleMoreRequestBodyAvailable calls
255void
256ServerStateData::handleRequestBodyProductionEnded()
257{
258 if (!requestSender)
259 doneSendingRequestBody();
260 else
261 debugs(9,3, HERE << "waiting for request body write to complete");
262}
263
264// called when we are done sending request body; kids extend this
265void
266ServerStateData::doneSendingRequestBody() {
267 debugs(9,3, HERE << "done sending request body");
268 assert(requestBodySource != NULL);
269 stopConsumingFrom(requestBodySource);
270
271 // kids extend this
272}
273
274// called when body producers aborts; kids extend this
275void
276ServerStateData::handleRequestBodyProducerAborted()
277{
278 if (requestSender != NULL)
279 debugs(9,3, HERE << "fyi: request body aborted while we were sending");
280
0919c51e 281 fwd->dontRetry(true); // the problem is not with the server
5f8252d2 282 stopConsumingFrom(requestBodySource); // requestSender, if any, will notice
283
284 // kids extend this
285}
286
287void
288ServerStateData::sentRequestBodyWrapper(int fd, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data)
289{
290 ServerStateData *server = static_cast<ServerStateData *>(data);
291 server->sentRequestBody(fd, size, errflag);
292}
293
294// called when we wrote request headers(!) or a part of the body
295void
296ServerStateData::sentRequestBody(int fd, size_t size, comm_err_t errflag)
297{
4a7a3d56 298 debugs(11, 5, "sentRequestBody: FD " << fd << ": size " << size << ": errflag " << errflag << ".");
5f8252d2 299 debugs(32,3,HERE << "sentRequestBody called");
300
301 requestSender = NULL;
302
303 if (size > 0) {
304 fd_bytes(fd, size, FD_WRITE);
305 kb_incr(&statCounter.server.all.kbytes_out, size);
306 // kids should increment their counters
307 }
308
309 if (errflag == COMM_ERR_CLOSING)
310 return;
311
312 if (!requestBodySource) {
313 debugs(9,3, HERE << "detected while-we-were-sending abort");
314 return; // do nothing;
315 }
316
317 if (errflag) {
bf8fe701 318 debugs(11, 1, "sentRequestBody error: FD " << fd << ": " << xstrerr(errno));
5f8252d2 319 ErrorState *err;
320 err = errorCon(ERR_WRITE_ERROR, HTTP_BAD_GATEWAY, fwd->request);
321 err->xerrno = errno;
322 fwd->fail(err);
323 abortTransaction("I/O error while sending request body");
324 return;
325 }
326
327 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
328 abortTransaction("store entry aborted while sending request body");
329 return;
330 }
331
332 if (requestBodySource->exhausted())
333 doneSendingRequestBody();
334 else
335 sendMoreRequestBody();
cd304fc2 336}
337
5f8252d2 338void
339ServerStateData::sendMoreRequestBody()
340{
341 assert(requestBodySource != NULL);
342 assert(!requestSender);
343 MemBuf buf;
344 if (requestBodySource->getMoreData(buf)) {
345 debugs(9,3, HERE << "will write " << buf.contentSize() << " request body bytes");
346 requestSender = &ServerStateData::sentRequestBodyWrapper;
347 comm_write_mbuf(dataDescriptor(), &buf, requestSender, this);
348 } else {
349 debugs(9,3, HERE << "will wait for more request body bytes or eof");
350 requestSender = NULL;
351 }
352}
353
0f283edf 354// called by noteIcapAnswer(), HTTP server overwrites this
5f8252d2 355void
356ServerStateData::haveParsedReplyHeaders()
357{
358 // default does nothing
359}
360
7dc79973 361HttpRequest *
362ServerStateData::originalRequest()
363{
364 return request;
365}
5f8252d2 366
0c25e715 367#if ICAP_CLIENT
cd304fc2 368/*
c99de607 369 * Initiate an ICAP transaction. Return true on success.
cd304fc2 370 * Caller will handle error condition by generating a Squid error message
371 * or take other action.
372 */
c99de607 373bool
5f8252d2 374ServerStateData::startIcap(ICAPServiceRep::Pointer service, HttpRequest *cause)
cd304fc2 375{
bf8fe701 376 debugs(11, 5, "ServerStateData::startIcap() called");
c99de607 377 if (!service) {
bf8fe701 378 debugs(11, 3, "ServerStateData::startIcap fails: lack of service");
c99de607 379 return false;
380 }
381 if (service->broken()) {
bf8fe701 382 debugs(11, 3, "ServerStateData::startIcap fails: broken service");
c99de607 383 return false;
384 }
5f8252d2 385
386 // check whether we should be sending a body as well
5f8252d2 387 // start body pipe to feed ICAP transaction if needed
585ab260 388 assert(!virginBodyDestination);
47f6e231 389 HttpReply *vrep = virginReply();
585ab260 390 assert(!vrep->body_pipe);
47f6e231 391 int64_t size = 0;
585ab260 392 if (vrep->expectingBody(cause->method, size) && size) {
5f8252d2 393 virginBodyDestination = new BodyPipe(this);
585ab260 394 vrep->body_pipe = virginBodyDestination;
5f8252d2 395 debugs(93, 6, HERE << "will send virgin reply body to " <<
396 virginBodyDestination << "; size: " << size);
c2eef5bd 397 if (size > 0)
398 virginBodyDestination->setBodySize(size);
5f8252d2 399 }
400
0f283edf 401 adaptedHeadSource = initiateIcap(
585ab260 402 new ICAPModXactLauncher(this, vrep, cause, service));
c99de607 403 return true;
cd304fc2 404}
0c25e715 405
5f8252d2 406// properly cleans up ICAP-related state
407// may be called multiple times
408void ServerStateData::cleanIcap() {
ba82c452 409 debugs(11,5, HERE << "cleaning ICAP; ACL: " << icapAccessCheckPending);
5f8252d2 410
411 if (virginBodyDestination != NULL)
412 stopProducingFor(virginBodyDestination, false);
413
0f283edf 414 announceInitiatorAbort(adaptedHeadSource);
5f8252d2 415
416 if (adaptedBodySource != NULL)
417 stopConsumingFrom(adaptedBodySource);
418
ba82c452 419 if (!icapAccessCheckPending) // we cannot cancel a pending callback
420 assert(doneWithIcap()); // make sure the two methods are in sync
5f8252d2 421}
422
423bool
424ServerStateData::doneWithIcap() const {
ba82c452 425 return !icapAccessCheckPending &&
426 !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource;
5f8252d2 427}
428
bc81cb2b 429// sends virgin reply body to ICAP, buffering excesses if needed
430void
431ServerStateData::adaptVirginReplyBody(const char *data, ssize_t len)
432{
433 assert(startedIcap);
434
435 if (!virginBodyDestination) {
436 debugs(11,3, HERE << "ICAP does not want more virgin body");
437 return;
438 }
439
440 // grow overflow area if already overflowed
441 if (responseBodyBuffer) {
442 responseBodyBuffer->append(data, len);
443 data = responseBodyBuffer->content();
444 len = responseBodyBuffer->contentSize();
445 }
446
447 const ssize_t putSize = virginBodyDestination->putMoreData(data, len);
448 data += putSize;
449 len -= putSize;
450
451 // if we had overflow area, shrink it as necessary
452 if (responseBodyBuffer) {
453 if (putSize == responseBodyBuffer->contentSize()) {
454 delete responseBodyBuffer;
455 responseBodyBuffer = NULL;
456 } else {
457 responseBodyBuffer->consume(putSize);
458 }
459 return;
460 }
461
462 // if we did not have an overflow area, create it as needed
463 if (len > 0) {
464 assert(!responseBodyBuffer);
465 responseBodyBuffer = new MemBuf;
466 responseBodyBuffer->init(4096, SQUID_TCP_SO_RCVBUF * 10);
467 responseBodyBuffer->append(data, len);
468 }
469}
470
5f8252d2 471// can supply more virgin response body data
472void
473ServerStateData::noteMoreBodySpaceAvailable(BodyPipe &)
474{
7dc79973 475 if (responseBodyBuffer) {
bc81cb2b 476 addVirginReplyBody(NULL, 0); // kick the buffered fragment alive again
477 if (completed && !responseBodyBuffer) {
478 serverComplete2();
479 return;
480 }
7dc79973 481 }
5f8252d2 482 maybeReadVirginBody();
483}
484
bc81cb2b 485// the consumer of our virgin response body aborted
5f8252d2 486void
487ServerStateData::noteBodyConsumerAborted(BodyPipe &bp)
488{
489 stopProducingFor(virginBodyDestination, false);
bc81cb2b 490
491 // do not force closeServer here in case we need to bypass IcapQueryAbort
492
493 if (doneWithIcap()) // we may still be receiving adapted response
494 handleIcapCompleted();
5f8252d2 495}
496
497// received adapted response headers (body may follow)
498void
0f283edf 499ServerStateData::noteIcapAnswer(HttpMsg *msg)
5f8252d2 500{
0f283edf 501 clearIcap(adaptedHeadSource); // we do not expect more messages
5f8252d2 502
585ab260 503 if (abortOnBadEntry("entry went bad while waiting for adapted headers"))
5f8252d2 504 return;
5f8252d2 505
585ab260 506 HttpReply *rep = dynamic_cast<HttpReply*>(msg);
5f8252d2 507 assert(rep);
585ab260 508 debugs(11,5, HERE << this << " setting adapted reply to " << rep);
509 setFinalReply(rep);
5f8252d2 510
511 assert(!adaptedBodySource);
585ab260 512 if (rep->body_pipe != NULL) {
5f8252d2 513 // subscribe to receive adapted body
585ab260 514 adaptedBodySource = rep->body_pipe;
5f8252d2 515 // assume that ICAP does not auto-consume on failures
516 assert(adaptedBodySource->setConsumerIfNotLate(this));
517 } else {
518 // no body
bc81cb2b 519 if (doneWithIcap()) // we may still be sending virgin response
520 handleIcapCompleted();
5f8252d2 521 }
522
523}
524
525// will not receive adapted response headers (and, hence, body)
526void
0f283edf 527ServerStateData::noteIcapQueryAbort(bool final)
5f8252d2 528{
0f283edf 529 clearIcap(adaptedHeadSource);
530 handleIcapAborted(!final);
5f8252d2 531}
532
533// more adapted response body is available
534void
535ServerStateData::handleMoreAdaptedBodyAvailable()
536{
537 const size_t contentSize = adaptedBodySource->buf().contentSize();
538
539 debugs(11,5, HERE << "consuming " << contentSize << " bytes of adapted " <<
540 "response body at offset " << adaptedBodySource->consumedSize());
541
542 if (abortOnBadEntry("entry refuses adapted body"))
543 return;
544
545 assert(entry);
546 BodyPipeCheckout bpc(*adaptedBodySource);
547 const StoreIOBuffer ioBuf(&bpc.buf, bpc.offset);
548 entry->write(ioBuf);
549 bpc.buf.consume(contentSize);
550 bpc.checkIn();
551}
552
553// the entire adapted response body was produced, successfully
554void
555ServerStateData::handleAdaptedBodyProductionEnded()
556{
557 stopConsumingFrom(adaptedBodySource);
558
559 if (abortOnBadEntry("entry went bad while waiting for adapted body eof"))
560 return;
561
562 handleIcapCompleted();
563}
564
565// premature end of the adapted response body
566void ServerStateData::handleAdaptedBodyProducerAborted()
567{
568 stopConsumingFrom(adaptedBodySource);
569 handleIcapAborted();
570}
571
0f283edf 572// common part of noteIcapAnswer and handleAdaptedBodyProductionEnded
5f8252d2 573void
574ServerStateData::handleIcapCompleted()
575{
576 debugs(11,5, HERE << "handleIcapCompleted");
577 cleanIcap();
bc81cb2b 578
579 // We stop reading origin response because we have no place to put it and
580 // cannot use it. If some origin servers do not like that or if we want to
581 // reuse more pconns, we can add code to discard unneeded origin responses.
582 if (!doneWithServer()) {
583 debugs(11,3, HERE << "closing origin conn due to ICAP completion");
584 closeServer();
585 }
586
5f8252d2 587 completeForwarding();
bc81cb2b 588
5f8252d2 589 quitIfAllDone();
590}
591
bc81cb2b 592
5f8252d2 593// common part of noteIcap*Aborted and noteBodyConsumerAborted methods
594void
0f283edf 595ServerStateData::handleIcapAborted(bool bypassable)
5f8252d2 596{
0f283edf 597 debugs(11,5, HERE << "handleIcapAborted; bypassable: " << bypassable <<
598 ", entry empty: " << entry->isEmpty());
5f8252d2 599
600 if (abortOnBadEntry("entry went bad while ICAP aborted"))
601 return;
602
0f283edf 603 // TODO: bypass if possible
604
5f8252d2 605 if (entry->isEmpty()) {
606 debugs(11,9, HERE << "creating ICAP error entry after ICAP failure");
607 ErrorState *err =
608 errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
609 err->xerrno = errno;
610 fwd->fail(err);
611 fwd->dontRetry(true);
612 }
613
0f283edf 614 abortTransaction("ICAP failure");
5f8252d2 615}
616
7c4e4e7f 617void
618ServerStateData::icapAclCheckDone(ICAPServiceRep::Pointer service)
619{
620 icapAccessCheckPending = false;
621
622 if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
623 return;
624
bc81cb2b 625 startedIcap = startIcap(service, originalRequest());
7c4e4e7f 626
627 if (!startedIcap && (!service || service->bypass)) {
628 // handle ICAP start failure when no service was selected
629 // or where the selected service was optional
585ab260 630 setFinalReply(virginReply());
7c4e4e7f 631 processReplyBody();
7c4e4e7f 632 return;
633 }
634
635 if (!startedIcap) {
636 // handle start failure for an essential ICAP service
637 ErrorState *err = errorCon(ERR_ICAP_FAILURE,
638 HTTP_INTERNAL_SERVER_ERROR, originalRequest());
639 err->xerrno = errno;
640 errorAppendEntry(entry, err);
641 abortTransaction("ICAP start failure");
642 return;
643 }
644
645 processReplyBody();
646}
647
7dc79973 648void
649ServerStateData::icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data)
650{
651 ServerStateData *state = (ServerStateData *)data;
652 state->icapAclCheckDone(service);
653}
0c25e715 654#endif
7dc79973 655
585ab260 656// TODO: when HttpStateData sends all errors to ICAP,
657// we should be able to move this at the end of setVirginReply().
7dc79973 658void
585ab260 659ServerStateData::adaptOrFinalizeReply()
7dc79973 660{
7dc79973 661#if ICAP_CLIENT
662
663 if (TheICAPConfig.onoff) {
664 ICAPAccessCheck *icap_access_check =
585ab260 665 new ICAPAccessCheck(ICAP::methodRespmod, ICAP::pointPreCache,
666 request, virginReply(), icapAclCheckDoneWrapper, this);
7dc79973 667
668 icapAccessCheckPending = true;
669 icap_access_check->check(); // will eventually delete self
670 return;
671 }
672
673#endif
674
585ab260 675 setFinalReply(virginReply());
7dc79973 676}
677
678void
bc81cb2b 679ServerStateData::addVirginReplyBody(const char *data, ssize_t len)
7dc79973 680{
7dc79973 681#if ICAP_CLIENT
bc81cb2b 682 assert(!icapAccessCheckPending); // or would need to buffer while waiting
683 if (startedIcap) {
684 adaptVirginReplyBody(data, len);
7dc79973 685 return;
686 }
7dc79973 687#endif
bc81cb2b 688 storeReplyBody(data, len);
689}
7dc79973 690
bc81cb2b 691// writes virgin or adapted reply body to store
692void
693ServerStateData::storeReplyBody(const char *data, ssize_t len)
694{
2d1a172f 695 // write even if len is zero to push headers towards the client side
7dc79973 696 entry->write (StoreIOBuffer(len, currentOffset, (char*)data));
697
698 currentOffset += len;
699}
700
701size_t ServerStateData::replyBodySpace(size_t space)
702{
703#if ICAP_CLIENT
704 if (responseBodyBuffer) {
705 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
706 }
707
708 if (virginBodyDestination != NULL) {
709 /*
710 * BodyPipe buffer has a finite size limit. We
711 * should not read more data from the network than will fit
712 * into the pipe buffer or we _lose_ what did not fit if
713 * the response ends sooner that BodyPipe frees up space:
714 * There is no code to keep pumping data into the pipe once
715 * response ends and serverComplete() is called.
716 *
717 * If the pipe is totally full, don't register the read handler.
718 * The BodyPipe will call our noteMoreBodySpaceAvailable() method
719 * when it has free space again.
720 */
721 size_t icap_space = virginBodyDestination->buf().potentialSpaceSize();
722
723 debugs(11,9, "ServerStateData may read up to min(" << icap_space <<
724 ", " << space << ") bytes");
725
726 if (icap_space < space)
727 space = icap_space;
728 }
729#endif
730
731 return space;
732}