ModXact.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
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  */
8 
9 /* DEBUG: section 93 ICAP (RFC 3507) Client */
10 
11 #include "squid.h"
12 #include "AccessLogEntry.h"
13 #include "adaptation/Answer.h"
14 #include "adaptation/History.h"
15 #include "adaptation/icap/Client.h"
16 #include "adaptation/icap/Config.h"
21 #include "adaptation/Initiator.h"
22 #include "auth/UserRequest.h"
23 #include "base/TextException.h"
24 #include "base64.h"
25 #include "comm.h"
26 #include "comm/Connection.h"
27 #include "error/Detail.h"
30 #include "HttpHeaderTools.h"
31 #include "HttpReply.h"
32 #include "MasterXaction.h"
33 #include "parser/Tokenizer.h"
34 #include "sbuf/Stream.h"
35 #include "SquidTime.h"
36 
37 // flow and terminology:
38 // HTTP| --> receive --> encode --> write --> |network
39 // end | <-- send <-- parse <-- read <-- |end
40 
41 // TODO: replace gotEncapsulated() with something faster; we call it often
42 
45 
46 static const size_t TheBackupLimit = BodyPipe::MaxCapacity;
47 
49 
51 {
52  memset(this, 0, sizeof(*this));
53 }
54 
57  AsyncJob("Adaptation::Icap::ModXact"),
58  Adaptation::Icap::Xaction("Adaptation::Icap::ModXact", aService),
59  virginConsumed(0),
61  canStartBypass(false), // too early
65  adaptHistoryId(-1),
66  trailerParser(nullptr),
67  alMaster(alp)
68 {
69  assert(virginHeader);
70 
71  virgin.setHeader(virginHeader); // sets virgin.body_pipe if needed
72  virgin.setCause(virginCause); // may be NULL
73 
74  // adapted header and body are initialized when we parse them
75 
76  // writing and reading ends are handled by Adaptation::Icap::Xaction
77 
78  // encoding
79  // nothing to do because we are using temporary buffers
80 
81  // parsing; TODO: do not set until we parse, see ICAPOptXact
82  icapReply = new HttpReply;
83  icapReply->protoPrefix = "ICAP/"; // TODO: make an IcapReply class?
84 
85  debugs(93,7, HERE << "initialized." << status());
86 }
87 
88 // initiator wants us to start
90 {
92 
93  // reserve an adaptation history slot (attempts are known at this time)
94  Adaptation::History::Pointer ah = virginRequest().adaptLogHistory();
95  if (ah != NULL)
96  adaptHistoryId = ah->recordXactStart(service().cfg().key, icap_tr_start, attempts > 1);
97 
98  estimateVirginBody(); // before virgin disappears!
99 
100  canStartBypass = service().cfg().bypass;
101 
102  // it is an ICAP violation to send request to a service w/o known OPTIONS
103  // and the service may is too busy for us: honor Max-Connections and such
104  if (service().up() && service().availableForNew())
105  startWriting();
106  else
107  waitForService();
108 }
109 
111 {
112  const char *comment;
113  Must(!state.serviceWaiting);
114 
115  if (!service().up()) {
116  AsyncCall::Pointer call = JobCallback(93,5,
118 
119  service().callWhenReady(call);
120  comment = "to be up";
121  } else {
122  //The service is unavailable because of max-connection or other reason
123 
124  if (service().cfg().onOverload != srvWait) {
125  // The service is overloaded, but waiting to be available prohibited by
126  // user configuration (onOverload is set to "block" or "bypass")
127  if (service().cfg().onOverload == srvBlock)
128  disableBypass("not available", true);
129  else //if (service().cfg().onOverload == srvBypass)
130  canStartBypass = true;
131 
132  disableRetries();
133  disableRepeats("ICAP service is not available");
134 
135  debugs(93, 7, HERE << "will not wait for the service to be available" <<
136  status());
137 
138  throw TexcHere("ICAP service is not available");
139  }
140 
141  AsyncCall::Pointer call = JobCallback(93,5,
143  service().callWhenAvailable(call, state.waitedForService);
144  comment = "to be available";
145  }
146 
147  debugs(93, 7, HERE << "will wait for the service " << comment << status());
148  state.serviceWaiting = true; // after callWhenReady() which may throw
149  state.waitedForService = true;
150 }
151 
153 {
154  Must(state.serviceWaiting);
155  state.serviceWaiting = false;
156 
157  if (!service().up()) {
158  disableRetries();
159  disableRepeats("ICAP service is unusable");
160  throw TexcHere("ICAP service is unusable");
161  }
162 
163  if (service().availableForOld())
164  startWriting();
165  else
166  waitForService();
167 }
168 
170 {
171  Must(state.serviceWaiting);
172  state.serviceWaiting = false;
173 
174  if (service().up() && service().availableForOld())
175  startWriting();
176  else
177  waitForService();
178 }
179 
181 {
182  state.writing = State::writingConnect;
183 
184  decideOnPreview(); // must be decided before we decideOnRetries
185  decideOnRetries();
186 
187  openConnection();
188 }
189 
191 {
192  Must(state.writing == State::writingConnect);
193 
194  startReading(); // wait for early errors from the ICAP server
195 
196  MemBuf requestBuf;
197  requestBuf.init();
198 
199  makeRequestHeaders(requestBuf);
200  debugs(93, 9, HERE << "will write" << status() << ":\n" <<
201  (requestBuf.terminate(), requestBuf.content()));
202 
203  // write headers
204  state.writing = State::writingHeaders;
205  icap_tio_start = current_time;
206  scheduleWrite(requestBuf);
207 }
208 
210 {
211  debugs(93, 5, HERE << "Wrote " << sz << " bytes");
212 
213  if (state.writing == State::writingHeaders)
214  handleCommWroteHeaders();
215  else
216  handleCommWroteBody();
217 }
218 
220 {
221  Must(state.writing == State::writingHeaders);
222 
223  // determine next step
224  if (preview.enabled()) {
225  if (preview.done())
226  decideWritingAfterPreview("zero-size");
227  else
228  state.writing = State::writingPreview;
229  } else if (virginBody.expected()) {
230  state.writing = State::writingPrime;
231  } else {
232  stopWriting(true);
233  return;
234  }
235 
236  writeMore();
237 }
238 
240 {
241  debugs(93, 5, HERE << "checking whether to write more" << status());
242 
243  if (writer != NULL) // already writing something
244  return;
245 
246  switch (state.writing) {
247 
248  case State::writingInit: // waiting for service OPTIONS
249  Must(state.serviceWaiting);
250  return;
251 
252  case State::writingConnect: // waiting for the connection to establish
253  case State::writingHeaders: // waiting for the headers to be written
254  case State::writingPaused: // waiting for the ICAP server response
255  case State::writingReallyDone: // nothing more to write
256  return;
257 
258  case State::writingAlmostDone: // was waiting for the last write
259  stopWriting(false);
260  return;
261 
262  case State::writingPreview:
263  writePreviewBody();
264  return;
265 
266  case State::writingPrime:
267  writePrimeBody();
268  return;
269 
270  default:
271  throw TexcHere("Adaptation::Icap::ModXact in bad writing state");
272  }
273 }
274 
276 {
277  debugs(93, 8, HERE << "will write Preview body from " <<
278  virgin.body_pipe << status());
279  Must(state.writing == State::writingPreview);
280  Must(virgin.body_pipe != NULL);
281 
282  const size_t sizeMax = (size_t)virgin.body_pipe->buf().contentSize();
283  const size_t size = min(preview.debt(), sizeMax);
284  writeSomeBody("preview body", size);
285 
286  // change state once preview is written
287 
288  if (preview.done())
289  decideWritingAfterPreview("body");
290 }
291 
294 {
295  if (preview.ieof()) // nothing more to write
296  stopWriting(true);
297  else if (state.parsing == State::psIcapHeader) // did not get a reply yet
298  state.writing = State::writingPaused; // wait for the ICAP server reply
299  else
300  stopWriting(true); // ICAP server reply implies no post-preview writing
301 
302  debugs(93, 6, HERE << "decided on writing after " << kind << " preview" <<
303  status());
304 }
305 
307 {
308  Must(state.writing == State::writingPrime);
309  Must(virginBodyWriting.active());
310 
311  const size_t size = (size_t)virgin.body_pipe->buf().contentSize();
312  writeSomeBody("prime virgin body", size);
313 
314  if (virginBodyEndReached(virginBodyWriting)) {
315  debugs(93, 5, HERE << "wrote entire body");
316  stopWriting(true);
317  }
318 }
319 
320 void Adaptation::Icap::ModXact::writeSomeBody(const char *label, size_t size)
321 {
322  Must(!writer && state.writing < state.writingAlmostDone);
323  Must(virgin.body_pipe != NULL);
324  debugs(93, 8, HERE << "will write up to " << size << " bytes of " <<
325  label);
326 
327  MemBuf writeBuf; // TODO: suggest a min size based on size and lastChunk
328 
329  writeBuf.init(); // note: we assume that last-chunk will fit
330 
331  const size_t writableSize = virginContentSize(virginBodyWriting);
332  const size_t chunkSize = min(writableSize, size);
333 
334  if (chunkSize) {
335  debugs(93, 7, HERE << "will write " << chunkSize <<
336  "-byte chunk of " << label);
337 
338  openChunk(writeBuf, chunkSize, false);
339  writeBuf.append(virginContentData(virginBodyWriting), chunkSize);
340  closeChunk(writeBuf);
341 
342  virginBodyWriting.progress(chunkSize);
343  virginConsume();
344  } else {
345  debugs(93, 7, HERE << "has no writable " << label << " content");
346  }
347 
348  const bool wroteEof = virginBodyEndReached(virginBodyWriting);
349  bool lastChunk = wroteEof;
350  if (state.writing == State::writingPreview) {
351  preview.wrote(chunkSize, wroteEof); // even if wrote nothing
352  lastChunk = lastChunk || preview.done();
353  }
354 
355  if (lastChunk) {
356  debugs(93, 8, HERE << "will write last-chunk of " << label);
357  addLastRequestChunk(writeBuf);
358  }
359 
360  debugs(93, 7, HERE << "will write " << writeBuf.contentSize()
361  << " raw bytes of " << label);
362 
363  if (writeBuf.hasContent()) {
364  scheduleWrite(writeBuf); // comm will free the chunk
365  } else {
366  writeBuf.clean();
367  }
368 }
369 
371 {
372  const bool ieof = state.writing == State::writingPreview && preview.ieof();
373  openChunk(buf, 0, ieof);
374  closeChunk(buf);
375 }
376 
377 void Adaptation::Icap::ModXact::openChunk(MemBuf &buf, size_t chunkSize, bool ieof)
378 {
379  buf.appendf((ieof ? "%x; ieof\r\n" : "%x\r\n"), (int) chunkSize);
380 }
381 
383 {
384  buf.append(ICAP::crlf, 2); // chunk-terminating CRLF
385 }
386 
388 {
389  const HttpRequest *request = virgin.cause ?
390  virgin.cause : dynamic_cast<const HttpRequest*>(virgin.header);
391  Must(request);
392  return *request;
393 }
394 
395 // did the activity reached the end of the virgin body?
397 {
398  return
399  !act.active() || // did all (assuming it was originally planned)
400  !virgin.body_pipe->expectMoreAfter(act.offset()); // will not have more
401 }
402 
403 // the size of buffered virgin body data available for the specified activity
404 // if this size is zero, we may be done or may be waiting for more data
406 {
407  Must(act.active());
408  // asbolute start of unprocessed data
409  const uint64_t dataStart = act.offset();
410  // absolute end of buffered data
411  const uint64_t dataEnd = virginConsumed + virgin.body_pipe->buf().contentSize();
412  Must(virginConsumed <= dataStart && dataStart <= dataEnd);
413  return static_cast<size_t>(dataEnd - dataStart);
414 }
415 
416 // pointer to buffered virgin body data available for the specified activity
418 {
419  Must(act.active());
420  const uint64_t dataStart = act.offset();
421  Must(virginConsumed <= dataStart);
422  return virgin.body_pipe->buf().content() + static_cast<size_t>(dataStart-virginConsumed);
423 }
424 
426 {
427  debugs(93, 9, HERE << "consumption guards: " << !virgin.body_pipe << isRetriable <<
428  isRepeatable << canStartBypass << protectGroupBypass);
429 
430  if (!virgin.body_pipe)
431  return; // nothing to consume
432 
433  if (isRetriable)
434  return; // do not consume if we may have to retry later
435 
436  BodyPipe &bp = *virgin.body_pipe;
437  const bool wantToPostpone = isRepeatable || canStartBypass || protectGroupBypass;
438 
439  // Why > 2? HttpState does not use the last bytes in the buffer
440  // because delayAwareRead() is arguably broken. See
441  // HttpStateData::maybeReadVirginBody for more details.
442  if (wantToPostpone && bp.buf().spaceSize() > 2) {
443  // Postponing may increase memory footprint and slow the HTTP side
444  // down. Not postponing may increase the number of ICAP errors
445  // if the ICAP service fails. We may also use "potential" space to
446  // postpone more aggressively. Should the trade-off be configurable?
447  debugs(93, 8, HERE << "postponing consumption from " << bp.status());
448  return;
449  }
450 
451  const size_t have = static_cast<size_t>(bp.buf().contentSize());
452  const uint64_t end = virginConsumed + have;
453  uint64_t offset = end;
454 
455  debugs(93, 9, HERE << "max virgin consumption offset=" << offset <<
456  " acts " << virginBodyWriting.active() << virginBodySending.active() <<
457  " consumed=" << virginConsumed <<
458  " from " << virgin.body_pipe->status());
459 
460  if (virginBodyWriting.active())
461  offset = min(virginBodyWriting.offset(), offset);
462 
463  if (virginBodySending.active())
464  offset = min(virginBodySending.offset(), offset);
465 
466  Must(virginConsumed <= offset && offset <= end);
467 
468  if (const size_t size = static_cast<size_t>(offset - virginConsumed)) {
469  debugs(93, 8, HERE << "consuming " << size << " out of " << have <<
470  " virgin body bytes");
471  bp.consume(size);
472  virginConsumed += size;
473  Must(!isRetriable); // or we should not be consuming
474  disableRepeats("consumed content");
475  disableBypass("consumed content", true);
476  }
477 }
478 
480 {
481  writeMore();
482 }
483 
484 // Called when we do not expect to call comm_write anymore.
485 // We may have a pending write though.
486 // If stopping nicely, we will just wait for that pending write, if any.
488 {
489  if (state.writing == State::writingReallyDone)
490  return;
491 
492  if (writer != NULL) {
493  if (nicely) {
494  debugs(93, 7, HERE << "will wait for the last write" << status());
495  state.writing = State::writingAlmostDone; // may already be set
496  checkConsuming();
497  return;
498  }
499  debugs(93, 3, HERE << "will NOT wait for the last write" << status());
500 
501  // Comm does not have an interface to clear the writer callback nicely,
502  // but without clearing the writer we cannot recycle the connection.
503  // We prevent connection reuse and hope that we can handle a callback
504  // call at any time, usually in the middle of the destruction sequence!
505  // Somebody should add comm_remove_write_handler() to comm API.
506  reuseConnection = false;
507  ignoreLastWrite = true;
508  }
509 
510  debugs(93, 7, HERE << "will no longer write" << status());
511  if (virginBodyWriting.active()) {
512  virginBodyWriting.disable();
513  virginConsume();
514  }
515  state.writing = State::writingReallyDone;
516  checkConsuming();
517 }
518 
520 {
521  if (!virginBodySending.active())
522  return;
523 
524  debugs(93, 7, HERE << "will no longer backup" << status());
525  virginBodySending.disable();
526  virginConsume();
527 }
528 
530 {
531  return Adaptation::Icap::Xaction::doneAll() && !state.serviceWaiting &&
532  doneSending() &&
533  doneReading() && state.doneWriting();
534 }
535 
537 {
538  Must(haveConnection());
539  Must(!reader);
540  Must(!adapted.header);
541  Must(!adapted.body_pipe);
542 
543  // we use the same buffer for headers and body and then consume headers
544  readMore();
545 }
546 
548 {
549  if (reader != NULL || doneReading()) {
550  debugs(93,3,HERE << "returning from readMore because reader or doneReading()");
551  return;
552  }
553 
554  // do not fill readBuf if we have no space to store the result
555  if (adapted.body_pipe != NULL &&
556  !adapted.body_pipe->buf().hasPotentialSpace()) {
557  debugs(93,3,HERE << "not reading because ICAP reply pipe is full");
558  return;
559  }
560 
561  if (readBuf.length() < SQUID_TCP_SO_RCVBUF)
562  scheduleRead();
563  else
564  debugs(93,3,HERE << "cannot read with a full buffer");
565 }
566 
567 // comm module read a portion of the ICAP response for us
569 {
570  Must(!state.doneParsing());
571  icap_tio_finish = current_time;
572  parseMore();
573  readMore();
574 }
575 
577 {
578  Must(state.sending == State::sendingVirgin);
579  Must(adapted.body_pipe != NULL);
580  Must(virginBodySending.active());
581 
582  const size_t sizeMax = virginContentSize(virginBodySending);
583  debugs(93,5, HERE << "will echo up to " << sizeMax << " bytes from " <<
584  virgin.body_pipe->status());
585  debugs(93,5, HERE << "will echo up to " << sizeMax << " bytes to " <<
586  adapted.body_pipe->status());
587 
588  if (sizeMax > 0) {
589  const size_t size = adapted.body_pipe->putMoreData(virginContentData(virginBodySending), sizeMax);
590  debugs(93,5, HERE << "echoed " << size << " out of " << sizeMax <<
591  " bytes");
592  virginBodySending.progress(size);
593  disableRepeats("echoed content");
594  disableBypass("echoed content", true);
595  virginConsume();
596  }
597 
598  if (virginBodyEndReached(virginBodySending)) {
599  debugs(93, 5, HERE << "echoed all" << status());
600  stopSending(true);
601  } else {
602  debugs(93, 5, HERE << "has " <<
603  virgin.body_pipe->buf().contentSize() << " bytes " <<
604  "and expects more to echo" << status());
605  // TODO: timeout if virgin or adapted pipes are broken
606  }
607 }
608 
610 {
611  return state.sending == State::sendingDone;
612 }
613 
614 // stop (or do not start) sending adapted message body
616 {
617  debugs(93, 7, HERE << "Enter stop sending ");
618  if (doneSending())
619  return;
620  debugs(93, 7, HERE << "Proceed with stop sending ");
621 
622  if (state.sending != State::sendingUndecided) {
623  debugs(93, 7, HERE << "will no longer send" << status());
624  if (adapted.body_pipe != NULL) {
625  virginBodySending.disable();
626  // we may leave debts if we were echoing and the virgin
627  // body_pipe got exhausted before we echoed all planned bytes
628  const bool leftDebts = adapted.body_pipe->needsMoreData();
629  stopProducingFor(adapted.body_pipe, nicely && !leftDebts);
630  }
631  } else {
632  debugs(93, 7, HERE << "will not start sending" << status());
633  Must(!adapted.body_pipe);
634  }
635 
636  state.sending = State::sendingDone;
637  checkConsuming();
638 }
639 
640 // should be called after certain state.writing or state.sending changes
642 {
643  // quit if we already stopped or are still using the pipe
644  if (!virgin.body_pipe || !state.doneConsumingVirgin())
645  return;
646 
647  debugs(93, 7, HERE << "will stop consuming" << status());
648  stopConsumingFrom(virgin.body_pipe);
649 }
650 
652 {
653  debugs(93, 5, "have " << readBuf.length() << " bytes to parse" << status());
654  debugs(93, 5, "\n" << readBuf);
655 
656  if (state.parsingHeaders())
657  parseHeaders();
658 
659  if (state.parsing == State::psBody)
660  parseBody();
661 
662  if (state.parsing == State::psIcapTrailer)
663  parseIcapTrailer();
664 }
665 
666 void Adaptation::Icap::ModXact::callException(const std::exception &e)
667 {
668  if (!canStartBypass || isRetriable) {
669  if (!isRetriable) {
670  if (const TextException *te = dynamic_cast<const TextException *>(&e))
671  detailError(new ExceptionErrorDetail(te->id()));
672  else
673  detailError(new ExceptionErrorDetail(Here().id()));
674  }
676  return;
677  }
678 
679  try {
680  debugs(93, 3, HERE << "bypassing " << inCall << " exception: " <<
681  e.what() << ' ' << status());
682  bypassFailure();
683  } catch (const TextException &bypassTe) {
684  detailError(new ExceptionErrorDetail(bypassTe.id()));
686  } catch (const std::exception &bypassE) {
687  detailError(new ExceptionErrorDetail(Here().id()));
689  }
690 }
691 
693 {
694  disableBypass("already started to bypass", false);
695 
696  Must(!isRetriable); // or we should not be bypassing
697  // TODO: should the same be enforced for isRepeatable? Check icap_repeat??
698 
699  prepEchoing();
700 
701  startSending();
702 
703  // end all activities associated with the ICAP server
704 
705  stopParsing(false);
706 
707  stopWriting(true); // or should we force it?
708  if (haveConnection()) {
709  reuseConnection = false; // be conservative
710  cancelRead(); // may not work; and we cannot stop connecting either
711  if (!doneWithIo())
712  debugs(93, 7, HERE << "Warning: bypass failed to stop I/O" << status());
713  }
714 
715  service().noteFailure(); // we are bypassing, but this is still a failure
716 }
717 
718 void Adaptation::Icap::ModXact::disableBypass(const char *reason, bool includingGroupBypass)
719 {
720  if (canStartBypass) {
721  debugs(93,7, HERE << "will never start bypass because " << reason);
722  canStartBypass = false;
723  }
724  if (protectGroupBypass && includingGroupBypass) {
725  debugs(93,7, HERE << "not protecting group bypass because " << reason);
726  protectGroupBypass = false;
727  }
728 }
729 
730 // note that allocation for echoing is done in handle204NoContent()
732 {
733  if (adapted.header) // already allocated
734  return;
735 
736  if (gotEncapsulated("res-hdr")) {
737  adapted.setHeader(new HttpReply);
738  setOutcome(service().cfg().method == ICAP::methodReqmod ?
740  } else if (gotEncapsulated("req-hdr")) {
741  adapted.setHeader(new HttpRequest(virginRequest().masterXaction));
742  setOutcome(xoModified);
743  } else
744  throw TexcHere("Neither res-hdr nor req-hdr in maybeAllocateHttpMsg()");
745 }
746 
748 {
749  Must(state.parsingHeaders());
750 
751  if (state.parsing == State::psIcapHeader) {
752  debugs(93, 5, HERE << "parse ICAP headers");
753  parseIcapHead();
754  }
755 
756  if (state.parsing == State::psHttpHeader) {
757  debugs(93, 5, HERE << "parse HTTP headers");
758  parseHttpHead();
759  }
760 
761  if (state.parsingHeaders()) { // need more data
762  Must(mayReadMore());
763  return;
764  }
765 
766  startSending();
767 }
768 
769 // called after parsing all headers or when bypassing an exception
771 {
772  disableRepeats("sent headers");
773  disableBypass("sent headers", true);
774  sendAnswer(Answer::Forward(adapted.header));
775 
776  if (state.sending == State::sendingVirgin)
777  echoMore();
778  else {
779  // If we are not using the virgin HTTP object update the
780  // Http::Message::sources flag.
781  // The state.sending may set to State::sendingVirgin in the case
782  // of 206 responses too, where we do not want to update Http::Message::sources
783  // flag. However even for 206 responses the state.sending is
784  // not set yet to sendingVirgin. This is done in later step
785  // after the parseBody method called.
786  updateSources();
787  }
788 }
789 
791 {
792  Must(state.sending == State::sendingUndecided);
793 
794  if (!parseHead(icapReply.getRaw()))
795  return;
796 
797  if (expectIcapTrailers()) {
798  Must(!trailerParser);
799  trailerParser = new TrailerParser;
800  }
801 
802  static SBuf close("close", 5);
803  if (httpHeaderHasConnDir(&icapReply->header, close)) {
804  debugs(93, 5, HERE << "found connection close");
805  reuseConnection = false;
806  }
807 
808  switch (icapReply->sline.status()) {
809 
810  case Http::scContinue:
811  handle100Continue();
812  break;
813 
814  case Http::scOkay:
815  case Http::scCreated: // Symantec Scan Engine 5.0 and later when modifying HTTP msg
816 
817  if (!validate200Ok()) {
818  throw TexcHere("Invalid ICAP Response");
819  } else {
820  handle200Ok();
821  }
822 
823  break;
824 
825  case Http::scNoContent:
826  handle204NoContent();
827  break;
828 
830  handle206PartialContent();
831  break;
832 
833  default:
834  debugs(93, 5, "ICAP status " << icapReply->sline.status());
835  handleUnknownScode();
836  break;
837  }
838 
839  const HttpRequest *request = dynamic_cast<HttpRequest*>(adapted.header);
840  if (!request)
841  request = &virginRequest();
842 
843  // update the cross-transactional database if needed (all status codes!)
844  if (const char *xxName = Adaptation::Config::masterx_shared_name) {
845  Adaptation::History::Pointer ah = request->adaptHistory(true);
846  if (ah != NULL) { // TODO: reorder checks to avoid creating history
847  const String val = icapReply->header.getByName(xxName);
848  if (val.size() > 0) // XXX: HttpHeader lacks empty value detection
849  ah->updateXxRecord(xxName, val);
850  }
851  }
852 
853  // update the adaptation plan if needed (all status codes!)
854  if (service().cfg().routing) {
855  String services;
856  if (icapReply->header.getList(Http::HdrType::X_NEXT_SERVICES, &services)) {
857  Adaptation::History::Pointer ah = request->adaptHistory(true);
858  if (ah != NULL)
859  ah->updateNextServices(services);
860  }
861  } // TODO: else warn (occasionally!) if we got Http::HdrType::X_NEXT_SERVICES
862 
863  // We need to store received ICAP headers for <icapLastHeader logformat option.
864  // If we already have stored headers from previous ICAP transaction related to this
865  // request, old headers will be replaced with the new one.
866 
867  Adaptation::History::Pointer ah = request->adaptLogHistory();
868  if (ah != NULL)
869  ah->recordMeta(&icapReply->header);
870 
871  // handle100Continue() manages state.writing on its own.
872  // Non-100 status means the server needs no postPreview data from us.
873  if (state.writing == State::writingPaused)
874  stopWriting(true);
875 }
876 
880 
881  if (parsePart(trailerParser, "trailer")) {
882  for (const auto &e: trailerParser->trailer.entries)
883  debugs(93, 5, "ICAP trailer: " << e->name << ": " << e->value);
884  stopParsing();
885  }
886 }
887 
889 {
890  if (service().cfg().method == ICAP::methodRespmod)
891  return gotEncapsulated("res-hdr");
892 
893  return service().cfg().method == ICAP::methodReqmod &&
894  expectHttpHeader();
895 }
896 
898 {
899  Must(state.writing == State::writingPaused);
900  // server must not respond before the end of preview: we may send ieof
901  Must(preview.enabled() && preview.done() && !preview.ieof());
902 
903  // 100 "Continue" cancels our Preview commitment,
904  // but not commitment to handle 204 or 206 outside Preview
905  if (!state.allowedPostview204 && !state.allowedPostview206)
906  stopBackup();
907 
908  state.parsing = State::psIcapHeader; // eventually
909  icapReply->reset();
910 
911  state.writing = State::writingPrime;
912 
913  writeMore();
914 }
915 
917 {
918  state.parsing = State::psHttpHeader;
919  state.sending = State::sendingAdapted;
920  stopBackup();
921  checkConsuming();
922 }
923 
925 {
926  stopParsing();
927  prepEchoing();
928 }
929 
931 {
932  if (state.writing == State::writingPaused) {
933  Must(preview.enabled());
934  Must(state.allowedPreview206);
935  debugs(93, 7, HERE << "206 inside preview");
936  } else {
937  Must(state.writing > State::writingPaused);
938  Must(state.allowedPostview206);
939  debugs(93, 7, HERE << "206 outside preview");
940  }
941  state.parsing = State::psHttpHeader;
942  state.sending = State::sendingAdapted;
943  state.readyForUob = true;
944  checkConsuming();
945 }
946 
947 // Called when we receive a 204 No Content response and
948 // when we are trying to bypass a service failure.
949 // We actually start sending (echoig or not) in startSending.
951 {
952  disableRepeats("preparing to echo content");
953  disableBypass("preparing to echo content", true);
954  setOutcome(xoEcho);
955 
956  // We want to clone the HTTP message, but we do not want
957  // to copy some non-HTTP state parts that Http::Message kids carry in them.
958  // Thus, we cannot use a smart pointer, copy constructor, or equivalent.
959  // Instead, we simply write the HTTP message and "clone" it by parsing.
960  // TODO: use Http::Message::clone()!
961 
962  Http::Message *oldHead = virgin.header;
963  debugs(93, 7, HERE << "cloning virgin message " << oldHead);
964 
965  MemBuf httpBuf;
966 
967  // write the virgin message into a memory buffer
968  httpBuf.init();
969  packHead(httpBuf, oldHead);
970 
971  // allocate the adapted message and copy metainfo
972  Must(!adapted.header);
973  {
974  Http::MessagePointer newHead;
975  if (const HttpRequest *r = dynamic_cast<const HttpRequest*>(oldHead)) {
976  newHead = new HttpRequest(r->masterXaction);
977  } else if (dynamic_cast<const HttpReply*>(oldHead)) {
978  newHead = new HttpReply;
979  }
980  Must(newHead);
981 
982  newHead->inheritProperties(oldHead);
983 
984  adapted.setHeader(newHead.getRaw());
985  }
986 
987  // parse the buffer back
989 
990  httpBuf.terminate(); // Http::Message::parse requires nil-terminated buffer
991  Must(adapted.header->parse(httpBuf.content(), httpBuf.contentSize(), true, &error));
992  Must(adapted.header->hdr_sz == httpBuf.contentSize()); // no leftovers
993 
994  httpBuf.clean();
995 
996  debugs(93, 7, HERE << "cloned virgin message " << oldHead << " to " <<
997  adapted.header);
998 
999  // setup adapted body pipe if needed
1000  if (oldHead->body_pipe != NULL) {
1001  debugs(93, 7, HERE << "will echo virgin body from " <<
1002  oldHead->body_pipe);
1003  if (!virginBodySending.active())
1004  virginBodySending.plan(); // will throw if not possible
1005  state.sending = State::sendingVirgin;
1006  checkConsuming();
1007 
1008  // TODO: optimize: is it possible to just use the oldHead pipe and
1009  // remove ICAP from the loop? This echoing is probably a common case!
1010  makeAdaptedBodyPipe("echoed virgin response");
1011  if (oldHead->body_pipe->bodySizeKnown())
1012  adapted.body_pipe->setBodySize(oldHead->body_pipe->bodySize());
1013  debugs(93, 7, HERE << "will echo virgin body to " <<
1014  adapted.body_pipe);
1015  } else {
1016  debugs(93, 7, HERE << "no virgin body to echo");
1017  stopSending(true);
1018  }
1019 }
1020 
1024 {
1025  Must(virginBodySending.active());
1026  Must(virgin.header->body_pipe != NULL);
1027 
1028  setOutcome(xoPartEcho);
1029 
1030  debugs(93, 7, HERE << "will echo virgin body suffix from " <<
1031  virgin.header->body_pipe << " offset " << pos );
1032 
1033  // check that use-original-body=N does not point beyond buffered data
1034  const uint64_t virginDataEnd = virginConsumed +
1035  virgin.body_pipe->buf().contentSize();
1036  Must(pos <= virginDataEnd);
1037  virginBodySending.progress(static_cast<size_t>(pos));
1038 
1039  state.sending = State::sendingVirgin;
1040  checkConsuming();
1041 
1042  if (virgin.header->body_pipe->bodySizeKnown())
1043  adapted.body_pipe->expectProductionEndAfter(virgin.header->body_pipe->bodySize() - pos);
1044 
1045  debugs(93, 7, HERE << "will echo virgin body suffix to " <<
1046  adapted.body_pipe);
1047 
1048  // Start echoing data
1049  echoMore();
1050 }
1051 
1053 {
1054  stopParsing(false);
1055  stopBackup();
1056  // TODO: mark connection as "bad"
1057 
1058  // Terminate the transaction; we do not know how to handle this response.
1059  throw TexcHere("Unsupported ICAP status code");
1060 }
1061 
1063 {
1064  if (expectHttpHeader()) {
1065  replyHttpHeaderSize = 0;
1066  maybeAllocateHttpMsg();
1067 
1068  if (!parseHead(adapted.header))
1069  return; // need more header data
1070 
1071  if (adapted.header)
1072  replyHttpHeaderSize = adapted.header->hdr_sz;
1073 
1074  if (dynamic_cast<HttpRequest*>(adapted.header)) {
1075  const HttpRequest *oldR = dynamic_cast<const HttpRequest*>(virgin.header);
1076  Must(oldR);
1077  // TODO: the adapted request did not really originate from the
1078  // client; give proxy admin an option to prevent copying of
1079  // sensitive client information here. See the following thread:
1080  // http://www.squid-cache.org/mail-archive/squid-dev/200703/0040.html
1081  }
1082 
1083  // Maybe adapted.header==NULL if HttpReply and have Http 0.9 ....
1084  if (adapted.header)
1085  adapted.header->inheritProperties(virgin.header);
1086  }
1087 
1088  decideOnParsingBody();
1089 }
1090 
1091 template<class Part>
1092 bool Adaptation::Icap::ModXact::parsePart(Part *part, const char *description)
1093 {
1094  Must(part);
1095  debugs(93, 5, "have " << readBuf.length() << ' ' << description << " bytes to parse; state: " << state.parsing);
1097  // XXX: performance regression. c_str() data copies
1098  // XXX: Http::Message::parse requires a terminated string buffer
1099  const char *tmpBuf = readBuf.c_str();
1100  const bool parsed = part->parse(tmpBuf, readBuf.length(), commEof, &error);
1101  debugs(93, (!parsed && error) ? 2 : 5, description << " parsing result: " << parsed << " detail: " << error);
1102  Must(parsed || !error);
1103  if (parsed)
1104  readBuf.consume(part->hdr_sz);
1105  return parsed;
1106 }
1107 
1108 // parses both HTTP and ICAP headers
1109 bool
1111 {
1112  if (!parsePart(head, "head")) {
1113  head->reset();
1114  return false;
1115  }
1116  return true;
1117 }
1118 
1120 {
1121  return gotEncapsulated("res-hdr") || gotEncapsulated("req-hdr");
1122 }
1123 
1125 {
1126  return gotEncapsulated("res-body") || gotEncapsulated("req-body");
1127 }
1128 
1130 {
1131  String trailers;
1132  const bool promisesToSendTrailer = icapReply->header.getByIdIfPresent(Http::HdrType::TRAILER, &trailers);
1133  const bool supportsTrailers = icapReply->header.hasListMember(Http::HdrType::ALLOW, "trailers", ',');
1134  // ICAP Trailer specs require us to reject transactions having either Trailer
1135  // header or Allow:trailers
1136  Must((promisesToSendTrailer == supportsTrailers) || (!promisesToSendTrailer && supportsTrailers));
1137  if (promisesToSendTrailer && !trailers.size())
1138  debugs(93, DBG_IMPORTANT, "ERROR: ICAP Trailer response header field must not be empty (salvaged)");
1139  return promisesToSendTrailer;
1140 }
1141 
1143 {
1144  if (expectHttpBody()) {
1145  debugs(93, 5, HERE << "expecting a body");
1146  state.parsing = State::psBody;
1147  replyHttpBodySize = 0;
1148  bodyParser = new Http1::TeChunkedParser;
1149  bodyParser->parseExtensionValuesWith(&extensionParser);
1150  makeAdaptedBodyPipe("adapted response from the ICAP server");
1151  Must(state.sending == State::sendingAdapted);
1152  } else {
1153  debugs(93, 5, HERE << "not expecting a body");
1154  if (trailerParser)
1155  state.parsing = State::psIcapTrailer;
1156  else
1157  stopParsing();
1158  stopSending(true);
1159  }
1160 }
1161 
1163 {
1164  Must(state.parsing == State::psBody);
1165  Must(bodyParser);
1166 
1167  debugs(93, 5, "have " << readBuf.length() << " body bytes to parse");
1168 
1169  // the parser will throw on errors
1170  BodyPipeCheckout bpc(*adapted.body_pipe);
1171  bodyParser->setPayloadBuffer(&bpc.buf);
1172  const bool parsed = bodyParser->parse(readBuf);
1173  readBuf = bodyParser->remaining(); // sync buffers after parse
1174  bpc.checkIn();
1175 
1176  debugs(93, 5, "have " << readBuf.length() << " body bytes after parsed all: " << parsed);
1177  replyHttpBodySize += adapted.body_pipe->buf().contentSize();
1178 
1179  // TODO: expose BodyPipe::putSize() to make this check simpler and clearer
1180  // TODO: do we really need this if we disable when sending headers?
1181  if (adapted.body_pipe->buf().contentSize() > 0) { // parsed something sometime
1182  disableRepeats("sent adapted content");
1183  disableBypass("sent adapted content", true);
1184  }
1185 
1186  if (parsed) {
1187  if (state.readyForUob && extensionParser.sawUseOriginalBody())
1188  prepPartialBodyEchoing(extensionParser.useOriginalBody());
1189  else
1190  stopSending(true); // the parser succeeds only if all parsed data fits
1191  if (trailerParser)
1192  state.parsing = State::psIcapTrailer;
1193  else
1194  stopParsing();
1195  return;
1196  }
1197 
1198  debugs(93,3,HERE << this << " needsMoreData = " << bodyParser->needsMoreData());
1199 
1200  if (bodyParser->needsMoreData()) {
1201  debugs(93,3,HERE << this);
1202  Must(mayReadMore());
1203  readMore();
1204  }
1205 
1206  if (bodyParser->needsMoreSpace()) {
1207  Must(!doneSending()); // can hope for more space
1208  Must(adapted.body_pipe->buf().contentSize() > 0); // paranoid
1209  // TODO: there should be a timeout in case the sink is broken
1210  // or cannot consume partial content (while we need more space)
1211  }
1212 }
1213 
1214 void Adaptation::Icap::ModXact::stopParsing(const bool checkUnparsedData)
1215 {
1216  if (state.parsing == State::psDone)
1217  return;
1218 
1219  if (checkUnparsedData)
1220  Must(readBuf.isEmpty());
1221 
1222  debugs(93, 7, "will no longer parse" << status());
1223 
1224  delete bodyParser;
1225  bodyParser = nullptr;
1226 
1227  delete trailerParser;
1228  trailerParser = nullptr;
1229 
1230  state.parsing = State::psDone;
1231 }
1232 
1233 // HTTP side added virgin body data
1235 {
1236  writeMore();
1237 
1238  if (state.sending == State::sendingVirgin)
1239  echoMore();
1240 }
1241 
1242 // HTTP side sent us all virgin info
1244 {
1245  Must(virgin.body_pipe->productionEnded());
1246 
1247  // push writer and sender in case we were waiting for the last-chunk
1248  writeMore();
1249 
1250  if (state.sending == State::sendingVirgin)
1251  echoMore();
1252 }
1253 
1254 // body producer aborted, but the initiator may still want to know
1255 // the answer, even though the HTTP message has been truncated
1257 {
1258  Must(virgin.body_pipe->productionEnded());
1259 
1260  // push writer and sender in case we were waiting for the last-chunk
1261  writeMore();
1262 
1263  if (state.sending == State::sendingVirgin)
1264  echoMore();
1265 }
1266 
1267 // adapted body consumer wants more adapted data and
1268 // possibly freed some buffer space
1270 {
1271  if (state.sending == State::sendingVirgin)
1272  echoMore();
1273  else if (state.sending == State::sendingAdapted)
1274  parseMore();
1275  else
1276  Must(state.sending == State::sendingUndecided);
1277 }
1278 
1279 // adapted body consumer aborted
1281 {
1282  static const auto d = MakeNamedErrorDetail("ICAP_XACT_BODY_CONSUMER_ABORT");
1283  detailError(d);
1284  mustStop("adapted body consumer aborted");
1285 }
1286 
1288 {
1289  delete bodyParser;
1290  delete trailerParser;
1291 }
1292 
1293 // internal cleanup
1295 {
1296  debugs(93, 5, HERE << "swan sings" << status());
1297 
1298  stopWriting(false);
1299  stopSending(false);
1300 
1301  if (theInitiator.set()) { // we have not sent the answer to the initiator
1302  static const auto d = MakeNamedErrorDetail("ICAP_XACT_OTHER");
1303  detailError(d);
1304  }
1305 
1306  // update adaptation history if start was called and we reserved a slot
1307  Adaptation::History::Pointer ah = virginRequest().adaptLogHistory();
1308  if (ah != NULL && adaptHistoryId >= 0)
1309  ah->recordXactFinish(adaptHistoryId);
1310 
1312 }
1313 
1315 
1317 {
1318  HttpRequest *adapted_request_ = nullptr;
1319  HttpReply *adapted_reply_ = nullptr;
1320  HttpRequest *virgin_request_ = const_cast<HttpRequest*>(&virginRequest());
1321  if (!(adapted_request_ = dynamic_cast<HttpRequest*>(adapted.header))) {
1322  // if the request was not adapted, use virgin request to simplify
1323  // the code further below
1324  adapted_request_ = virgin_request_;
1325  adapted_reply_ = dynamic_cast<HttpReply*>(adapted.header);
1326  }
1327 
1328  Adaptation::Icap::History::Pointer h = virgin_request_->icapHistory();
1329  Must(h != NULL); // ICAPXaction::maybeLog calls only if there is a log
1330  al.icp.opcode = ICP_INVALID;
1331  al.url = h->log_uri.termedBuf();
1332  const Adaptation::Icap::ServiceRep &s = service();
1333  al.icap.reqMethod = s.cfg().method;
1334 
1335  al.cache.caddr = virgin_request_->client_addr;
1336 
1337  al.request = virgin_request_;
1338  HTTPMSGLOCK(al.request);
1339  al.adapted_request = adapted_request_;
1340  HTTPMSGLOCK(al.adapted_request);
1341 
1342  // XXX: This reply (and other ALE members!) may have been needed earlier.
1343  al.reply = adapted_reply_;
1344 
1345  if (h->rfc931.size())
1346  al.cache.rfc931 = h->rfc931.termedBuf();
1347 
1348 #if USE_OPENSSL
1349  if (h->ssluser.size())
1350  al.cache.ssluser = h->ssluser.termedBuf();
1351 #endif
1352  al.cache.code = h->logType;
1353 
1354  const Http::Message *virgin_msg = dynamic_cast<HttpReply*>(virgin.header);
1355  if (!virgin_msg)
1356  virgin_msg = virgin_request_;
1357  assert(virgin_msg != virgin.cause);
1358  al.http.clientRequestSz.header = virgin_msg->hdr_sz;
1359  if (virgin_msg->body_pipe != NULL)
1360  al.http.clientRequestSz.payloadData = virgin_msg->body_pipe->producedSize();
1361 
1362  // leave al.icap.bodyBytesRead negative if no body
1363  if (replyHttpHeaderSize >= 0 || replyHttpBodySize >= 0) {
1364  const int64_t zero = 0; // to make max() argument types the same
1365  const uint64_t headerSize = max(zero, replyHttpHeaderSize);
1366  const uint64_t bodySize = max(zero, replyHttpBodySize);
1367  al.icap.bodyBytesRead = headerSize + bodySize;
1368  al.http.clientReplySz.header = headerSize;
1369  al.http.clientReplySz.payloadData = bodySize;
1370  }
1371 
1372  if (adapted_reply_) {
1373  al.http.code = adapted_reply_->sline.status();
1374  al.http.content_type = adapted_reply_->content_type.termedBuf();
1375  if (replyHttpBodySize >= 0)
1376  al.cache.highOffset = replyHttpBodySize;
1377  //don't set al.cache.objectSize because it hasn't exist yet
1378  }
1379  prepareLogWithRequestDetails(adapted_request_, alep);
1381 }
1382 
1384 {
1385  char ntoabuf[MAX_IPSTRLEN];
1386  /*
1387  * XXX These should use HttpHdr interfaces instead of Printfs
1388  */
1389  const Adaptation::ServiceConfig &s = service().cfg();
1390  buf.appendf("%s " SQUIDSTRINGPH " ICAP/1.0\r\n", s.methodStr(), SQUIDSTRINGPRINT(s.uri));
1391  buf.appendf("Host: " SQUIDSTRINGPH ":%d\r\n", SQUIDSTRINGPRINT(s.host), s.port);
1392  buf.appendf("Date: %s\r\n", mkrfc1123(squid_curtime));
1393 
1395  buf.appendf("Connection: close\r\n");
1396 
1397  const HttpRequest *request = &virginRequest();
1398 
1399  // we must forward "Proxy-Authenticate" and "Proxy-Authorization"
1400  // as ICAP headers.
1401  if (virgin.header->header.has(Http::HdrType::PROXY_AUTHENTICATE)) {
1402  String vh=virgin.header->header.getById(Http::HdrType::PROXY_AUTHENTICATE);
1403  buf.appendf("Proxy-Authenticate: " SQUIDSTRINGPH "\r\n",SQUIDSTRINGPRINT(vh));
1404  }
1405 
1406  if (virgin.header->header.has(Http::HdrType::PROXY_AUTHORIZATION)) {
1407  String vh=virgin.header->header.getById(Http::HdrType::PROXY_AUTHORIZATION);
1408  buf.appendf("Proxy-Authorization: " SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(vh));
1409  } else if (request->extacl_user.size() > 0 && request->extacl_passwd.size() > 0) {
1410  struct base64_encode_ctx ctx;
1411  base64_encode_init(&ctx);
1412  char base64buf[base64_encode_len(MAX_LOGIN_SZ)];
1413  size_t resultLen = base64_encode_update(&ctx, base64buf, request->extacl_user.size(), reinterpret_cast<const uint8_t*>(request->extacl_user.rawBuf()));
1414  resultLen += base64_encode_update(&ctx, base64buf+resultLen, 1, reinterpret_cast<const uint8_t*>(":"));
1415  resultLen += base64_encode_update(&ctx, base64buf+resultLen, request->extacl_passwd.size(), reinterpret_cast<const uint8_t*>(request->extacl_passwd.rawBuf()));
1416  resultLen += base64_encode_final(&ctx, base64buf+resultLen);
1417  buf.appendf("Proxy-Authorization: Basic %.*s\r\n", (int)resultLen, base64buf);
1418  }
1419 
1420  // share the cross-transactional database records if needed
1422  Adaptation::History::Pointer ah = request->adaptHistory(false);
1423  if (ah != NULL) {
1424  String name, value;
1425  if (ah->getXxRecord(name, value)) {
1426  buf.appendf(SQUIDSTRINGPH ": " SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(name), SQUIDSTRINGPRINT(value));
1427  }
1428  }
1429  }
1430 
1431  buf.append("Encapsulated: ", 14);
1432 
1433  MemBuf httpBuf;
1434 
1435  httpBuf.init();
1436 
1437  // build HTTP request header, if any
1438  ICAP::Method m = s.method;
1439 
1440  // to simplify, we could assume that request is always available
1441 
1442  if (request) {
1443  if (ICAP::methodRespmod == m)
1444  encapsulateHead(buf, "req-hdr", httpBuf, request);
1445  else if (ICAP::methodReqmod == m)
1446  encapsulateHead(buf, "req-hdr", httpBuf, virgin.header);
1447  }
1448 
1449  if (ICAP::methodRespmod == m)
1450  if (const Http::Message *prime = virgin.header)
1451  encapsulateHead(buf, "res-hdr", httpBuf, prime);
1452 
1453  if (!virginBody.expected())
1454  buf.appendf("null-body=%d", (int) httpBuf.contentSize());
1455  else if (ICAP::methodReqmod == m)
1456  buf.appendf("req-body=%d", (int) httpBuf.contentSize());
1457  else
1458  buf.appendf("res-body=%d", (int) httpBuf.contentSize());
1459 
1460  buf.append(ICAP::crlf, 2); // terminate Encapsulated line
1461 
1462  if (preview.enabled()) {
1463  buf.appendf("Preview: %d\r\n", (int)preview.ad());
1464  if (!virginBody.expected()) // there is no body to preview
1465  finishNullOrEmptyBodyPreview(httpBuf);
1466  }
1467 
1468  makeAllowHeader(buf);
1469 
1470  if (TheConfig.send_client_ip && request) {
1471  Ip::Address client_addr;
1472 #if FOLLOW_X_FORWARDED_FOR
1474  client_addr = request->indirect_client_addr;
1475  } else
1476 #endif
1477  client_addr = request->client_addr;
1478  if (!client_addr.isAnyAddr() && !client_addr.isNoAddr())
1479  buf.appendf("X-Client-IP: %s\r\n", client_addr.toStr(ntoabuf,MAX_IPSTRLEN));
1480  }
1481 
1483  makeUsernameHeader(request, buf);
1484 
1485  // Adaptation::Config::metaHeaders
1486  for (auto h: Adaptation::Config::metaHeaders) {
1487  HttpRequest *r = virgin.cause ?
1488  virgin.cause : dynamic_cast<HttpRequest*>(virgin.header);
1489  Must(r);
1490 
1491  HttpReply *reply = dynamic_cast<HttpReply*>(virgin.header);
1492 
1493  SBuf matched;
1494  if (h->match(r, reply, alMaster, matched)) {
1495  buf.append(h->key().rawContent(), h->key().length());
1496  buf.append(": ", 2);
1497  buf.append(matched.rawContent(), matched.length());
1498  buf.append("\r\n", 2);
1499  Adaptation::History::Pointer ah = request->adaptHistory(false);
1500  if (ah != NULL) {
1501  if (ah->metaHeaders == NULL)
1502  ah->metaHeaders = new NotePairs;
1503  if (!ah->metaHeaders->hasPair(h->key(), matched))
1504  ah->metaHeaders->add(h->key(), matched);
1505  }
1506  }
1507  }
1508 
1509  // fprintf(stderr, "%s\n", buf.content());
1510 
1511  buf.append(ICAP::crlf, 2); // terminate ICAP header
1512 
1513  // fill icapRequest for logging
1514  Must(icapRequest->parseCharBuf(buf.content(), buf.contentSize()));
1515 
1516  // start ICAP request body with encapsulated HTTP headers
1517  buf.append(httpBuf.content(), httpBuf.contentSize());
1518 
1519  httpBuf.clean();
1520 }
1521 
1522 // decides which Allow values to write and updates the request buffer
1524 {
1525  const bool allow204in = preview.enabled(); // TODO: add shouldAllow204in()
1526  const bool allow204out = state.allowedPostview204 = shouldAllow204();
1527  const bool allow206in = state.allowedPreview206 = shouldAllow206in();
1528  const bool allow206out = state.allowedPostview206 = shouldAllow206out();
1529  const bool allowTrailers = true; // TODO: make configurable
1530 
1531  debugs(93, 9, "Allows: " << allow204in << allow204out <<
1532  allow206in << allow206out << allowTrailers);
1533 
1534  const bool allow204 = allow204in || allow204out;
1535  const bool allow206 = allow206in || allow206out;
1536 
1537  if ((allow204 || allow206) && virginBody.expected())
1538  virginBodySending.plan(); // if there is a virgin body, plan to send it
1539 
1540  // writing Preview:... means we will honor 204 inside preview
1541  // writing Allow/204 means we will honor 204 outside preview
1542  // writing Allow:206 means we will honor 206 inside preview
1543  // writing Allow:204,206 means we will honor 206 outside preview
1544  if (allow204 || allow206 || allowTrailers) {
1545  buf.appendf("Allow: ");
1546  if (allow204out)
1547  buf.appendf("204, ");
1548  if (allow206)
1549  buf.appendf("206, ");
1550  if (allowTrailers)
1551  buf.appendf("trailers");
1552  buf.appendf("\r\n");
1553  }
1554 }
1555 
1557 {
1558 #if USE_AUTH
1559  struct base64_encode_ctx ctx;
1560  base64_encode_init(&ctx);
1561 
1562  const char *value = NULL;
1563  if (request->auth_user_request != NULL) {
1564  value = request->auth_user_request->username();
1565  } else if (request->extacl_user.size() > 0) {
1566  value = request->extacl_user.termedBuf();
1567  }
1568 
1569  if (value) {
1571  char base64buf[base64_encode_len(MAX_LOGIN_SZ)];
1572  size_t resultLen = base64_encode_update(&ctx, base64buf, strlen(value), reinterpret_cast<const uint8_t*>(value));
1573  resultLen += base64_encode_final(&ctx, base64buf+resultLen);
1574  buf.appendf("%s: %.*s\r\n", TheConfig.client_username_header, (int)resultLen, base64buf);
1575  } else
1576  buf.appendf("%s: %s\r\n", TheConfig.client_username_header, value);
1577  }
1578 #else
1579  (void)request;
1580  (void)buf;
1581 #endif
1582 }
1583 
1584 void
1585 Adaptation::Icap::ModXact::encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &httpBuf, const Http::Message *head)
1586 {
1587  // update ICAP header
1588  icapBuf.appendf("%s=%d, ", section, (int) httpBuf.contentSize());
1589 
1590  // begin cloning
1591  Http::MessagePointer headClone;
1592 
1593  if (const HttpRequest* old_request = dynamic_cast<const HttpRequest*>(head)) {
1594  HttpRequest::Pointer new_request(new HttpRequest(old_request->masterXaction));
1595  // copy the request-line details
1596  new_request->method = old_request->method;
1597  new_request->url = old_request->url;
1598  new_request->http_ver = old_request->http_ver;
1599  headClone = new_request.getRaw();
1600  } else if (const HttpReply *old_reply = dynamic_cast<const HttpReply*>(head)) {
1601  HttpReply::Pointer new_reply(new HttpReply);
1602  new_reply->sline = old_reply->sline;
1603  headClone = new_reply.getRaw();
1604  }
1605  Must(headClone);
1606  headClone->inheritProperties(head);
1607 
1609  while (HttpHeaderEntry* p_head_entry = head->header.getEntry(&pos))
1610  headClone->header.addEntry(p_head_entry->clone());
1611 
1612  // end cloning
1613 
1614  // remove all hop-by-hop headers from the clone
1616  headClone->header.removeHopByHopEntries();
1617 
1618  // TODO: modify HttpHeader::removeHopByHopEntries to accept a list of
1619  // excluded hop-by-hop headers
1620  if (head->header.has(Http::HdrType::UPGRADE)) {
1621  const auto upgrade = head->header.getList(Http::HdrType::UPGRADE);
1622  headClone->header.putStr(Http::HdrType::UPGRADE, upgrade.termedBuf());
1623  }
1624 
1625  // pack polished HTTP header
1626  packHead(httpBuf, headClone.getRaw());
1627 
1628  // headClone unlocks and, hence, deletes the message we packed
1629 }
1630 
1631 void
1633 {
1634  head->packInto(&httpBuf, true);
1635 }
1636 
1637 // decides whether to offer a preview and calculates its size
1639 {
1640  if (!TheConfig.preview_enable) {
1641  debugs(93, 5, HERE << "preview disabled by squid.conf");
1642  return;
1643  }
1644 
1645  const SBuf urlPath(virginRequest().url.path());
1646  size_t wantedSize;
1647  if (!service().wantsPreview(urlPath, wantedSize)) {
1648  debugs(93, 5, "should not offer preview for " << urlPath);
1649  return;
1650  }
1651 
1652  // we decided to do preview, now compute its size
1653 
1654  // cannot preview more than we can backup
1655  size_t ad = min(wantedSize, TheBackupLimit);
1656 
1657  if (!virginBody.expected())
1658  ad = 0;
1659  else if (virginBody.knownSize())
1660  ad = min(static_cast<uint64_t>(ad), virginBody.size()); // not more than we have
1661 
1662  debugs(93, 5, HERE << "should offer " << ad << "-byte preview " <<
1663  "(service wanted " << wantedSize << ")");
1664 
1665  preview.enable(ad);
1666  Must(preview.enabled());
1667 }
1668 
1669 // decides whether to allow 204 responses
1671 {
1672  if (!service().allows204())
1673  return false;
1674 
1675  return canBackupEverything();
1676 }
1677 
1678 // decides whether to allow 206 responses in some mode
1680 {
1681  return TheConfig.allow206_enable && service().allows206() &&
1682  virginBody.expected(); // no need for 206 without a body
1683 }
1684 
1685 // decides whether to allow 206 responses in preview mode
1687 {
1688  return shouldAllow206any() && preview.enabled();
1689 }
1690 
1691 // decides whether to allow 206 responses outside of preview
1693 {
1694  return shouldAllow206any() && canBackupEverything();
1695 }
1696 
1697 // used by shouldAllow204 and decideOnRetries
1699 {
1700  if (!virginBody.expected())
1701  return true; // no body means no problems with backup
1702 
1703  // if there is a body, check whether we can backup it all
1704 
1705  if (!virginBody.knownSize())
1706  return false;
1707 
1708  // or should we have a different backup limit?
1709  // note that '<' allows for 0-termination of the "full" backup buffer
1710  return virginBody.size() < TheBackupLimit;
1711 }
1712 
1713 // Decide whether this transaction can be retried if pconn fails
1714 // Must be called after decideOnPreview and before openConnection()
1716 {
1717  if (!isRetriable)
1718  return; // no, already decided
1719 
1720  if (preview.enabled())
1721  return; // yes, because preview provides enough guarantees
1722 
1723  if (canBackupEverything())
1724  return; // yes, because we can back everything up
1725 
1726  disableRetries(); // no, because we cannot back everything up
1727 }
1728 
1729 // Normally, the body-writing code handles preview body. It can deal with
1730 // bodies of unexpected size, including those that turn out to be empty.
1731 // However, that code assumes that the body was expected and body control
1732 // structures were initialized. This is not the case when there is no body
1733 // or the body is known to be empty, because the virgin message will lack a
1734 // body_pipe. So we handle preview of null-body and zero-size bodies here.
1736 {
1737  Must(!virginBodyWriting.active()); // one reason we handle it here
1738  Must(!virgin.body_pipe); // another reason we handle it here
1739  Must(!preview.ad());
1740 
1741  // do not add last-chunk because our Encapsulated header says null-body
1742  // addLastRequestChunk(buf);
1743  preview.wrote(0, true);
1744 
1745  Must(preview.done());
1746  Must(preview.ieof());
1747 }
1748 
1750 {
1752 
1753  if (state.serviceWaiting)
1754  buf.append("U", 1);
1755 
1756  if (virgin.body_pipe != NULL)
1757  buf.append("R", 1);
1758 
1759  if (haveConnection() && !doneReading())
1760  buf.append("r", 1);
1761 
1762  if (!state.doneWriting() && state.writing != State::writingInit)
1763  buf.appendf("w(%d)", state.writing);
1764 
1765  if (preview.enabled()) {
1766  if (!preview.done())
1767  buf.appendf("P(%d)", (int) preview.debt());
1768  }
1769 
1770  if (virginBodySending.active())
1771  buf.append("B", 1);
1772 
1773  if (!state.doneParsing() && state.parsing != State::psIcapHeader)
1774  buf.appendf("p(%d)", state.parsing);
1775 
1776  if (!doneSending() && state.sending != State::sendingUndecided)
1777  buf.appendf("S(%d)", state.sending);
1778 
1779  if (state.readyForUob)
1780  buf.append("6", 1);
1781 
1782  if (canStartBypass)
1783  buf.append("Y", 1);
1784 
1785  if (protectGroupBypass)
1786  buf.append("G", 1);
1787 }
1788 
1790 {
1792 
1793  if (!virgin.body_pipe)
1794  buf.append("R", 1);
1795 
1796  if (state.doneWriting())
1797  buf.append("w", 1);
1798 
1799  if (preview.enabled()) {
1800  if (preview.done())
1801  buf.appendf("P%s", preview.ieof() ? "(ieof)" : "");
1802  }
1803 
1804  if (doneReading())
1805  buf.append("r", 1);
1806 
1807  if (state.doneParsing())
1808  buf.append("p", 1);
1809 
1810  if (doneSending())
1811  buf.append("S", 1);
1812 }
1813 
1814 bool Adaptation::Icap::ModXact::gotEncapsulated(const char *section) const
1815 {
1816  return !icapReply->header.getByNameListMember("Encapsulated",
1817  section, ',').isEmpty();
1818 }
1819 
1820 // calculate whether there is a virgin HTTP body and
1821 // whether its expected size is known
1822 // TODO: rename because we do not just estimate
1824 {
1825  // note: lack of size info may disable previews and 204s
1826 
1827  Http::Message *msg = virgin.header;
1828  Must(msg);
1829 
1830  HttpRequestMethod method;
1831 
1832  if (virgin.cause)
1833  method = virgin.cause->method;
1834  else if (HttpRequest *req = dynamic_cast<HttpRequest*>(msg))
1835  method = req->method;
1836  else
1837  method = Http::METHOD_NONE;
1838 
1839  int64_t size;
1840  // expectingBody returns true for zero-sized bodies, but we will not
1841  // get a pipe for that body, so we treat the message as bodyless
1842  if (method != Http::METHOD_NONE && msg->expectingBody(method, size) && size) {
1843  debugs(93, 6, HERE << "expects virgin body from " <<
1844  virgin.body_pipe << "; size: " << size);
1845 
1846  virginBody.expect(size);
1847  virginBodyWriting.plan();
1848 
1849  // sign up as a body consumer
1850  Must(msg->body_pipe != NULL);
1851  Must(msg->body_pipe == virgin.body_pipe);
1852  Must(virgin.body_pipe->setConsumerIfNotLate(this));
1853 
1854  // make sure TheBackupLimit is in-sync with the buffer size
1855  Must(TheBackupLimit <= static_cast<size_t>(msg->body_pipe->buf().max_capacity));
1856  } else {
1857  debugs(93, 6, HERE << "does not expect virgin body");
1858  Must(msg->body_pipe == NULL);
1859  checkConsuming();
1860  }
1861 }
1862 
1864 {
1865  Must(!adapted.body_pipe);
1866  Must(!adapted.header->body_pipe);
1867  adapted.header->body_pipe = new BodyPipe(this);
1868  adapted.body_pipe = adapted.header->body_pipe;
1869  debugs(93, 7, HERE << "will supply " << what << " via " <<
1870  adapted.body_pipe << " pipe");
1871 }
1872 
1873 // TODO: Move SizedEstimate and Preview elsewhere
1874 
1876  : theData(dtUnexpected)
1877 {}
1878 
1880 {
1881  theData = (aSize >= 0) ? aSize : (int64_t)dtUnknown;
1882 }
1883 
1885 {
1886  return theData != dtUnexpected;
1887 }
1888 
1890 {
1891  Must(expected());
1892  return theData != dtUnknown;
1893 }
1894 
1896 {
1897  Must(knownSize());
1898  return static_cast<uint64_t>(theData);
1899 }
1900 
1901 Adaptation::Icap::VirginBodyAct::VirginBodyAct(): theStart(0), theState(stUndecided)
1902 {}
1903 
1905 {
1906  Must(!disabled());
1907  Must(!theStart); // not started
1908  theState = stActive;
1909 }
1910 
1912 {
1913  theState = stDisabled;
1914 }
1915 
1917 {
1918  Must(active());
1919 #if SIZEOF_SIZE_T > 4
1920  /* always true for smaller size_t's */
1921  Must(static_cast<int64_t>(size) >= 0);
1922 #endif
1923  theStart += static_cast<int64_t>(size);
1924 }
1925 
1927 {
1928  Must(active());
1929  return static_cast<uint64_t>(theStart);
1930 }
1931 
1932 Adaptation::Icap::Preview::Preview(): theWritten(0), theAd(0), theState(stDisabled)
1933 {}
1934 
1936 {
1937  // TODO: check for anAd not exceeding preview size limit
1938  Must(!enabled());
1939  theAd = anAd;
1940  theState = stWriting;
1941 }
1942 
1944 {
1945  return theState != stDisabled;
1946 }
1947 
1949 {
1950  Must(enabled());
1951  return theAd;
1952 }
1953 
1955 {
1956  Must(enabled());
1957  return theState >= stIeof;
1958 }
1959 
1961 {
1962  Must(enabled());
1963  return theState == stIeof;
1964 }
1965 
1967 {
1968  Must(enabled());
1969  return done() ? 0 : (theAd - theWritten);
1970 }
1971 
1972 void Adaptation::Icap::Preview::wrote(size_t size, bool wroteEof)
1973 {
1974  Must(enabled());
1975 
1976  theWritten += size;
1977 
1978  Must(theWritten <= theAd);
1979 
1980  if (wroteEof)
1981  theState = stIeof; // written size is irrelevant
1982  else if (theWritten >= theAd)
1983  theState = stDone;
1984 }
1985 
1987 {
1988  if (virgin.header == NULL)
1989  return false;
1990 
1991  virgin.header->firstLineBuf(mb);
1992 
1993  return true;
1994 }
1995 
1997 {
1998  HttpRequest *request = dynamic_cast<HttpRequest*>(adapted.header);
1999  // if no adapted request, update virgin (and inherit its properties later)
2000  // TODO: make this and HttpRequest::detailError constant, like adaptHistory
2001  if (!request)
2002  request = const_cast<HttpRequest*>(&virginRequest());
2003 
2004  if (request)
2005  request->detailError(ERR_ICAP_FAILURE, errDetail);
2006 }
2007 
2009 {
2010  HttpRequest *request = dynamic_cast<HttpRequest*>(adapted.header);
2011  // if no adapted request, update virgin (and inherit its properties later)
2012  if (!request)
2013  request = const_cast<HttpRequest*>(&virginRequest());
2014 
2015  if (request)
2016  request->clearError();
2017 }
2018 
2020 {
2021  Must(adapted.header);
2022  adapted.header->sources |= (service().cfg().connectionEncryption ? Http::Message::srcIcaps : Http::Message::srcIcap);
2023 }
2024 
2025 /* Adaptation::Icap::ModXactLauncher */
2026 
2028  AsyncJob("Adaptation::Icap::ModXactLauncher"),
2029  Adaptation::Icap::Launcher("Adaptation::Icap::ModXactLauncher", aService),
2030  al(alp)
2031 {
2032  virgin.setHeader(virginHeader);
2033  virgin.setCause(virginCause);
2034  updateHistory(true);
2035 }
2036 
2038 {
2040  dynamic_cast<Adaptation::Icap::ServiceRep*>(theService.getRaw());
2041  Must(s != NULL);
2042  return new Adaptation::Icap::ModXact(virgin.header, virgin.cause, al, s);
2043 }
2044 
2046 {
2047  debugs(93, 5, HERE << "swan sings");
2048  updateHistory(false);
2050 }
2051 
2053 {
2054  HttpRequest *r = virgin.cause ?
2055  virgin.cause : dynamic_cast<HttpRequest*>(virgin.header);
2056 
2057  // r should never be NULL but we play safe; TODO: add Should()
2058  if (r) {
2060  if (h != NULL) {
2061  if (doStart)
2062  h->start("ICAPModXactLauncher");
2063  else
2064  h->stop("ICAPModXactLauncher");
2065  }
2066  }
2067 }
2068 
2069 bool Adaptation::Icap::TrailerParser::parse(const char *buf, int len, int atEnd, Http::StatusCode *error) {
2071  // RFC 7230 section 4.1.2: MUST NOT generate a trailer that contains
2072  // a field necessary for message framing (e.g., Transfer-Encoding and Content-Length)
2073  clen.applyTrailerRules();
2074  const int parsed = trailer.parse(buf, len, atEnd, hdr_sz, clen);
2075  if (parsed < 0)
2076  *error = Http::scInvalidHeader; // TODO: should we add a new Http::scInvalidTrailer?
2077  return parsed > 0;
2078 }
2079 
2080 void
2082 {
2083  if (extName == UseOriginalBodyName) {
2084  useOriginalBody_ = tok.udec64("use-original-body");
2085  assert(useOriginalBody_ >= 0);
2086  } else {
2087  Ignore(tok, extName);
2088  }
2089 }
2090 
void disableBypass(const char *reason, bool includeGroupBypass)
Definition: ModXact.cc:718
const MemBuf & buf() const
Definition: BodyPipe.h:137
int hdr_sz
Definition: Message.h:82
void stop(const char *context)
note the end of an ICAP processing interval
Definition: History.cc:32
void prepPartialBodyEchoing(uint64_t pos)
Definition: ModXact.cc:1023
String content_type
Definition: HttpReply.h:46
SourceLocationId id() const
same-location exceptions have the same ID
Definition: TextException.h:39
#define Here()
source code location of the caller
Definition: Here.h:15
AccessLogEntry::Pointer alMaster
Master transaction AccessLogEntry.
Definition: ModXact.h:373
ModXactLauncher(Http::Message *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp, Adaptation::ServicePointer s)
Definition: ModXact.cc:2027
ModXact(Http::Message *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp, ServiceRep::Pointer &s)
Definition: ModXact.cc:55
void terminate()
Definition: MemBuf.cc:243
AnyP::ProtocolVersion http_ver
Definition: Message.h:73
#define base64_encode_len(length)
Definition: base64.h:169
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
@ srvBlock
Definition: Elements.h:19
void openChunk(MemBuf &buf, size_t chunkSize, bool ieof)
Definition: ModXact.cc:377
void applyTrailerRules()
prohibits Content-Length in GET/HEAD requests
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
common parts of HttpRequest and HttpReply
Definition: Message.h:26
void progress(size_t size)
Definition: ModXact.cc:1916
BodyPipe::Pointer body_pipe
optional pipeline to receive message body
Definition: Message.h:98
virtual void fillDoneStatus(MemBuf &buf) const
Definition: ModXact.cc:1789
void enable(size_t anAd)
Definition: ModXact.cc:1935
size_t virginContentSize(const VirginBodyAct &act) const
Definition: ModXact.cc:405
virtual bool expectingBody(const HttpRequestMethod &, int64_t &) const =0
void makeRequestHeaders(MemBuf &buf)
Definition: ModXact.cc:1383
virtual void fillDoneStatus(MemBuf &buf) const
Definition: Xaction.cc:665
Config TheConfig
Definition: Config.cc:19
const XactOutcome xoPartEcho
preserved virgin msg part (ICAP 206)
Definition: Elements.cc:24
void packHead(MemBuf &httpBuf, const Http::Message *head)
Definition: ModXact.cc:1632
@ scNone
Definition: StatusCode.h:21
void removeHopByHopEntries()
Definition: HttpHeader.cc:1738
HttpHeader header
Definition: Message.h:75
virtual void callException(const std::exception &e)
called when the job throws during an async call
Definition: ModXact.cc:666
Http1::TeChunkedParser * bodyParser
Definition: ModXact.h:303
virtual void noteMoreBodyDataAvailable(BodyPipe::Pointer)
Definition: ModXact.cc:1234
bool virginBodyEndReached(const VirginBodyAct &act) const
Definition: ModXact.cc:396
Parses and stores ICAP trailer header block.
Definition: ModXact.h:111
ssize_t HttpHeaderPos
Definition: HttpHeader.h:45
static int send_client_ip
Definition: Config.h:47
@ srcIcaps
Secure ICAP service.
Definition: Message.h:35
char * client_username_header
Definition: Config.h:36
void updateXxRecord(const char *name, const String &value)
sets or resets a cross-transactional database record
Definition: History.cc:104
size_t base64_encode_final(struct base64_encode_ctx *ctx, char *dst)
Definition: base64.c:308
bool isAnyAddr() const
Definition: Address.cc:170
void updateNextServices(const String &services)
sets or resets next services for the Adaptation::Iterator to notice
Definition: History.cc:120
CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Icap, ModXact)
#define HttpHeaderInitPos
Definition: HttpHeader.h:48
void error(char *format,...)
bool hasContent() const
Definition: MemBuf.h:54
#define SQUIDSTRINGPRINT(s)
Definition: SquidString.h:22
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:95
Definition: SBuf.h:87
virtual bool doneAll() const
whether positive goal has been reached
Definition: Xaction.cc:387
void setHeader(Header *h)
Definition: InOut.h:48
virtual void fillPendingStatus(MemBuf &buf) const
Definition: Xaction.cc:647
void stopParsing(const bool checkUnparsedData=true)
Definition: ModXact.cc:1214
const XactOutcome xoEcho
preserved virgin message (ICAP 204)
Definition: Elements.cc:23
static int send_username
Definition: Config.h:48
void closeChunk(MemBuf &buf)
Definition: ModXact.cc:382
static int use_indirect_client
Definition: Config.h:49
C * getRaw() const
Definition: RefCount.h:80
virtual void append(const char *c, int sz)
Definition: MemBuf.cc:211
String log_uri
the request uri
Definition: History.h:45
TrailerParser * trailerParser
Definition: ModXact.h:321
String rfc931
the username from ident
Definition: History.h:39
Http::StatusLine sline
Definition: HttpReply.h:56
virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer)
Definition: ModXact.cc:1269
void makeAllowHeader(MemBuf &buf)
Definition: ModXact.cc:1523
bool getXxRecord(String &name, String &value) const
returns true and fills the record fields iff there is a db record
Definition: History.cc:110
virtual void noteBodyProducerAborted(BodyPipe::Pointer)
Definition: ModXact.cc:1256
bool parsePart(Part *part, const char *description)
Definition: ModXact.cc:1092
StatusCode
Definition: StatusCode.h:20
#define SQUIDSTRINGPH
Definition: SquidString.h:21
@ MaxCapacity
Definition: BodyPipe.h:100
virtual bool doneAll() const
whether positive goal has been reached
Definition: ModXact.cc:529
#define DBG_IMPORTANT
Definition: Debug.h:41
virtual void noteBodyConsumerAborted(BodyPipe::Pointer)
Definition: ModXact.cc:1280
int const char size_t
Definition: stub_liblog.cc:86
A const & max(A const &lhs, A const &rhs)
void addLastRequestChunk(MemBuf &buf)
Definition: ModXact.cc:370
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:792
void encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &httpBuf, const Http::Message *head)
Definition: ModXact.cc:1585
LogTags logType
the squid request status (TCP_MISS etc)
Definition: History.h:43
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:58
virtual bool inheritProperties(const Http::Message *)=0
void consume(size_t size)
Definition: BodyPipe.cc:309
void stopSending(bool nicely)
Definition: ModXact.cc:615
mb_size_t max_capacity
Definition: MemBuf.h:142
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
void recordXactFinish(int hid)
record the end of a xact identified by its history ID
Definition: History.cc:60
bool bodySizeKnown() const
Definition: BodyPipe.h:109
MemBuf & buf
Definition: BodyPipe.h:74
void wrote(size_t size, bool wroteEof)
Definition: ModXact.cc:1972
bool doneSending() const
Definition: ModXact.cc:609
@ ERR_ICAP_FAILURE
Definition: forward.h:64
void makeAdaptedBodyPipe(const char *what)
Definition: ModXact.cc:1863
@ ICP_INVALID
Definition: icp_opcode.h:15
bool httpHeaderHasConnDir(const HttpHeader *hdr, const SBuf &directive)
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
uint64_t bodySize() const
Definition: BodyPipe.cc:161
virtual void start()
called by AsyncStart; do not call directly
Definition: ModXact.cc:89
int size
Definition: ModDevPoll.cc:76
void parseExtensionValuesWith(ChunkExtensionValueParser *parser)
#define NULL
Definition: types.h:166
const char * rawContent() const
Definition: SBuf.cc:509
virtual void swanSong()
Definition: Launcher.cc:105
void stopWriting(bool nicely)
Definition: ModXact.cc:487
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
virtual void parse(Tokenizer &tok, const SBuf &extName) override
Definition: ModXact.cc:2081
#define MAX_LOGIN_SZ
Definition: defines.h:79
uint64_t producedSize() const
Definition: BodyPipe.h:112
#define true
Definition: GnuRegex.c:234
bool expectHttpHeader() const
whether ICAP response header indicates HTTP header presence
Definition: ModXact.cc:1119
Definition: MemBuf.h:24
bool expectHttpBody() const
whether ICAP response header indicates HTTP body presence
Definition: ModXact.cc:1124
void clean()
Definition: MemBuf.cc:112
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:152
@ scPartialContent
Definition: StatusCode.h:32
int delById(Http::HdrType id)
Definition: HttpHeader.cc:694
@ srcIcap
traditional ICAP service without encryption
Definition: Message.h:41
Adaptation::Icap::History::Pointer icapHistory() const
Returns possibly nil history, creating it if icap logging is enabled.
Definition: HttpRequest.cc:390
@ scCreated
Definition: StatusCode.h:27
void addEntry(HttpHeaderEntry *e)
Definition: HttpHeader.cc:764
void updateSources()
Update the Http::Message sources.
Definition: ModXact.cc:2019
#define assert(EX)
Definition: assert.h:19
char const * termedBuf() const
Definition: SquidString.h:92
void writeSomeBody(const char *label, size_t size)
Definition: ModXact.cc:320
virtual Xaction * createXaction()
Definition: ModXact.cc:2037
@ scContinue
Definition: StatusCode.h:22
virtual void fillPendingStatus(MemBuf &buf) const
Definition: ModXact.cc:1749
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:161
virtual void finalizeLogInfo()
Definition: ModXact.cc:1316
NotePairs::Pointer metaHeaders
Definition: History.h:65
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
void prepareLogWithRequestDetails(HttpRequest *, const AccessLogEntryPointer &)
Definition: client_side.cc:327
void const char int sz
Definition: stub_cbdata.cc:16
const char * virginContentData(const VirginBodyAct &act) const
Definition: ModXact.cc:417
void base64_encode_init(struct base64_encode_ctx *ctx)
Definition: base64.c:232
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:408
virtual bool fillVirginHttpHeader(MemBuf &) const
Definition: ModXact.cc:1986
time_t squid_curtime
Definition: stub_time.cc:17
virtual void callException(const std::exception &e)
called when the job throws during an async call
Definition: Xaction.cc:371
static Notes metaHeaders
The list of configured meta headers.
Definition: Config.h:58
bool isNoAddr() const
Definition: Address.cc:284
const char * mkrfc1123(time_t)
Definition: rfc1123.c:202
void setCause(HttpRequest *r)
Definition: InOut.h:38
virtual void handleCommRead(size_t size)
Definition: ModXact.cc:568
bool canBackupEverything() const
Definition: ModXact.cc:1698
bool expectIcapTrailers() const
whether ICAP response header indicates ICAP trailers presence
Definition: ModXact.cc:1129
static Answer Forward(Http::Message *aMsg)
create an akForward answer
Definition: Answer.cc:26
virtual void finalizeLogInfo()
Definition: Xaction.cc:610
const HttpRequest & virginRequest() const
locates the request, either as a cause or as a virgin message itself
Definition: ModXact.cc:387
HttpRequestMethod method
Definition: HttpRequest.h:114
struct timeval current_time
Definition: stub_time.cc:15
void start(const char *context)
record the start of an ICAP processing interval
Definition: History.cc:23
@ methodRespmod
Definition: Elements.h:17
String protoPrefix
Definition: HttpReply.h:60
const char * status() const
Definition: BodyPipe.cc:446
static const size_t TheBackupLimit
Definition: ModXact.cc:46
void decideWritingAfterPreview(const char *previewKind)
determine state.writing after we wrote the entire preview
Definition: ModXact.cc:293
Definition: parse.c:160
bool hasPair(const SBuf &key, const SBuf &value) const
Definition: Notes.cc:365
squidaio_request_t * head
Definition: aiops.cc:127
virtual void handleCommWrote(size_t size)
Definition: ModXact.cc:209
@ scNoContent
Definition: StatusCode.h:30
@ PROXY_AUTHENTICATE
virtual void startShoveling()
starts sending/receiving ICAP messages
Definition: ModXact.cc:190
an std::runtime_error with thrower location info
Definition: TextException.h:20
char * content()
start of the added data
Definition: MemBuf.h:41
mb_size_t spaceSize() const
Definition: MemBuf.cc:157
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1051
size_type size() const
Definition: SquidString.h:73
@ METHOD_NONE
Definition: MethodType.h:22
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:73
size_t base64_encode_update(struct base64_encode_ctx *ctx, char *dst, size_t length, const uint8_t *src)
Definition: base64.c:265
bool gotEncapsulated(const char *section) const
Definition: ModXact.cc:1814
void add(const SBuf &key, const SBuf &value)
Definition: Notes.cc:312
virtual void swanSong()
Definition: Xaction.cc:571
@ methodReqmod
Definition: Elements.h:17
static char * masterx_shared_name
Definition: Config.h:45
@ scInvalidHeader
Definition: StatusCode.h:86
String ssluser
the username from SSL
Definition: History.h:41
virtual const char * status() const
internal cleanup; do not call directly
Definition: Xaction.cc:633
const XactOutcome xoSatisfied
request satisfaction
Definition: Elements.cc:26
virtual void swanSong()
Definition: ModXact.cc:1294
void finishNullOrEmptyBodyPreview(MemBuf &buf)
Definition: ModXact.cc:1735
virtual void detailError(const ErrorDetail::Pointer &errDetail)
record error detail in the virgin request if possible
Definition: ModXact.cc:1996
virtual void clearError()
clear stored error details, if any; used for retries/repeats
Definition: ModXact.cc:2008
void expect(int64_t aSize)
Definition: ModXact.cc:1879
@ scOkay
Definition: StatusCode.h:26
HttpReply::Pointer icapReply
received ICAP reply, if any
Definition: Xaction.h:64
void recordMeta(const HttpHeader *lm)
store the last meta header fields received from the adaptation service
Definition: History.cc:139
int recordXactStart(const String &serviceId, const timeval &when, bool retrying)
record the start of a xact, return xact history ID
Definition: History.cc:50
bool parse(const char *buf, int len, int atEnd, Http::StatusCode *error)
Definition: ModXact.cc:2069
static StatHist s
#define false
Definition: GnuRegex.c:233
bool parseHead(Http::Message *head)
Definition: ModXact.cc:1110
A const & min(A const &lhs, A const &rhs)
const XactOutcome xoModified
replaced virgin msg with adapted
Definition: Elements.cc:25
int adaptHistoryId
adaptation history slot reservation
Definition: ModXact.h:319
Ip::Address client_addr
Definition: HttpRequest.h:149
void makeUsernameHeader(const HttpRequest *request, MemBuf &buf)
Definition: ModXact.cc:1556
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
virtual void start()
called by AsyncStart; do not call directly
Definition: Xaction.cc:141
size_t debt() const
Definition: ModXact.cc:1966
void updateHistory(bool start)
starts or stops transaction accounting in ICAP history
Definition: ModXact.cc:2052
const CharacterSet crlf("crlf","\r\n")
virtual void noteBodyProductionEnded(BodyPipe::Pointer)
Definition: ModXact.cc:1243
ErrorDetail::Pointer MakeNamedErrorDetail(const char *name)
Definition: Detail.cc:54
@ PROXY_AUTHORIZATION

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors