]>
Commit | Line | Data |
---|---|---|
bbc27441 | 1 | /* |
bde978a6 | 2 | * Copyright (C) 1996-2015 The Squid Software Foundation and contributors |
bbc27441 AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
1b39caaa | 8 | |
582c2af2 | 9 | #include "squid.h" |
4299f876 | 10 | #include "base/AsyncJobCalls.h" |
3d93a84d | 11 | #include "base/TextException.h" |
1b39caaa | 12 | #include "BodyPipe.h" |
13 | ||
e7352f30 | 14 | // BodySink is a BodyConsumer class which just consume and drops |
26ac0430 AJ |
15 | // data from a BodyPipe |
16 | class BodySink: public BodyConsumer | |
17 | { | |
5c2f68b7 AJ |
18 | CBDATA_CLASS(BodySink); |
19 | ||
e7352f30 | 20 | public: |
5a856d1e AR |
21 | BodySink(const BodyPipe::Pointer &bp): AsyncJob("BodySink"), body_pipe(bp) {} |
22 | virtual ~BodySink() { assert(!body_pipe); } | |
26ac0430 | 23 | |
e7352f30 | 24 | virtual void noteMoreBodyDataAvailable(BodyPipe::Pointer bp) { |
26ac0430 AJ |
25 | size_t contentSize = bp->buf().contentSize(); |
26 | bp->consume(contentSize); | |
e7352f30 | 27 | } |
ced8def3 | 28 | virtual void noteBodyProductionEnded(BodyPipe::Pointer) { |
5a856d1e | 29 | stopConsumingFrom(body_pipe); |
e7352f30 | 30 | } |
ced8def3 | 31 | virtual void noteBodyProducerAborted(BodyPipe::Pointer) { |
5a856d1e | 32 | stopConsumingFrom(body_pipe); |
e7352f30 | 33 | } |
5a856d1e AR |
34 | bool doneAll() const {return !body_pipe && AsyncJob::doneAll();} |
35 | ||
36 | private: | |
37 | BodyPipe::Pointer body_pipe; ///< the pipe we are consuming from | |
e7352f30 | 38 | }; |
39 | ||
40 | CBDATA_CLASS_INIT(BodySink); | |
41 | ||
42 | // The BodyProducerDialer is an AsyncCall class which used to schedule BodyProducer calls. | |
26ac0430 | 43 | // In addition to a normal AsyncCall checks if the BodyProducer is still the producer of |
e7352f30 | 44 | // the BodyPipe passed as argument |
45 | class BodyProducerDialer: public UnaryMemFunT<BodyProducer, BodyPipe::Pointer> | |
46 | { | |
47 | public: | |
26ac0430 | 48 | typedef UnaryMemFunT<BodyProducer, BodyPipe::Pointer> Parent; |
e7352f30 | 49 | |
4299f876 | 50 | BodyProducerDialer(const BodyProducer::Pointer &aProducer, |
4cb2536f | 51 | Parent::Method aHandler, BodyPipe::Pointer bp): |
f53969cc | 52 | Parent(aProducer, aHandler, bp) {} |
e7352f30 | 53 | |
26ac0430 | 54 | virtual bool canDial(AsyncCall &call); |
e7352f30 | 55 | }; |
56 | ||
57 | // The BodyConsumerDialer is an AsyncCall class which used to schedule BodyConsumer calls. | |
26ac0430 | 58 | // In addition to a normal AsyncCall checks if the BodyConsumer is still the reciptient |
e7352f30 | 59 | // of the BodyPipe passed as argument |
60 | class BodyConsumerDialer: public UnaryMemFunT<BodyConsumer, BodyPipe::Pointer> | |
61 | { | |
62 | public: | |
26ac0430 | 63 | typedef UnaryMemFunT<BodyConsumer, BodyPipe::Pointer> Parent; |
e7352f30 | 64 | |
4299f876 | 65 | BodyConsumerDialer(const BodyConsumer::Pointer &aConsumer, |
4cb2536f | 66 | Parent::Method aHandler, BodyPipe::Pointer bp): |
f53969cc | 67 | Parent(aConsumer, aHandler, bp) {} |
e7352f30 | 68 | |
26ac0430 | 69 | virtual bool canDial(AsyncCall &call); |
e7352f30 | 70 | }; |
71 | ||
72 | bool | |
26ac0430 AJ |
73 | BodyProducerDialer::canDial(AsyncCall &call) |
74 | { | |
75 | if (!Parent::canDial(call)) | |
76 | return false; | |
77 | ||
4299f876 | 78 | const BodyProducer::Pointer &producer = job; |
3be43416 AJ |
79 | BodyPipe::Pointer aPipe = arg1; |
80 | if (!aPipe->stillProducing(producer)) { | |
81 | debugs(call.debugSection, call.debugLevel, producer << " no longer producing for " << aPipe->status()); | |
26ac0430 AJ |
82 | return call.cancel("no longer producing"); |
83 | } | |
e7352f30 | 84 | |
26ac0430 | 85 | return true; |
e7352f30 | 86 | } |
87 | ||
88 | bool | |
26ac0430 AJ |
89 | BodyConsumerDialer::canDial(AsyncCall &call) |
90 | { | |
91 | if (!Parent::canDial(call)) | |
92 | return false; | |
93 | ||
4299f876 | 94 | const BodyConsumer::Pointer &consumer = job; |
3be43416 AJ |
95 | BodyPipe::Pointer aPipe = arg1; |
96 | if (!aPipe->stillConsuming(consumer)) { | |
97 | debugs(call.debugSection, call.debugLevel, consumer << " no longer consuming from " << aPipe->status()); | |
26ac0430 AJ |
98 | return call.cancel("no longer consuming"); |
99 | } | |
e7352f30 | 100 | |
26ac0430 | 101 | return true; |
e7352f30 | 102 | } |
103 | ||
e7352f30 | 104 | /* BodyProducer */ |
105 | ||
1b39caaa | 106 | // inform the pipe that we are done and clear the Pointer |
3be43416 | 107 | void BodyProducer::stopProducingFor(RefCount<BodyPipe> &p, bool atEof) |
1b39caaa | 108 | { |
3be43416 AJ |
109 | debugs(91,7, this << " will not produce for " << p << "; atEof: " << atEof); |
110 | assert(p != NULL); // be strict: the caller state may depend on this | |
111 | p->clearProducer(atEof); | |
112 | p = NULL; | |
1b39caaa | 113 | } |
114 | ||
e7352f30 | 115 | /* BodyConsumer */ |
116 | ||
1b39caaa | 117 | // inform the pipe that we are done and clear the Pointer |
3be43416 | 118 | void BodyConsumer::stopConsumingFrom(RefCount<BodyPipe> &p) |
1b39caaa | 119 | { |
3be43416 AJ |
120 | debugs(91,7, this << " will not consume from " << p); |
121 | assert(p != NULL); // be strict: the caller state may depend on this | |
122 | p->clearConsumer(); | |
123 | p = NULL; | |
1b39caaa | 124 | } |
125 | ||
126 | /* BodyPipe */ | |
127 | ||
128 | BodyPipe::BodyPipe(Producer *aProducer): theBodySize(-1), | |
f53969cc SM |
129 | theProducer(aProducer), theConsumer(0), |
130 | thePutSize(0), theGetSize(0), | |
131 | mustAutoConsume(false), abortedConsumption(false), isCheckedOut(false) | |
1b39caaa | 132 | { |
26ac0430 AJ |
133 | // TODO: teach MemBuf to start with zero minSize |
134 | // TODO: limit maxSize by theBodySize, when known? | |
135 | theBuf.init(2*1024, MaxCapacity); | |
136 | debugs(91,7, HERE << "created BodyPipe" << status()); | |
1b39caaa | 137 | } |
138 | ||
139 | BodyPipe::~BodyPipe() | |
140 | { | |
26ac0430 AJ |
141 | debugs(91,7, HERE << "destroying BodyPipe" << status()); |
142 | assert(!theProducer); | |
143 | assert(!theConsumer); | |
144 | theBuf.clean(); | |
1b39caaa | 145 | } |
146 | ||
47f6e231 | 147 | void BodyPipe::setBodySize(uint64_t aBodySize) |
1b39caaa | 148 | { |
26ac0430 | 149 | assert(!bodySizeKnown()); |
26ac0430 | 150 | assert(thePutSize <= aBodySize); |
1b39caaa | 151 | |
26ac0430 AJ |
152 | // If this assert fails, we need to add code to check for eof and inform |
153 | // the consumer about the eof condition via scheduleBodyEndNotification, | |
154 | // because just setting a body size limit may trigger the eof condition. | |
155 | assert(!theConsumer); | |
1b39caaa | 156 | |
26ac0430 AJ |
157 | theBodySize = aBodySize; |
158 | debugs(91,7, HERE << "set body size" << status()); | |
1b39caaa | 159 | } |
160 | ||
47f6e231 | 161 | uint64_t BodyPipe::bodySize() const |
1b39caaa | 162 | { |
26ac0430 AJ |
163 | assert(bodySizeKnown()); |
164 | return static_cast<uint64_t>(theBodySize); | |
1b39caaa | 165 | } |
166 | ||
47f6e231 | 167 | bool BodyPipe::expectMoreAfter(uint64_t offset) const |
1b39caaa | 168 | { |
26ac0430 AJ |
169 | assert(theGetSize <= offset); |
170 | return offset < thePutSize || // buffer has more now or | |
171 | (!productionEnded() && mayNeedMoreData()); // buffer will have more | |
1b39caaa | 172 | } |
173 | ||
174 | bool BodyPipe::exhausted() const | |
175 | { | |
26ac0430 | 176 | return !expectMoreAfter(theGetSize); |
1b39caaa | 177 | } |
178 | ||
610d8f3b | 179 | uint64_t BodyPipe::unproducedSize() const |
1b39caaa | 180 | { |
26ac0430 | 181 | return bodySize() - thePutSize; // bodySize() asserts that size is known |
1b39caaa | 182 | } |
183 | ||
83c51da9 CT |
184 | void BodyPipe::expectProductionEndAfter(uint64_t size) |
185 | { | |
186 | const uint64_t expectedSize = thePutSize + size; | |
187 | if (bodySizeKnown()) | |
188 | Must(bodySize() == expectedSize); | |
189 | else | |
190 | theBodySize = expectedSize; | |
191 | } | |
192 | ||
1b39caaa | 193 | void |
194 | BodyPipe::clearProducer(bool atEof) | |
195 | { | |
4299f876 | 196 | if (theProducer.set()) { |
26ac0430 | 197 | debugs(91,7, HERE << "clearing BodyPipe producer" << status()); |
4299f876 | 198 | theProducer.clear(); |
26ac0430 AJ |
199 | if (atEof) { |
200 | if (!bodySizeKnown()) | |
201 | theBodySize = thePutSize; | |
e1381638 AJ |
202 | else if (bodySize() != thePutSize) |
203 | debugs(91,3, HERE << "aborting on premature eof" << status()); | |
26ac0430 AJ |
204 | } else { |
205 | // asserta that we can detect the abort if the consumer joins later | |
206 | assert(!bodySizeKnown() || bodySize() != thePutSize); | |
207 | } | |
208 | scheduleBodyEndNotification(); | |
209 | } | |
1b39caaa | 210 | } |
211 | ||
212 | size_t | |
350e2aec | 213 | BodyPipe::putMoreData(const char *aBuffer, size_t size) |
1b39caaa | 214 | { |
26ac0430 | 215 | if (bodySizeKnown()) |
d85c3078 | 216 | size = min((uint64_t)size, unproducedSize()); |
26ac0430 AJ |
217 | |
218 | const size_t spaceSize = static_cast<size_t>(theBuf.potentialSpaceSize()); | |
d85c3078 | 219 | if ((size = min(size, spaceSize))) { |
350e2aec | 220 | theBuf.append(aBuffer, size); |
26ac0430 AJ |
221 | postAppend(size); |
222 | return size; | |
223 | } | |
224 | return 0; | |
1b39caaa | 225 | } |
226 | ||
227 | bool | |
4299f876 | 228 | BodyPipe::setConsumerIfNotLate(const Consumer::Pointer &aConsumer) |
1b39caaa | 229 | { |
26ac0430 | 230 | assert(!theConsumer); |
4299f876 | 231 | assert(aConsumer.set()); // but might be invalid |
26ac0430 AJ |
232 | |
233 | // TODO: convert this into an exception and remove IfNotLate suffix | |
234 | // If there is something consumed already, we are in an auto-consuming mode | |
235 | // and it is too late to attach a real consumer to the pipe. | |
236 | if (theGetSize > 0) { | |
237 | assert(mustAutoConsume); | |
238 | return false; | |
239 | } | |
1b39caaa | 240 | |
abe286b8 AR |
241 | Must(!abortedConsumption); // did not promise to never consume |
242 | ||
26ac0430 AJ |
243 | theConsumer = aConsumer; |
244 | debugs(91,7, HERE << "set consumer" << status()); | |
245 | if (theBuf.hasContent()) | |
246 | scheduleBodyDataNotification(); | |
247 | if (!theProducer) | |
248 | scheduleBodyEndNotification(); | |
1b39caaa | 249 | |
26ac0430 | 250 | return true; |
1b39caaa | 251 | } |
252 | ||
253 | void | |
26ac0430 AJ |
254 | BodyPipe::clearConsumer() |
255 | { | |
4299f876 | 256 | if (theConsumer.set()) { |
26ac0430 | 257 | debugs(91,7, HERE << "clearing consumer" << status()); |
4299f876 | 258 | theConsumer.clear(); |
abe286b8 AR |
259 | // do not abort if we have not consumed so that HTTP or ICAP can retry |
260 | // benign xaction failures due to persistent connection race conditions | |
261 | if (consumedSize()) | |
262 | expectNoConsumption(); | |
263 | } | |
264 | } | |
265 | ||
266 | void | |
267 | BodyPipe::expectNoConsumption() | |
268 | { | |
b84d327b AR |
269 | // We may be called multiple times because multiple jobs on the consumption |
270 | // chain may realize that there will be no more setConsumer() calls (e.g., | |
271 | // consuming code and retrying code). It is both difficult and not really | |
272 | // necessary for them to coordinate their expectNoConsumption() calls. | |
273 | ||
274 | // As a consequence, we may be called when we are auto-consuming already. | |
275 | ||
abe286b8 | 276 | if (!abortedConsumption && !exhausted()) { |
b84d327b | 277 | // Before we abort, any regular consumption should be over and auto |
d8eddc48 | 278 | // consumption must not be started. |
b84d327b AR |
279 | Must(!theConsumer); |
280 | ||
e6f9e263 A |
281 | AsyncCall::Pointer call= asyncCall(91, 7, |
282 | "BodyProducer::noteBodyConsumerAborted", | |
283 | BodyProducerDialer(theProducer, | |
f53969cc | 284 | &BodyProducer::noteBodyConsumerAborted, this)); |
e6f9e263 A |
285 | ScheduleCallHere(call); |
286 | abortedConsumption = true; | |
b84d327b AR |
287 | |
288 | // in case somebody enabled auto-consumption before regular one aborted | |
289 | if (mustAutoConsume) | |
290 | startAutoConsumption(); | |
26ac0430 | 291 | } |
1b39caaa | 292 | } |
293 | ||
294 | size_t | |
350e2aec | 295 | BodyPipe::getMoreData(MemBuf &aMemBuffer) |
1b39caaa | 296 | { |
26ac0430 AJ |
297 | if (!theBuf.hasContent()) |
298 | return 0; // did not touch the possibly uninitialized buf | |
1b39caaa | 299 | |
350e2aec FC |
300 | if (aMemBuffer.isNull()) |
301 | aMemBuffer.init(); | |
302 | const size_t size = min(theBuf.contentSize(), aMemBuffer.potentialSpaceSize()); | |
303 | aMemBuffer.append(theBuf.content(), size); | |
26ac0430 AJ |
304 | theBuf.consume(size); |
305 | postConsume(size); | |
306 | return size; // cannot be zero if we called buf.init above | |
1b39caaa | 307 | } |
308 | ||
309 | void | |
310 | BodyPipe::consume(size_t size) | |
311 | { | |
26ac0430 AJ |
312 | theBuf.consume(size); |
313 | postConsume(size); | |
1b39caaa | 314 | } |
315 | ||
26ac0430 | 316 | // In the AutoConsumption mode the consumer has gone but the producer continues |
e7352f30 | 317 | // producing data. We are using a BodySink BodyConsumer which just discards the produced data. |
1b39caaa | 318 | void |
26ac0430 AJ |
319 | BodyPipe::enableAutoConsumption() |
320 | { | |
321 | mustAutoConsume = true; | |
322 | debugs(91,5, HERE << "enabled auto consumption" << status()); | |
323 | if (!theConsumer && theBuf.hasContent()) | |
324 | startAutoConsumption(); | |
5121d48e AR |
325 | } |
326 | ||
327 | // start auto consumption by creating body sink | |
328 | void | |
329 | BodyPipe::startAutoConsumption() | |
330 | { | |
26ac0430 AJ |
331 | Must(mustAutoConsume); |
332 | Must(!theConsumer); | |
5a856d1e | 333 | theConsumer = new BodySink(this); |
26ac0430 AJ |
334 | debugs(91,7, HERE << "starting auto consumption" << status()); |
335 | scheduleBodyDataNotification(); | |
1b39caaa | 336 | } |
337 | ||
338 | MemBuf & | |
26ac0430 AJ |
339 | BodyPipe::checkOut() |
340 | { | |
341 | assert(!isCheckedOut); | |
342 | isCheckedOut = true; | |
343 | return theBuf; | |
1b39caaa | 344 | } |
345 | ||
346 | void | |
347 | BodyPipe::checkIn(Checkout &checkout) | |
348 | { | |
26ac0430 AJ |
349 | assert(isCheckedOut); |
350 | isCheckedOut = false; | |
351 | const size_t currentSize = theBuf.contentSize(); | |
352 | if (checkout.checkedOutSize > currentSize) | |
353 | postConsume(checkout.checkedOutSize - currentSize); | |
e1381638 AJ |
354 | else if (checkout.checkedOutSize < currentSize) |
355 | postAppend(currentSize - checkout.checkedOutSize); | |
1b39caaa | 356 | } |
357 | ||
358 | void | |
359 | BodyPipe::undoCheckOut(Checkout &checkout) | |
360 | { | |
26ac0430 AJ |
361 | assert(isCheckedOut); |
362 | const size_t currentSize = theBuf.contentSize(); | |
363 | // We can only undo if size did not change, and even that carries | |
364 | // some risk. If this becomes a problem, the code checking out | |
365 | // raw buffers should always check them in (possibly unchanged) | |
366 | // instead of relying on the automated undo mechanism of Checkout. | |
367 | // The code can always use a temporary buffer to accomplish that. | |
87728a5b | 368 | Must(checkout.checkedOutSize == currentSize); |
1b39caaa | 369 | } |
370 | ||
371 | // TODO: Optimize: inform consumer/producer about more data/space only if | |
372 | // they used the data/space since we notified them last time. | |
373 | ||
374 | void | |
26ac0430 AJ |
375 | BodyPipe::postConsume(size_t size) |
376 | { | |
377 | assert(!isCheckedOut); | |
378 | theGetSize += size; | |
379 | debugs(91,7, HERE << "consumed " << size << " bytes" << status()); | |
380 | if (mayNeedMoreData()) { | |
381 | AsyncCall::Pointer call= asyncCall(91, 7, | |
382 | "BodyProducer::noteMoreBodySpaceAvailable", | |
383 | BodyProducerDialer(theProducer, | |
f53969cc | 384 | &BodyProducer::noteMoreBodySpaceAvailable, this)); |
26ac0430 AJ |
385 | ScheduleCallHere(call); |
386 | } | |
1b39caaa | 387 | } |
388 | ||
389 | void | |
26ac0430 AJ |
390 | BodyPipe::postAppend(size_t size) |
391 | { | |
392 | assert(!isCheckedOut); | |
393 | thePutSize += size; | |
394 | debugs(91,7, HERE << "added " << size << " bytes" << status()); | |
1b39caaa | 395 | |
26ac0430 AJ |
396 | if (mustAutoConsume && !theConsumer && size > 0) |
397 | startAutoConsumption(); | |
5121d48e | 398 | |
26ac0430 AJ |
399 | // We should not consume here even if mustAutoConsume because the |
400 | // caller may not be ready for the data to be consumed during this call. | |
401 | scheduleBodyDataNotification(); | |
1b39caaa | 402 | |
26ac0430 AJ |
403 | if (!mayNeedMoreData()) |
404 | clearProducer(true); // reached end-of-body | |
1b39caaa | 405 | } |
406 | ||
6c56baf6 | 407 | void |
408 | BodyPipe::scheduleBodyDataNotification() | |
409 | { | |
4299f876 | 410 | if (theConsumer.valid()) { // TODO: allow asyncCall() to check this instead |
26ac0430 AJ |
411 | AsyncCall::Pointer call = asyncCall(91, 7, |
412 | "BodyConsumer::noteMoreBodyDataAvailable", | |
413 | BodyConsumerDialer(theConsumer, | |
f53969cc | 414 | &BodyConsumer::noteMoreBodyDataAvailable, this)); |
26ac0430 AJ |
415 | ScheduleCallHere(call); |
416 | } | |
6c56baf6 | 417 | } |
418 | ||
1b39caaa | 419 | void |
420 | BodyPipe::scheduleBodyEndNotification() | |
421 | { | |
4299f876 | 422 | if (theConsumer.valid()) { // TODO: allow asyncCall() to check this instead |
26ac0430 AJ |
423 | if (bodySizeKnown() && bodySize() == thePutSize) { |
424 | AsyncCall::Pointer call = asyncCall(91, 7, | |
425 | "BodyConsumer::noteBodyProductionEnded", | |
426 | BodyConsumerDialer(theConsumer, | |
f53969cc | 427 | &BodyConsumer::noteBodyProductionEnded, this)); |
26ac0430 AJ |
428 | ScheduleCallHere(call); |
429 | } else { | |
430 | AsyncCall::Pointer call = asyncCall(91, 7, | |
431 | "BodyConsumer::noteBodyProducerAborted", | |
432 | BodyConsumerDialer(theConsumer, | |
f53969cc | 433 | &BodyConsumer::noteBodyProducerAborted, this)); |
26ac0430 AJ |
434 | ScheduleCallHere(call); |
435 | } | |
436 | } | |
1b39caaa | 437 | } |
438 | ||
1b39caaa | 439 | // a short temporary string describing buffer status for debugging |
440 | const char *BodyPipe::status() const | |
441 | { | |
350e2aec FC |
442 | static MemBuf outputBuffer; |
443 | outputBuffer.reset(); | |
1b39caaa | 444 | |
350e2aec | 445 | outputBuffer.append(" [", 2); |
1b39caaa | 446 | |
c91ca3ce | 447 | outputBuffer.Printf("%" PRIu64 "<=%" PRIu64, theGetSize, thePutSize); |
1b39caaa | 448 | if (theBodySize >= 0) |
c91ca3ce | 449 | outputBuffer.Printf("<=%" PRId64, theBodySize); |
26ac0430 | 450 | else |
350e2aec | 451 | outputBuffer.append("<=?", 3); |
1b39caaa | 452 | |
350e2aec | 453 | outputBuffer.Printf(" %d+%d", (int)theBuf.contentSize(), (int)theBuf.spaceSize()); |
1b39caaa | 454 | |
350e2aec | 455 | outputBuffer.Printf(" pipe%p", this); |
4299f876 AR |
456 | if (theProducer.set()) |
457 | outputBuffer.Printf(" prod%p", theProducer.get()); | |
458 | if (theConsumer.set()) | |
459 | outputBuffer.Printf(" cons%p", theConsumer.get()); | |
1b39caaa | 460 | |
26ac0430 | 461 | if (mustAutoConsume) |
350e2aec | 462 | outputBuffer.append(" A", 2); |
abe286b8 AR |
463 | if (abortedConsumption) |
464 | outputBuffer.append(" !C", 3); | |
26ac0430 | 465 | if (isCheckedOut) |
350e2aec | 466 | outputBuffer.append(" L", 2); // Locked |
1b39caaa | 467 | |
350e2aec | 468 | outputBuffer.append("]", 1); |
1b39caaa | 469 | |
350e2aec | 470 | outputBuffer.terminate(); |
1b39caaa | 471 | |
350e2aec | 472 | return outputBuffer.content(); |
1b39caaa | 473 | } |
474 | ||
1b39caaa | 475 | /* BodyPipeCheckout */ |
476 | ||
3be43416 | 477 | BodyPipeCheckout::BodyPipeCheckout(BodyPipe &aPipe): thePipe(aPipe), |
f53969cc SM |
478 | buf(aPipe.checkOut()), offset(aPipe.consumedSize()), |
479 | checkedOutSize(buf.contentSize()), checkedIn(false) | |
1b39caaa | 480 | { |
481 | } | |
482 | ||
483 | BodyPipeCheckout::~BodyPipeCheckout() | |
484 | { | |
87728a5b AR |
485 | if (!checkedIn) { |
486 | // Do not pipe.undoCheckOut(*this) because it asserts or throws | |
487 | // TODO: consider implementing the long-term solution discussed at | |
488 | // http://www.mail-archive.com/squid-dev@squid-cache.org/msg07910.html | |
489 | debugs(91,2, HERE << "Warning: cannot undo BodyPipeCheckout"); | |
3be43416 | 490 | thePipe.checkIn(*this); |
87728a5b | 491 | } |
1b39caaa | 492 | } |
493 | ||
494 | void | |
495 | BodyPipeCheckout::checkIn() | |
496 | { | |
26ac0430 | 497 | assert(!checkedIn); |
3be43416 | 498 | thePipe.checkIn(*this); |
26ac0430 | 499 | checkedIn = true; |
1b39caaa | 500 | } |
501 | ||
3be43416 | 502 | BodyPipeCheckout::BodyPipeCheckout(const BodyPipeCheckout &c): thePipe(c.thePipe), |
f53969cc SM |
503 | buf(c.buf), offset(c.offset), checkedOutSize(c.checkedOutSize), |
504 | checkedIn(c.checkedIn) | |
1b39caaa | 505 | { |
26ac0430 | 506 | assert(false); // prevent copying |
1b39caaa | 507 | } |
508 | ||
509 | BodyPipeCheckout & | |
510 | BodyPipeCheckout::operator =(const BodyPipeCheckout &) | |
511 | { | |
26ac0430 AJ |
512 | assert(false); // prevent assignment |
513 | return *this; | |
1b39caaa | 514 | } |
f53969cc | 515 |