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