]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ICAP/ICAPModXact.cc
If a consumer leaves without consuming anything, do not tell
[thirdparty/squid.git] / src / ICAP / ICAPModXact.cc
CommitLineData
774c051c 1/*
507d0a78 2 * DEBUG: section 93 ICAP (RFC 3507) Client
774c051c 3 */
4
5#include "squid.h"
6#include "comm.h"
5f8252d2 7#include "HttpMsg.h"
774c051c 8#include "HttpRequest.h"
9#include "HttpReply.h"
10#include "ICAPServiceRep.h"
5f8252d2 11#include "ICAPInitiator.h"
774c051c 12#include "ICAPModXact.h"
13#include "ICAPClient.h"
14#include "ChunkedCodingParser.h"
15#include "TextException.h"
a97e82a8 16#include "AuthUserRequest.h"
12b91c99 17#include "ICAPConfig.h"
985c86bc 18#include "SquidTime.h"
774c051c 19
20// flow and terminology:
21// HTTP| --> receive --> encode --> write --> |network
22// end | <-- send <-- parse <-- read <-- |end
23
774c051c 24// TODO: replace gotEncapsulated() with something faster; we call it often
25
26CBDATA_CLASS_INIT(ICAPModXact);
27
5f8252d2 28static const size_t TheBackupLimit = BodyPipe::MaxCapacity;
774c051c 29
12b91c99 30extern ICAPConfig TheICAPConfig;
31
774c051c 32
33ICAPModXact::State::State()
34{
35 memset(this, sizeof(*this), 0);
36}
37
5f8252d2 38ICAPModXact::ICAPModXact(ICAPInitiator *anInitiator, HttpMsg *virginHeader,
39 HttpRequest *virginCause, ICAPServiceRep::Pointer &aService):
40 ICAPXaction("ICAPModXact"),
41 initiator(cbdataReference(anInitiator)),
42 icapReply(NULL),
43 virginConsumed(0),
44 bodyParser(NULL)
774c051c 45{
5f8252d2 46 assert(virginHeader);
774c051c 47
774c051c 48 service(aService);
49
5f8252d2 50 virgin.setHeader(virginHeader); // sets virgin.body_pipe if needed
51 virgin.setCause(virginCause); // may be NULL
774c051c 52
5f8252d2 53 // adapted header and body are initialized when we parse them
774c051c 54
55 // writing and reading ends are handled by ICAPXaction
56
57 // encoding
58 // nothing to do because we are using temporary buffers
59
60 // parsing
61 icapReply = new HttpReply;
62 icapReply->protoPrefix = "ICAP/"; // TODO: make an IcapReply class?
63
5f8252d2 64 debugs(93,7, "ICAPModXact initialized." << status());
774c051c 65}
66
5f8252d2 67// initiator wants us to start
68void ICAPModXact::start()
774c051c 69{
5f8252d2 70 ICAPXaction_Enter(start);
774c051c 71
5f8252d2 72 ICAPXaction::start();
774c051c 73
74 estimateVirginBody(); // before virgin disappears!
75
76 // it is an ICAP violation to send request to a service w/o known OPTIONS
77
78 if (service().up())
79 startWriting();
80 else
81 waitForService();
82
83 // XXX: but this has to be here to catch other errors. Thus, if
84 // commConnectStart in startWriting fails, we may get here
85 //_after_ the object got destroyed. Somebody please fix commConnectStart!
5f8252d2 86 // XXX: Is the above comment still valid?
774c051c 87 ICAPXaction_Exit();
88}
89
90static
91void ICAPModXact_noteServiceReady(void *data, ICAPServiceRep::Pointer &)
92{
93 ICAPModXact *x = static_cast<ICAPModXact*>(data);
94 assert(x);
95 x->noteServiceReady();
96}
97
98void ICAPModXact::waitForService()
99{
100 Must(!state.serviceWaiting);
5f8252d2 101 debugs(93, 7, "ICAPModXact will wait for the ICAP service" << status());
774c051c 102 state.serviceWaiting = true;
103 service().callWhenReady(&ICAPModXact_noteServiceReady, this);
104}
105
106void ICAPModXact::noteServiceReady()
107{
108 ICAPXaction_Enter(noteServiceReady);
109
110 Must(state.serviceWaiting);
111 state.serviceWaiting = false;
c99de607 112
113 Must(service().up());
114
115 startWriting();
774c051c 116
117 ICAPXaction_Exit();
118}
119
120void ICAPModXact::startWriting()
121{
774c051c 122 state.writing = State::writingConnect;
123 openConnection();
124 // put nothing here as openConnection calls commConnectStart
125 // and that may call us back without waiting for the next select loop
126}
127
128// connection with the ICAP service established
129void ICAPModXact::handleCommConnected()
130{
131 Must(state.writing == State::writingConnect);
132
133 startReading(); // wait for early errors from the ICAP server
134
135 MemBuf requestBuf;
136 requestBuf.init();
137
138 makeRequestHeaders(requestBuf);
5f8252d2 139 debugs(93, 9, "ICAPModXact ICAP will write" << status() << ":\n" <<
774c051c 140 (requestBuf.terminate(), requestBuf.content()));
141
142 // write headers
143 state.writing = State::writingHeaders;
144 scheduleWrite(requestBuf);
145}
146
b107a5a5 147void ICAPModXact::handleCommWrote(size_t sz)
774c051c 148{
b107a5a5 149 debugs(93, 5, HERE << "Wrote " << sz << " bytes");
150
774c051c 151 if (state.writing == State::writingHeaders)
152 handleCommWroteHeaders();
153 else
154 handleCommWroteBody();
155}
156
157void ICAPModXact::handleCommWroteHeaders()
158{
159 Must(state.writing == State::writingHeaders);
160
5f8252d2 161 // determine next step
162 if (preview.enabled())
163 state.writing = preview.done() ? State::writingPaused : State::writingPreview;
164 else
165 if (virginBody.expected())
166 state.writing = State::writingPrime;
167 else {
c99de607 168 stopWriting(true);
5f8252d2 169 return;
774c051c 170 }
5f8252d2 171
172 writeMore();
774c051c 173}
174
175void ICAPModXact::writeMore()
176{
5f8252d2 177 debugs(93, 5, HERE << "checking whether to write more" << status());
178
774c051c 179 if (writer) // already writing something
180 return;
181
182 switch (state.writing) {
183
184 case State::writingInit: // waiting for service OPTIONS
185 Must(state.serviceWaiting);
186
187 case State::writingConnect: // waiting for the connection to establish
188
189 case State::writingHeaders: // waiting for the headers to be written
190
191 case State::writingPaused: // waiting for the ICAP server response
192
c99de607 193 case State::writingReallyDone: // nothing more to write
194 return;
195
196 case State::writingAlmostDone: // was waiting for the last write
197 stopWriting(false);
774c051c 198 return;
199
200 case State::writingPreview:
5f8252d2 201 writePreviewBody();
774c051c 202 return;
203
204 case State::writingPrime:
205 writePrimeBody();
206 return;
207
208 default:
209 throw TexcHere("ICAPModXact in bad writing state");
210 }
211}
212
5f8252d2 213void ICAPModXact::writePreviewBody()
774c051c 214{
5f8252d2 215 debugs(93, 8, HERE << "will write Preview body from " <<
216 virgin.body_pipe << status());
774c051c 217 Must(state.writing == State::writingPreview);
5f8252d2 218 Must(virgin.body_pipe != NULL);
774c051c 219
5f8252d2 220 const size_t sizeMax = (size_t)virgin.body_pipe->buf().contentSize();
221 const size_t size = XMIN(preview.debt(), sizeMax);
774c051c 222 writeSomeBody("preview body", size);
223
224 // change state once preview is written
225
226 if (preview.done()) {
5f8252d2 227 debugs(93, 7, "ICAPModXact wrote entire Preview body" << status());
774c051c 228
229 if (preview.ieof())
c99de607 230 stopWriting(true);
774c051c 231 else
232 state.writing = State::writingPaused;
233 }
234}
235
236void ICAPModXact::writePrimeBody()
237{
238 Must(state.writing == State::writingPrime);
5f8252d2 239 Must(virginBodyWriting.active());
774c051c 240
5f8252d2 241 const size_t size = (size_t)virgin.body_pipe->buf().contentSize();
774c051c 242 writeSomeBody("prime virgin body", size);
243
5f8252d2 244 if (virginBodyEndReached(virginBodyWriting)) {
245 debugs(93, 5, HERE << "wrote entire body");
c99de607 246 stopWriting(true);
b107a5a5 247 }
774c051c 248}
249
250void ICAPModXact::writeSomeBody(const char *label, size_t size)
251{
c99de607 252 Must(!writer && state.writing < state.writingAlmostDone);
5f8252d2 253 Must(virgin.body_pipe != NULL);
12f4b710 254 debugs(93, 8, HERE << "will write up to " << size << " bytes of " <<
774c051c 255 label);
256
257 MemBuf writeBuf; // TODO: suggest a min size based on size and lastChunk
258
259 writeBuf.init(); // note: we assume that last-chunk will fit
260
5f8252d2 261 const size_t writableSize = virginContentSize(virginBodyWriting);
c99de607 262 const size_t chunkSize = XMIN(writableSize, size);
774c051c 263
264 if (chunkSize) {
12f4b710 265 debugs(93, 7, HERE << "will write " << chunkSize <<
774c051c 266 "-byte chunk of " << label);
5f8252d2 267
268 openChunk(writeBuf, chunkSize, false);
269 writeBuf.append(virginContentData(virginBodyWriting), chunkSize);
270 closeChunk(writeBuf);
271
272 virginBodyWriting.progress(chunkSize);
273 virginConsume();
774c051c 274 } else {
c99de607 275 debugs(93, 7, "ICAPModXact has no writable " << label << " content");
774c051c 276 }
277
5f8252d2 278 const bool wroteEof = virginBodyEndReached(virginBodyWriting);
279 bool lastChunk = wroteEof;
280 if (state.writing == State::writingPreview) {
281 preview.wrote(chunkSize, wroteEof); // even if wrote nothing
282 lastChunk = lastChunk || preview.done();
283 }
774c051c 284
5f8252d2 285 if (lastChunk) {
12f4b710 286 debugs(93, 8, HERE << "will write last-chunk of " << label);
774c051c 287 addLastRequestChunk(writeBuf);
288 }
289
12f4b710 290 debugs(93, 7, HERE << "will write " << writeBuf.contentSize()
774c051c 291 << " raw bytes of " << label);
292
293 if (writeBuf.hasContent()) {
294 scheduleWrite(writeBuf); // comm will free the chunk
295 } else {
296 writeBuf.clean();
297 }
298}
299
774c051c 300void ICAPModXact::addLastRequestChunk(MemBuf &buf)
301{
c99de607 302 const bool ieof = state.writing == State::writingPreview && preview.ieof();
303 openChunk(buf, 0, ieof);
304 closeChunk(buf);
774c051c 305}
306
c99de607 307void ICAPModXact::openChunk(MemBuf &buf, size_t chunkSize, bool ieof)
774c051c 308{
c99de607 309 buf.Printf((ieof ? "%x; ieof\r\n" : "%x\r\n"), (int) chunkSize);
774c051c 310}
311
c99de607 312void ICAPModXact::closeChunk(MemBuf &buf)
774c051c 313{
774c051c 314 buf.append(ICAP::crlf, 2); // chunk-terminating CRLF
315}
316
5f8252d2 317// did the activity reached the end of the virgin body?
318bool ICAPModXact::virginBodyEndReached(const VirginBodyAct &act) const
319{
320 return
321 !act.active() || // did all (assuming it was originally planned)
322 !virgin.body_pipe->expectMoreAfter(act.offset()); // wont have more
323}
324
325// the size of buffered virgin body data available for the specified activity
326// if this size is zero, we may be done or may be waiting for more data
327size_t ICAPModXact::virginContentSize(const VirginBodyAct &act) const
774c051c 328{
5f8252d2 329 Must(act.active());
330 // asbolute start of unprocessed data
331 const size_t start = act.offset();
332 // absolute end of buffered data
333 const size_t end = virginConsumed + virgin.body_pipe->buf().contentSize();
774c051c 334 Must(virginConsumed <= start && start <= end);
335 return end - start;
336}
337
5f8252d2 338// pointer to buffered virgin body data available for the specified activity
339const char *ICAPModXact::virginContentData(const VirginBodyAct &act) const
774c051c 340{
5f8252d2 341 Must(act.active());
342 const size_t start = act.offset();
774c051c 343 Must(virginConsumed <= start);
5f8252d2 344 return virgin.body_pipe->buf().content() + (start-virginConsumed);
774c051c 345}
346
347void ICAPModXact::virginConsume()
348{
5f8252d2 349 if (!virgin.body_pipe)
350 return;
351
352 BodyPipe &bp = *virgin.body_pipe;
353 const size_t have = static_cast<size_t>(bp.buf().contentSize());
774c051c 354 const size_t end = virginConsumed + have;
355 size_t offset = end;
356
5f8252d2 357 if (virginBodyWriting.active())
358 offset = XMIN(virginBodyWriting.offset(), offset);
774c051c 359
5f8252d2 360 if (virginBodySending.active())
361 offset = XMIN(virginBodySending.offset(), offset);
774c051c 362
363 Must(virginConsumed <= offset && offset <= end);
364
365 if (const size_t size = offset - virginConsumed) {
b107a5a5 366 debugs(93, 8, HERE << "consuming " << size << " out of " << have <<
774c051c 367 " virgin body bytes");
5f8252d2 368 bp.consume(size);
774c051c 369 virginConsumed += size;
774c051c 370 }
371}
372
373void ICAPModXact::handleCommWroteBody()
374{
375 writeMore();
376}
377
c99de607 378// Called when we do not expect to call comm_write anymore.
379// We may have a pending write though.
380// If stopping nicely, we will just wait for that pending write, if any.
381void ICAPModXact::stopWriting(bool nicely)
774c051c 382{
c99de607 383 if (state.writing == State::writingReallyDone)
774c051c 384 return;
385
c99de607 386 if (writer) {
387 if (nicely) {
5f8252d2 388 debugs(93, 7, HERE << "will wait for the last write" << status());
c99de607 389 state.writing = State::writingAlmostDone; // may already be set
5f8252d2 390 checkConsuming();
c99de607 391 return;
392 }
5f8252d2 393 debugs(93, 2, HERE << "will NOT wait for the last write" << status());
774c051c 394
c99de607 395 // Comm does not have an interface to clear the writer callback nicely,
396 // but without clearing the writer we cannot recycle the connection.
397 // We prevent connection reuse and hope that we can handle a callback
5f8252d2 398 // call at any time, usually in the middle of the destruction sequence!
399 // Somebody should add comm_remove_write_handler() to comm API.
c99de607 400 reuseConnection = false;
401 }
402
5f8252d2 403 debugs(93, 7, HERE << "will no longer write" << status());
c99de607 404 state.writing = State::writingReallyDone;
774c051c 405
5f8252d2 406 if (virginBodyWriting.active()) {
407 virginBodyWriting.disable();
408 virginConsume();
409 }
774c051c 410}
411
412void ICAPModXact::stopBackup()
413{
5f8252d2 414 if (!virginBodySending.active())
774c051c 415 return;
416
5f8252d2 417 debugs(93, 7, "ICAPModXact will no longer backup" << status());
418 virginBodySending.disable();
774c051c 419 virginConsume();
420}
421
422bool ICAPModXact::doneAll() const
423{
424 return ICAPXaction::doneAll() && !state.serviceWaiting &&
5f8252d2 425 doneSending() &&
774c051c 426 doneReading() && state.doneWriting();
427}
428
429void ICAPModXact::startReading()
430{
431 Must(connection >= 0);
432 Must(!reader);
5f8252d2 433 Must(!adapted.header);
434 Must(!adapted.body_pipe);
774c051c 435
436 // we use the same buffer for headers and body and then consume headers
437 readMore();
438}
439
440void ICAPModXact::readMore()
441{
3b299123 442 if (reader || doneReading()) {
c99de607 443 debugs(93,3,HERE << "returning from readMore because reader or doneReading()");
774c051c 444 return;
3b299123 445 }
774c051c 446
447 // do not fill readBuf if we have no space to store the result
5f8252d2 448 if (adapted.body_pipe != NULL &&
449 !adapted.body_pipe->buf().hasPotentialSpace()) {
450 debugs(93,3,HERE << "not reading because ICAP reply pipe is full");
774c051c 451 return;
3b299123 452 }
774c051c 453
454 if (readBuf.hasSpace())
455 scheduleRead();
3b299123 456 else
c99de607 457 debugs(93,3,HERE << "nothing to do because !readBuf.hasSpace()");
774c051c 458}
459
460// comm module read a portion of the ICAP response for us
461void ICAPModXact::handleCommRead(size_t)
462{
463 Must(!state.doneParsing());
464 parseMore();
465 readMore();
466}
467
468void ICAPModXact::echoMore()
469{
470 Must(state.sending == State::sendingVirgin);
5f8252d2 471 Must(adapted.body_pipe != NULL);
472 Must(virginBodySending.active());
473
474 const size_t sizeMax = virginContentSize(virginBodySending);
475 debugs(93,5, HERE << "will echo up to " << sizeMax << " bytes from " <<
476 virgin.body_pipe->status());
477 debugs(93,5, HERE << "will echo up to " << sizeMax << " bytes to " <<
478 adapted.body_pipe->status());
479
480 if (sizeMax > 0) {
481 const size_t size = adapted.body_pipe->putMoreData(virginContentData(virginBodySending), sizeMax);
482 debugs(93,5, HERE << "echoed " << size << " out of " << sizeMax <<
774c051c 483 " bytes");
5f8252d2 484 virginBodySending.progress(size);
774c051c 485 virginConsume();
774c051c 486 }
487
5f8252d2 488 if (virginBodyEndReached(virginBodySending)) {
489 debugs(93, 5, "ICAPModXact echoed all" << status());
774c051c 490 stopSending(true);
491 } else {
5f8252d2 492 debugs(93, 5, "ICAPModXact has " <<
493 virgin.body_pipe->buf().contentSize() << " bytes " <<
494 "and expects more to echo" << status());
495 // TODO: timeout if virgin or adapted pipes are broken
774c051c 496 }
497}
498
499bool ICAPModXact::doneSending() const
500{
774c051c 501 return state.sending == State::sendingDone;
502}
503
504void ICAPModXact::stopSending(bool nicely)
505{
506 if (doneSending())
507 return;
508
509 if (state.sending != State::sendingUndecided) {
5f8252d2 510 debugs(93, 7, "ICAPModXact will no longer send" << status());
511 if (adapted.body_pipe != NULL) {
512 virginBodySending.disable();
513 // we may leave debts if we were echoing and the virgin
514 // body_pipe got exhausted before we echoed all planned bytes
515 const bool leftDebts = adapted.body_pipe->needsMoreData();
516 stopProducingFor(adapted.body_pipe, nicely && !leftDebts);
517 }
774c051c 518 } else {
5f8252d2 519 debugs(93, 7, "ICAPModXact will not start sending" << status());
520 Must(!adapted.body_pipe);
774c051c 521 }
522
523 state.sending = State::sendingDone;
5f8252d2 524 checkConsuming();
774c051c 525}
526
5f8252d2 527// should be called after certain state.writing or state.sending changes
528void ICAPModXact::checkConsuming()
774c051c 529{
5f8252d2 530 // quit if we already stopped or are still using the pipe
531 if (!virgin.body_pipe || !state.doneConsumingVirgin())
774c051c 532 return;
533
5f8252d2 534 debugs(93, 7, HERE << "will stop consuming" << status());
535 stopConsumingFrom(virgin.body_pipe);
774c051c 536}
537
538void ICAPModXact::parseMore()
539{
aa761e5f 540 debugs(93, 5, HERE << "have " << readBuf.contentSize() << " bytes to parse" <<
774c051c 541 status());
d5cfacfb 542 debugs(93, 5, HERE << "\n" << readBuf.content());
774c051c 543
544 if (state.parsingHeaders())
545 parseHeaders();
546
547 if (state.parsing == State::psBody)
548 parseBody();
549}
550
551// note that allocation for echoing is done in handle204NoContent()
552void ICAPModXact::maybeAllocateHttpMsg()
553{
5f8252d2 554 if (adapted.header) // already allocated
774c051c 555 return;
556
557 if (gotEncapsulated("res-hdr")) {
5f8252d2 558 adapted.setHeader(new HttpReply);
774c051c 559 } else if (gotEncapsulated("req-hdr")) {
5f8252d2 560 adapted.setHeader(new HttpRequest);
774c051c 561 } else
562 throw TexcHere("Neither res-hdr nor req-hdr in maybeAllocateHttpMsg()");
563}
564
565void ICAPModXact::parseHeaders()
566{
567 Must(state.parsingHeaders());
568
b107a5a5 569 if (state.parsing == State::psIcapHeader) {
570 debugs(93, 5, HERE << "parse ICAP headers");
774c051c 571 parseIcapHead();
b107a5a5 572 }
774c051c 573
b107a5a5 574 if (state.parsing == State::psHttpHeader) {
575 debugs(93, 5, HERE << "parse HTTP headers");
774c051c 576 parseHttpHead();
b107a5a5 577 }
774c051c 578
579 if (state.parsingHeaders()) { // need more data
580 Must(mayReadMore());
581 return;
582 }
583
5f8252d2 584 AsyncCall(93,5, initiator, ICAPInitiator::noteIcapHeadersAdapted);
585 cbdataReferenceDone(initiator);
774c051c 586
587 if (state.sending == State::sendingVirgin)
588 echoMore();
589}
590
591void ICAPModXact::parseIcapHead()
592{
593 Must(state.sending == State::sendingUndecided);
594
595 if (!parseHead(icapReply))
596 return;
597
fc764d26 598 if (httpHeaderHasConnDir(&icapReply->header, "close")) {
599 debugs(93, 5, HERE << "found connection close");
600 reuseConnection = false;
601 }
602
774c051c 603 switch (icapReply->sline.status) {
604
605 case 100:
606 handle100Continue();
607 break;
608
609 case 200:
b559db5d 610
611 if (!validate200Ok()) {
612 throw TexcHere("Invalid ICAP Response");
613 } else {
614 handle200Ok();
615 }
616
774c051c 617 break;
618
619 case 204:
620 handle204NoContent();
621 break;
622
623 default:
b559db5d 624 debugs(93, 5, HERE << "ICAP status " << icapReply->sline.status);
774c051c 625 handleUnknownScode();
626 break;
627 }
628
629 // handle100Continue() manages state.writing on its own.
630 // Non-100 status means the server needs no postPreview data from us.
631 if (state.writing == State::writingPaused)
c99de607 632 stopWriting(true);
774c051c 633
634 // TODO: Consider applying a Squid 2.5 patch to recognize 201 responses
635}
636
b559db5d 637bool ICAPModXact::validate200Ok()
638{
639 if (ICAP::methodRespmod == service().method) {
640 if (!gotEncapsulated("res-hdr"))
641 return false;
642
643 return true;
644 }
645
646 if (ICAP::methodReqmod == service().method) {
647 if (!gotEncapsulated("res-hdr") && !gotEncapsulated("req-hdr"))
648 return false;
649
650 return true;
651 }
652
653 return false;
654}
655
774c051c 656void ICAPModXact::handle100Continue()
657{
658 Must(state.writing == State::writingPaused);
5f8252d2 659 // server must not respond before the end of preview: we may send ieof
774c051c 660 Must(preview.enabled() && preview.done() && !preview.ieof());
774c051c 661
5f8252d2 662 // 100 "Continue" cancels our preview commitment, not 204s outside preview
663 if (!state.allowedPostview204)
774c051c 664 stopBackup();
665
c99de607 666 state.parsing = State::psIcapHeader; // eventually
667 icapReply->reset();
774c051c 668
669 state.writing = State::writingPrime;
670
671 writeMore();
672}
673
674void ICAPModXact::handle200Ok()
675{
676 state.parsing = State::psHttpHeader;
677 state.sending = State::sendingAdapted;
678 stopBackup();
5f8252d2 679 checkConsuming();
774c051c 680}
681
682void ICAPModXact::handle204NoContent()
683{
684 stopParsing();
774c051c 685
686 // We want to clone the HTTP message, but we do not want
5f8252d2 687 // to copy some non-HTTP state parts that HttpMsg kids carry in them.
774c051c 688 // Thus, we cannot use a smart pointer, copy constructor, or equivalent.
689 // Instead, we simply write the HTTP message and "clone" it by parsing.
690
5f8252d2 691 HttpMsg *oldHead = virgin.header;
774c051c 692 debugs(93, 7, "ICAPModXact cloning virgin message " << oldHead);
693
694 MemBuf httpBuf;
695
696 // write the virgin message into a memory buffer
697 httpBuf.init();
698 packHead(httpBuf, oldHead);
699
c99de607 700 // allocate the adapted message and copy metainfo
5f8252d2 701 Must(!adapted.header);
7514268e 702 HttpMsg *newHead = NULL;
c99de607 703 if (const HttpRequest *oldR = dynamic_cast<const HttpRequest*>(oldHead)) {
704 HttpRequest *newR = new HttpRequest;
5f8252d2 705 inheritVirginProperties(*newR, *oldR);
c99de607 706 newHead = newR;
707 } else
708 if (dynamic_cast<const HttpReply*>(oldHead))
709 newHead = new HttpReply;
774c051c 710 Must(newHead);
711
5f8252d2 712 adapted.setHeader(newHead);
7514268e 713
774c051c 714 // parse the buffer back
715 http_status error = HTTP_STATUS_NONE;
716
717 Must(newHead->parse(&httpBuf, true, &error));
718
719 Must(newHead->hdr_sz == httpBuf.contentSize()); // no leftovers
720
721 httpBuf.clean();
722
5f8252d2 723 debugs(93, 7, "ICAPModXact cloned virgin message " << oldHead << " to " <<
724 newHead);
725
726 // setup adapted body pipe if needed
727 if (oldHead->body_pipe != NULL) {
728 debugs(93, 7, HERE << "will echo virgin body from " <<
729 oldHead->body_pipe);
730 state.sending = State::sendingVirgin;
731 checkConsuming();
732 Must(virginBodySending.active());
733 // TODO: optimize: is it possible to just use the oldHead pipe and
734 // remove ICAP from the loop? This echoing is probably a common case!
735 makeAdaptedBodyPipe("echoed virgin response");
736 if (oldHead->body_pipe->bodySizeKnown())
737 adapted.body_pipe->setBodySize(oldHead->body_pipe->bodySize());
738 debugs(93, 7, HERE << "will echo virgin body to " <<
739 adapted.body_pipe);
740 } else {
741 debugs(93, 7, HERE << "no virgin body to echo");
742 stopSending(true);
743 }
774c051c 744}
745
746void ICAPModXact::handleUnknownScode()
747{
748 stopParsing();
749 stopBackup();
750 // TODO: mark connection as "bad"
751
752 // Terminate the transaction; we do not know how to handle this response.
753 throw TexcHere("Unsupported ICAP status code");
754}
755
756void ICAPModXact::parseHttpHead()
757{
758 if (gotEncapsulated("res-hdr") || gotEncapsulated("req-hdr")) {
759 maybeAllocateHttpMsg();
760
5f8252d2 761 if (!parseHead(adapted.header))
c99de607 762 return; // need more header data
5f8252d2 763
764 if (HttpRequest *newHead = dynamic_cast<HttpRequest*>(adapted.header)) {
765 const HttpRequest *oldR = dynamic_cast<const HttpRequest*>(virgin.header);
766 Must(oldR);
767 // TODO: the adapted request did not really originate from the
768 // client; give proxy admin an option to prevent copying of
769 // sensitive client information here. See the following thread:
770 // http://www.squid-cache.org/mail-archive/squid-dev/200703/0040.html
771 inheritVirginProperties(*newHead, *oldR);
772 }
774c051c 773 }
774
5f8252d2 775 decideOnParsingBody();
774c051c 776}
777
c99de607 778// parses both HTTP and ICAP headers
774c051c 779bool ICAPModXact::parseHead(HttpMsg *head)
780{
c99de607 781 Must(head);
def17b6a 782 debugs(93, 5, HERE << "have " << readBuf.contentSize() << " head bytes to parse" <<
774c051c 783 "; state: " << state.parsing);
784
785 http_status error = HTTP_STATUS_NONE;
786 const bool parsed = head->parse(&readBuf, commEof, &error);
787 Must(parsed || !error); // success or need more data
788
c99de607 789 if (!parsed) { // need more data
b107a5a5 790 debugs(93, 5, HERE << "parse failed, need more data, return false");
774c051c 791 head->reset();
792 return false;
793 }
794
b107a5a5 795 debugs(93, 5, HERE << "parse success, consume " << head->hdr_sz << " bytes, return true");
774c051c 796 readBuf.consume(head->hdr_sz);
797 return true;
798}
799
5f8252d2 800// TODO: Move this method to HttpRequest?
801void ICAPModXact::inheritVirginProperties(HttpRequest &newR, const HttpRequest &oldR) {
774c051c 802
5f8252d2 803 newR.client_addr = oldR.client_addr;
804 newR.client_port = oldR.client_port;
805
806 newR.my_addr = oldR.my_addr;
807 newR.my_port = oldR.my_port;
808
809 // This may be too conservative for the 204 No Content case
810 // may eventually need cloneNullAdaptationImmune() for that.
811 newR.flags = oldR.flags.cloneAdaptationImmune();
774c051c 812
5f8252d2 813 if (oldR.auth_user_request) {
814 newR.auth_user_request = oldR.auth_user_request;
815 newR.auth_user_request->lock();
816 }
817}
818
819void ICAPModXact::decideOnParsingBody() {
200ac359 820 if (gotEncapsulated("res-body") || gotEncapsulated("req-body")) {
5f8252d2 821 debugs(93, 5, HERE << "expecting a body");
822 state.parsing = State::psBody;
823 bodyParser = new ChunkedCodingParser;
824 makeAdaptedBodyPipe("adapted response from the ICAP server");
825 Must(state.sending == State::sendingAdapted);
774c051c 826 } else {
b559db5d 827 debugs(93, 5, HERE << "not expecting a body");
5f8252d2 828 stopParsing();
829 stopSending(true);
774c051c 830 }
774c051c 831}
832
5f8252d2 833void ICAPModXact::parseBody()
774c051c 834{
5f8252d2 835 Must(state.parsing == State::psBody);
836 Must(bodyParser);
774c051c 837
5f8252d2 838 debugs(93, 5, HERE << "have " << readBuf.contentSize() << " body bytes to parse");
774c051c 839
5f8252d2 840 // the parser will throw on errors
841 BodyPipeCheckout bpc(*adapted.body_pipe);
842 const bool parsed = bodyParser->parse(&readBuf, &bpc.buf);
843 bpc.checkIn();
774c051c 844
aa761e5f 845 debugs(93, 5, HERE << "have " << readBuf.contentSize() << " body bytes after " <<
774c051c 846 "parse; parsed all: " << parsed);
847
5f8252d2 848 if (parsed) {
849 stopParsing();
850 stopSending(true); // the parser succeeds only if all parsed data fits
851 return;
852 }
774c051c 853
c99de607 854 debugs(93,3,HERE << this << " needsMoreData = " << bodyParser->needsMoreData());
3b299123 855
856 if (bodyParser->needsMoreData()) {
c99de607 857 debugs(93,3,HERE << this);
774c051c 858 Must(mayReadMore());
3b299123 859 readMore();
860 }
774c051c 861
862 if (bodyParser->needsMoreSpace()) {
863 Must(!doneSending()); // can hope for more space
5f8252d2 864 Must(adapted.body_pipe->buf().contentSize() > 0); // paranoid
865 // TODO: there should be a timeout in case the sink is broken
866 // or cannot consume partial content (while we need more space)
774c051c 867 }
774c051c 868}
869
870void ICAPModXact::stopParsing()
871{
872 if (state.parsing == State::psDone)
873 return;
874
5f8252d2 875 debugs(93, 7, "ICAPModXact will no longer parse" << status());
774c051c 876
877 delete bodyParser;
878
879 bodyParser = NULL;
880
881 state.parsing = State::psDone;
882}
883
884// HTTP side added virgin body data
5f8252d2 885void ICAPModXact::noteMoreBodyDataAvailable(BodyPipe &)
774c051c 886{
5f8252d2 887 ICAPXaction_Enter(noteMoreBodyDataAvailable);
774c051c 888
774c051c 889 writeMore();
890
891 if (state.sending == State::sendingVirgin)
892 echoMore();
893
894 ICAPXaction_Exit();
895}
896
897// HTTP side sent us all virgin info
5f8252d2 898void ICAPModXact::noteBodyProductionEnded(BodyPipe &)
774c051c 899{
5f8252d2 900 ICAPXaction_Enter(noteBodyProductionEnded);
774c051c 901
5f8252d2 902 Must(virgin.body_pipe->productionEnded());
774c051c 903
904 // push writer and sender in case we were waiting for the last-chunk
905 writeMore();
906
907 if (state.sending == State::sendingVirgin)
908 echoMore();
909
910 ICAPXaction_Exit();
911}
912
5f8252d2 913// body producer aborted
914void ICAPModXact::noteBodyProducerAborted(BodyPipe &)
774c051c 915{
5f8252d2 916 ICAPXaction_Enter(noteBodyProducerAborted);
917
918 mustStop("virgin HTTP body producer aborted");
774c051c 919
5f8252d2 920 ICAPXaction_Exit();
921}
922
923// initiator aborted
924void ICAPModXact::noteInitiatorAborted()
925{
926 ICAPXaction_Enter(noteInitiatorAborted);
927
928 if (initiator) {
929 cbdataReferenceDone(initiator);
930 mustStop("initiator aborted");
931 }
774c051c 932
933 ICAPXaction_Exit();
934}
935
5f8252d2 936// adapted body consumer wants more adapted data and
937// possibly freed some buffer space
938void ICAPModXact::noteMoreBodySpaceAvailable(BodyPipe &)
774c051c 939{
5f8252d2 940 ICAPXaction_Enter(noteMoreBodySpaceAvailable);
774c051c 941
942 if (state.sending == State::sendingVirgin)
943 echoMore();
3b299123 944 else if (state.sending == State::sendingAdapted)
945 parseMore();
774c051c 946 else
3b299123 947 Must(state.sending == State::sendingUndecided);
774c051c 948
949 ICAPXaction_Exit();
950}
951
5f8252d2 952// adapted body consumer aborted
953void ICAPModXact::noteBodyConsumerAborted(BodyPipe &)
774c051c 954{
5f8252d2 955 ICAPXaction_Enter(noteBodyConsumerAborted);
774c051c 956
5f8252d2 957 mustStop("adapted body consumer aborted");
774c051c 958
959 ICAPXaction_Exit();
960}
961
962// internal cleanup
5f8252d2 963void ICAPModXact::swanSong()
774c051c 964{
5f8252d2 965 debugs(93, 5, HERE << "swan sings" << status());
966
967 if (initiator) {
bf8fe701 968 debugs(93, 2, HERE << "swan sings for " << stopReason << status());
5f8252d2 969 AsyncCall(93,5, initiator, ICAPInitiator::noteIcapHeadersAborted);
970 cbdataReferenceDone(initiator);
971 }
774c051c 972
c99de607 973 stopWriting(false);
774c051c 974 stopBackup();
975
976 if (icapReply) {
977 delete icapReply;
978 icapReply = NULL;
979 }
980
981 stopSending(false);
982
5f8252d2 983 ICAPXaction::swanSong();
774c051c 984}
985
986void ICAPModXact::makeRequestHeaders(MemBuf &buf)
987{
12b91c99 988 /*
989 * XXX These should use HttpHdr interfaces instead of Printfs
990 */
774c051c 991 const ICAPServiceRep &s = service();
992 buf.Printf("%s %s ICAP/1.0\r\n", s.methodStr(), s.uri.buf());
993 buf.Printf("Host: %s:%d\r\n", s.host.buf(), s.port);
12b91c99 994 buf.Printf("Date: %s\r\n", mkrfc1123(squid_curtime));
995
996 if (!TheICAPConfig.reuse_connections)
997 buf.Printf("Connection: close\r\n");
998
774c051c 999 buf.Printf("Encapsulated: ");
1000
1001 MemBuf httpBuf;
12b91c99 1002
774c051c 1003 httpBuf.init();
1004
1005 // build HTTP request header, if any
1006 ICAP::Method m = s.method;
1007
5f8252d2 1008 const HttpRequest *request = virgin.cause ?
1009 virgin.cause :
1010 dynamic_cast<const HttpRequest*>(virgin.header);
c99de607 1011
5f8252d2 1012 // to simplify, we could assume that request is always available
c99de607 1013
1014 String urlPath;
1015 if (request) {
1016 urlPath = request->urlpath;
1017 if (ICAP::methodRespmod == m)
1018 encapsulateHead(buf, "req-hdr", httpBuf, request);
1019 else
1020 if (ICAP::methodReqmod == m)
5f8252d2 1021 encapsulateHead(buf, "req-hdr", httpBuf, virgin.header);
c99de607 1022 }
774c051c 1023
1024 if (ICAP::methodRespmod == m)
5f8252d2 1025 if (const HttpMsg *prime = virgin.header)
774c051c 1026 encapsulateHead(buf, "res-hdr", httpBuf, prime);
1027
1028 if (!virginBody.expected())
1dd6edf2 1029 buf.Printf("null-body=%d", (int) httpBuf.contentSize());
774c051c 1030 else if (ICAP::methodReqmod == m)
1dd6edf2 1031 buf.Printf("req-body=%d", (int) httpBuf.contentSize());
774c051c 1032 else
1dd6edf2 1033 buf.Printf("res-body=%d", (int) httpBuf.contentSize());
774c051c 1034
1035 buf.append(ICAP::crlf, 2); // terminate Encapsulated line
1036
c99de607 1037 if (shouldPreview(urlPath)) {
774c051c 1038 buf.Printf("Preview: %d\r\n", (int)preview.ad());
5f8252d2 1039 if (virginBody.expected()) // there is a body to preview
1040 virginBodySending.plan();
1041 else
1042 finishNullOrEmptyBodyPreview(httpBuf);
774c051c 1043 }
1044
1045 if (shouldAllow204()) {
5f8252d2 1046 debugs(93,5, HERE << "will allow 204s outside of preview");
1047 state.allowedPostview204 = true;
774c051c 1048 buf.Printf("Allow: 204\r\n");
5f8252d2 1049 if (virginBody.expected()) // there is a body to echo
1050 virginBodySending.plan();
774c051c 1051 }
1052
c99de607 1053 if (TheICAPConfig.send_client_ip && request)
1054 if (request->client_addr.s_addr != any_addr.s_addr &&
1055 request->client_addr.s_addr != no_addr.s_addr)
12b91c99 1056 buf.Printf("X-Client-IP: %s\r\n", inet_ntoa(request->client_addr));
a97e82a8 1057
c99de607 1058 if (TheICAPConfig.send_client_username && request)
5f8252d2 1059 makeUsernameHeader(request, buf);
a97e82a8 1060
2dfede9e 1061 // fprintf(stderr, "%s\n", buf.content());
a97e82a8 1062
774c051c 1063 buf.append(ICAP::crlf, 2); // terminate ICAP header
1064
1065 // start ICAP request body with encapsulated HTTP headers
1066 buf.append(httpBuf.content(), httpBuf.contentSize());
1067
1068 httpBuf.clean();
1069}
1070
5f8252d2 1071void ICAPModXact::makeUsernameHeader(const HttpRequest *request, MemBuf &buf) {
1072 if (const auth_user_request_t *auth = request->auth_user_request) {
1073 if (char const *name = auth->username()) {
1074 const char *value = TheICAPConfig.client_username_encode ?
1075 base64_encode(name) : name;
1076 buf.Printf("%s: %s\r\n", TheICAPConfig.client_username_header,
1077 value);
1078 }
1079 }
1080}
1081
774c051c 1082void ICAPModXact::encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &httpBuf, const HttpMsg *head)
1083{
1084 // update ICAP header
7cab7e9f 1085 icapBuf.Printf("%s=%d, ", section, (int) httpBuf.contentSize());
774c051c 1086
1087 // pack HTTP head
1088 packHead(httpBuf, head);
1089}
1090
1091void ICAPModXact::packHead(MemBuf &httpBuf, const HttpMsg *head)
1092{
1093 Packer p;
1094 packerToMemInit(&p, &httpBuf);
1095 head->packInto(&p, true);
1096 packerClean(&p);
1097}
1098
1099// decides whether to offer a preview and calculates its size
c99de607 1100bool ICAPModXact::shouldPreview(const String &urlPath)
774c051c 1101{
7cdbbd47 1102 if (!TheICAPConfig.preview_enable) {
1103 debugs(93, 5, HERE << "preview disabled by squid.conf");
1104 return false;
1105 }
1106
5f8252d2 1107 size_t wantedSize;
1108
c99de607 1109 if (!service().wantsPreview(urlPath, wantedSize)) {
1110 debugs(93, 5, "ICAPModXact should not offer preview for " << urlPath);
774c051c 1111 return false;
1112 }
1113
1114 Must(wantedSize >= 0);
1115
1116 // cannot preview more than we can backup
1117 size_t ad = XMIN(wantedSize, TheBackupLimit);
1118
5f8252d2 1119 if (!virginBody.expected())
1120 ad = 0;
774c051c 1121 else
5f8252d2 1122 if (virginBody.knownSize())
1123 ad = XMIN(ad, virginBody.size()); // not more than we have
774c051c 1124
1125 debugs(93, 5, "ICAPModXact should offer " << ad << "-byte preview " <<
1126 "(service wanted " << wantedSize << ")");
1127
1128 preview.enable(ad);
5f8252d2 1129 Must(preview.enabled());
774c051c 1130
5f8252d2 1131 return true;
774c051c 1132}
1133
1134// decides whether to allow 204 responses
1135bool ICAPModXact::shouldAllow204()
1136{
1137 if (!service().allows204())
1138 return false;
1139
1140 if (!virginBody.expected())
1141 return true; // no body means no problems with supporting 204s.
1142
1143 // if there is a body, make sure we can backup it all
1144
1145 if (!virginBody.knownSize())
1146 return false;
1147
1148 // or should we have a different backup limit?
1149 // note that '<' allows for 0-termination of the "full" backup buffer
1150 return virginBody.size() < TheBackupLimit;
1151}
1152
5f8252d2 1153// Normally, the body-writing code handles preview body. It can deal with
1154// bodies of unexpected size, including those that turn out to be empty.
1155// However, that code assumes that the body was expected and body control
1156// structures were initialized. This is not the case when there is no body
1157// or the body is known to be empty, because the virgin message will lack a
1158// body_pipe. So we handle preview of null-body and zero-size bodies here.
1159void ICAPModXact::finishNullOrEmptyBodyPreview(MemBuf &buf)
1160{
1161 Must(!virginBodyWriting.active()); // one reason we handle it here
1162 Must(!virgin.body_pipe); // another reason we handle it here
1163 Must(!preview.ad());
1164
1165 // do not add last-chunk because our Encapsulated header says null-body
1166 // addLastRequestChunk(buf);
1167 preview.wrote(0, true);
1168
1169 Must(preview.done());
1170 Must(preview.ieof());
1171}
1172
774c051c 1173void ICAPModXact::fillPendingStatus(MemBuf &buf) const
1174{
c99de607 1175 ICAPXaction::fillPendingStatus(buf);
1176
774c051c 1177 if (state.serviceWaiting)
1178 buf.append("U", 1);
1179
5f8252d2 1180 if (virgin.body_pipe != NULL)
c99de607 1181 buf.append("R", 1);
1182
5f8252d2 1183 if (connection > 0 && !doneReading())
c99de607 1184 buf.append("r", 1);
1185
774c051c 1186 if (!state.doneWriting() && state.writing != State::writingInit)
1187 buf.Printf("w(%d)", state.writing);
1188
1189 if (preview.enabled()) {
1190 if (!preview.done())
1dd6edf2 1191 buf.Printf("P(%d)", (int) preview.debt());
774c051c 1192 }
1193
5f8252d2 1194 if (virginBodySending.active())
774c051c 1195 buf.append("B", 1);
1196
1197 if (!state.doneParsing() && state.parsing != State::psIcapHeader)
1198 buf.Printf("p(%d)", state.parsing);
1199
1200 if (!doneSending() && state.sending != State::sendingUndecided)
1201 buf.Printf("S(%d)", state.sending);
1202}
1203
1204void ICAPModXact::fillDoneStatus(MemBuf &buf) const
1205{
c99de607 1206 ICAPXaction::fillDoneStatus(buf);
1207
5f8252d2 1208 if (!virgin.body_pipe)
774c051c 1209 buf.append("R", 1);
1210
1211 if (state.doneWriting())
1212 buf.append("w", 1);
1213
1214 if (preview.enabled()) {
1215 if (preview.done())
1216 buf.Printf("P%s", preview.ieof() ? "(ieof)" : "");
1217 }
1218
1219 if (doneReading())
1220 buf.append("r", 1);
1221
1222 if (state.doneParsing())
1223 buf.append("p", 1);
1224
1225 if (doneSending())
1226 buf.append("S", 1);
1227}
1228
1229bool ICAPModXact::gotEncapsulated(const char *section) const
1230{
a9925b40 1231 return icapReply->header.getByNameListMember("Encapsulated",
1232 section, ',').size() > 0;
774c051c 1233}
1234
1235// calculate whether there is a virgin HTTP body and
1236// whether its expected size is known
5f8252d2 1237// TODO: rename because we do not just estimate
774c051c 1238void ICAPModXact::estimateVirginBody()
1239{
5f8252d2 1240 // note: lack of size info may disable previews and 204s
774c051c 1241
5f8252d2 1242 HttpMsg *msg = virgin.header;
1243 Must(msg);
774c051c 1244
1245 method_t method;
1246
5f8252d2 1247 if (virgin.cause)
1248 method = virgin.cause->method;
774c051c 1249 else
5f8252d2 1250 if (HttpRequest *req = dynamic_cast<HttpRequest*>(msg))
1251 method = req->method;
1252 else
1253 method = METHOD_NONE;
774c051c 1254
1255 ssize_t size;
5f8252d2 1256 // expectingBody returns true for zero-sized bodies, but we will not
1257 // get a pipe for that body, so we treat the message as bodyless
1258 if (method != METHOD_NONE && msg->expectingBody(method, size) && size) {
1259 debugs(93, 6, "ICAPModXact expects virgin body from " <<
1260 virgin.body_pipe << "; size: " << size);
1261
1262 virginBody.expect(size);
1263 virginBodyWriting.plan();
1264
1265 // sign up as a body consumer
1266 Must(msg->body_pipe != NULL);
1267 Must(msg->body_pipe == virgin.body_pipe);
1268 Must(virgin.body_pipe->setConsumerIfNotLate(this));
1269
1270 // make sure TheBackupLimit is in-sync with the buffer size
1271 Must(TheBackupLimit <= static_cast<size_t>(msg->body_pipe->buf().max_capacity));
774c051c 1272 } else {
1273 debugs(93, 6, "ICAPModXact does not expect virgin body");
5f8252d2 1274 Must(msg->body_pipe == NULL);
1275 checkConsuming();
774c051c 1276 }
1277}
1278
5f8252d2 1279void ICAPModXact::makeAdaptedBodyPipe(const char *what) {
1280 Must(!adapted.body_pipe);
1281 Must(!adapted.header->body_pipe);
1282 adapted.header->body_pipe = new BodyPipe(this);
1283 adapted.body_pipe = adapted.header->body_pipe;
1284 debugs(93, 7, HERE << "will supply " << what << " via " <<
1285 adapted.body_pipe << " pipe");
1286}
1287
774c051c 1288
1289// TODO: Move SizedEstimate, MemBufBackup, and ICAPPreview elsewhere
1290
1291SizedEstimate::SizedEstimate()
1292 : theData(dtUnexpected)
1293{}
1294
1295void SizedEstimate::expect(ssize_t aSize)
1296{
1297 theData = (aSize >= 0) ? aSize : (ssize_t)dtUnknown;
1298}
1299
1300bool SizedEstimate::expected() const
1301{
1302 return theData != dtUnexpected;
1303}
1304
1305bool SizedEstimate::knownSize() const
1306{
1307 Must(expected());
1308 return theData != dtUnknown;
1309}
1310
1311size_t SizedEstimate::size() const
1312{
1313 Must(knownSize());
1314 return static_cast<size_t>(theData);
1315}
1316
1317
1318
5f8252d2 1319VirginBodyAct::VirginBodyAct(): theStart(-1)
774c051c 1320{}
1321
5f8252d2 1322void VirginBodyAct::plan()
774c051c 1323{
1324 if (theStart < 0)
1325 theStart = 0;
774c051c 1326}
1327
5f8252d2 1328void VirginBodyAct::disable()
774c051c 1329{
5f8252d2 1330 theStart = -2;
774c051c 1331}
1332
5f8252d2 1333void VirginBodyAct::progress(size_t size)
774c051c 1334{
1335 Must(active());
1336 Must(size >= 0);
1337 theStart += static_cast<ssize_t>(size);
774c051c 1338}
1339
5f8252d2 1340size_t VirginBodyAct::offset() const
774c051c 1341{
1342 Must(active());
1343 return static_cast<size_t>(theStart);
1344}
1345
774c051c 1346
1347ICAPPreview::ICAPPreview(): theWritten(0), theAd(0), theState(stDisabled)
1348{}
1349
1350void ICAPPreview::enable(size_t anAd)
1351{
1352 // TODO: check for anAd not exceeding preview size limit
1353 Must(anAd >= 0);
1354 Must(!enabled());
1355 theAd = anAd;
1356 theState = stWriting;
1357}
1358
1359bool ICAPPreview::enabled() const
1360{
1361 return theState != stDisabled;
1362}
1363
1364size_t ICAPPreview::ad() const
1365{
1366 Must(enabled());
1367 return theAd;
1368}
1369
1370bool ICAPPreview::done() const
1371{
1372 Must(enabled());
1373 return theState >= stIeof;
1374}
1375
1376bool ICAPPreview::ieof() const
1377{
1378 Must(enabled());
1379 return theState == stIeof;
1380}
1381
1382size_t ICAPPreview::debt() const
1383{
1384 Must(enabled());
1385 return done() ? 0 : (theAd - theWritten);
1386}
1387
c99de607 1388void ICAPPreview::wrote(size_t size, bool wroteEof)
774c051c 1389{
1390 Must(enabled());
5f8252d2 1391
774c051c 1392 theWritten += size;
1393
5f8252d2 1394 Must(theWritten <= theAd);
1395
1396 if (wroteEof)
1397 theState = stIeof; // written size is irrelevant
1398 else
774c051c 1399 if (theWritten >= theAd)
5f8252d2 1400 theState = stDone;
774c051c 1401}
1402
3cfc19b3 1403bool ICAPModXact::fillVirginHttpHeader(MemBuf &mb) const
1404{
5f8252d2 1405 if (virgin.header == NULL)
3cfc19b3 1406 return false;
1407
5f8252d2 1408 virgin.header->firstLineBuf(mb);
3cfc19b3 1409
1410 return true;
1411}