#include "AccessLogEntry.h"
#include "acl/FilledChecklist.h"
#include "acl/Gadgets.h"
+#include "acl/Tree.h"
#include "client_side.h"
#include "ConfigParser.h"
#include "globals.h"
#include "StrList.h"
#include <algorithm>
+#include <ostream>
#include <string>
Note::Value::~Value()
valueFormat = new Format::Format(descr ? descr : "Notes");
if (!valueFormat->parse(theValue.c_str())) {
delete valueFormat;
- SBuf exceptionMsg;
- exceptionMsg.Printf("failed to parse annotation value %s", theValue.c_str());
- throw TexcHere(exceptionMsg.c_str());
+ throw TextException(ToSBuf("failed to parse annotation value ", theValue), Here());
}
}
}
}
void
-Note::dump(StoreEntry *entry, const char *k)
+Note::printAsNoteDirective(StoreEntry * const entry, const char * const directiveName) const
{
+ PackableStream os(*entry);
for (const auto &v: values) {
- storeAppendPrintf(entry, "%s %.*s %s",
- k, key().length(), key().rawContent(), ConfigParser::QuoteString(SBufToString(v->value())));
- dump_acl_list(entry, v->aclList);
- storeAppendPrintf(entry, "\n");
+ os << directiveName << ' ' << key() << ' ' << ConfigParser::QuoteString(SBufToString(v->value()));
+ if (v->aclList) {
+ // TODO: Use Acl::dump() after fixing the XXX in dump_acl_list().
+ for (const auto &item: v->aclList->treeDump("", &Acl::AllowOrDeny)) {
+ if (item.isEmpty()) // treeDump("") adds this prefix
+ continue;
+ if (item.cmp("\n") == 0) // treeDump() adds this suffix
+ continue;
+ os << ' ' << item; // ACL name
+ }
+ }
+ os << '\n';
}
}
-SBuf
-Note::toString(const char *sep) const
+void
+Note::printAsAnnotationAclParameters(std::ostream &os) const
{
- SBuf result;
- for (const auto &val: values)
- result.appendf("%.*s: %.*s%s", key().length(), key().rawContent(),
- val->value().length(), val->value().rawContent(), sep);
- return result;
+ auto separator = "";
+ for (const auto &v: values) {
+ os << separator;
+ os << key() << (v->method() == Value::mhReplace ? "=" : "+=") << v->value();
+ separator = " ";
+ }
}
const Notes::Keys &
}
void
-Notes::dump(StoreEntry *entry, const char *key)
+Notes::printAsNoteDirectives(StoreEntry *entry, const char * const directiveName) const
{
for (const auto &n: notes)
- n->dump(entry, key);
+ n->printAsNoteDirective(entry, directiveName);
}
-const char *
-Notes::toString(const char *sep) const
+void
+Notes::printAsAnnotationAclParameters(std::ostream &os) const
{
- static SBuf result;
- result.clear();
- for (const auto ¬e: notes)
- result.append(note->toString(sep));
- return result.isEmpty() ? nullptr : result.c_str();
+ const char *separator = "";
+ for (const auto ¬e: notes) {
+ os << separator;
+ note->printAsAnnotationAclParameters(os);
+ separator = " ";
+ }
}
bool
return resultNote.length();
}
-const char *
-NotePairs::toString(const char *sep) const
+void
+NotePairs::print(std::ostream &os, const char * const nameValueSeparator, const char * const entryTerminator) const
{
- static SBuf result;
- result.clear();
for (const auto &e: entries)
- result.appendf("%.*s: %.*s%s", e->name().length(), e->name().rawContent(),
- e->value().length(), e->value().rawContent(), sep);
- return result.isEmpty() ? nullptr : result.c_str();
+ os << e->name() << nameValueSeparator << e->value() << entryTerminator;
}
const char *
#include "acl/forward.h"
#include "base/RefCount.h"
+#include "sbuf/forward.h"
#include "format/Format.h"
#include "mem/forward.h"
#include "SquidString.h"
+#include <iosfwd>
#include <string>
#include <vector>
bool match(HttpRequest *request, HttpReply *reply, const AccessLogEntryPointer &al, SBuf &matched);
const SBuf &key() const { return theKey; }
void updateNotePairs(NotePairsPointer pairs, const CharacterSet *delimiters, const AccessLogEntryPointer &al);
- /// Dump the single Note to the given StoreEntry object.
- void dump(StoreEntry *entry, const char *key);
- /// For the key and all its Values compile a string of
- /// "Key: Value" pairs separated by sep string.
- SBuf toString(const char *sep) const;
+
+ /// Prints key and value(s) using a "note" directive format (including directive name).
+ void printAsNoteDirective(StoreEntry *, const char *directiveName) const;
+
+ /// Prints using "annotate_transaction acl parameter" format, one key=value
+ /// or key+=value parameter per stored value.
+ void printAsAnnotationAclParameters(std::ostream &) const;
private:
SBuf theKey; ///< The note key
/// Parses an annotate line with "key=value" or "key+=value" formats.
void parseKvPair();
- /// Dump the notes list to the given StoreEntry object.
- void dump(StoreEntry *entry, const char *name);
+ /// Prints notes using "note" squid.conf directive format, one directive per stored note.
+ void printAsNoteDirectives(StoreEntry *, const char *directiveName) const;
+
/// clean the notes list
void clean() { notes.clear(); }
iterator end() { return notes.end(); }
/// \returns true if the notes list is empty
bool empty() const { return notes.empty(); }
- /// Convert Notes list to a string consist of "Key: Value"
- /// entries separated by sep string.
- const char *toString(const char *sep = "\r\n") const;
+
+ /// print notes using "annotate_transaction acl parameters" format, one
+ /// key=value parameter per note
+ void printAsAnnotationAclParameters(std::ostream &) const;
+
void updateNotePairs(NotePairsPointer pairs, const CharacterSet *delimiters,
const AccessLogEntryPointer &al);
private:
/// \returns true if the key/value pair is already stored
bool hasPair(const SBuf &key, const SBuf &value) const;
- /// Convert NotePairs list to a string consist of "Key: Value"
- /// entries separated by sep string.
- const char *toString(const char *sep = "\r\n") const;
+ /// Reports all entries (if any), printing exactly four items for each:
+ /// entry name, nameValueSeparator, entry value, and entry terminator.
+ void print(std::ostream &os, const char *nameValueSeparator, const char *entryTerminator) const;
/// \returns true if there are not entries in the list
bool empty() const {return entries.empty();}
#include "debug/Stream.h"
#include "format/Format.h"
#include "sbuf/Algorithms.h"
+#include "sbuf/Stream.h"
ACLAnnotationData::ACLAnnotationData()
: notes(new Notes("annotation_data")) {}
SBufList
ACLAnnotationData::dump() const
{
- SBufList sl;
- if (const char *strNotes = notes->toString())
- sl.push_back(SBuf(strNotes));
- return sl;
+ if (notes->empty())
+ return SBufList();
+
+ SBufStream os;
+ notes->printAsAnnotationAclParameters(os);
+ return SBufList{os.buf()};
}
void
static void dump_note(StoreEntry *entry, const char *name, Notes ¬es)
{
- notes.dump(entry, name);
+ notes.printAsNoteDirectives(entry, name);
}
static void free_note(Notes *notes)
out = sb.c_str();
quote = 1;
} else {
+ // No specific annotation requested. Report all annotations.
+
// if no argument given use default "\r\n" as notes separator
const char *separator = fmt->data.string ? tmp : "\r\n";
+ SBufStream os;
#if USE_ADAPTATION
Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
- if (ah && ah->metaHeaders && !ah->metaHeaders->empty())
- sb.append(ah->metaHeaders->toString(separator));
+ if (ah && ah->metaHeaders)
+ ah->metaHeaders->print(os, ": ", separator);
#endif
- if (al->notes && !al->notes->empty())
- sb.append(al->notes->toString(separator));
+ if (al->notes)
+ al->notes->print(os, ": ", separator);
+ sb = os.buf();
out = sb.c_str();
quote = 1;
}
// dump the helper key=pair "notes" list
if (!r.notes.empty()) {
os << ", notes={";
- os << r.notes.toString("; ");
+ // This simple format matches what most helpers use and is sufficient
+ // for debugging nearly any helper response, but the result differs from
+ // raw helper responses when the helper quotes values or escapes special
+ // characters. See also: Helper::Reply::parseResponseKeys().
+ r.notes.print(os, "=", " ");
os << "}";
}