How to read headers?

Asked by Evgeny Petrov on 2017-10-20

Hello! Could you please provide a simple snippet how to use NamedValueVisitor to iterate over list of headers in request/response?
I'm a bit confused, how to get list of headers and their "size" (number of headers).

Question information

Language:
English Edit question
Status:
Solved
For:
eCAP Edit question
Assignee:
No assignee Edit question
Solved by:
Alex Rousskov
Solved:
2017-10-23
Last query:
2017-10-23
Last reply:
2017-10-23
Alex Rousskov (rousskov) said : #1

I do not have a ready-to-use snippet to paste here, but here are a few existing examples of NamedValueVisitor API usage:

* Adaptation::Ecap::XactionRep::start() in Squid uses the NamedValueVisitor API to iterate over a list of meta headers in the adapter transaction. You might be asking about iteration of HTTP headers rather than meta headers, but both cases use the same kind of API. For example, to iterate virgin HTTP headers, just call Header::visitEach() instead of calling Xaction::visitEachOption(), where the Header object is the result of host::Xaction::virgin().header().

* Adapter::Xaction::visitEachOption() in ClamAV adapter uses the NamedValueVisitor API to report the virus ID meta header to the host application. This code works in the opposite direction of what you are looking for (i.e., the host application iterating adapted transaction meta headers) but the direction does not really matter as far as APIs are concerned.

* The Cfgtor code in sample adapter_modifying.cc uses the NamedValueVisitor API to iterate over each service configuration option. The ClamAV adapter has similar code as well. These example work with service configuration values but they use the same kind of API as transaction headers.

> and their "size" (number of headers)

The visiting API does not give you the number of header fields directly, but you can count the number of times your visitor object has been called, of course.

If you write a simple snippet suitable for sharing with others, please post it here!

Evgeny Petrov (groosha) said : #2

I really don't understand how this NamedValueVisitor works. I see no real code (only objects/functions definitions) in Squid source. Maybe I'm looking for wrong strings?

In adapter_modifying, there is visitEachOption method (in Xaction), however it's empty and no idea how it is used.
There is also class Cfgtor: public libecap::NamedValueVisitor and I cloned it, however Cfgtor itself has Service as argument in configure/reconfigure methods (which are not used at all! reconfigure() calls configure() and configure() isn't called anywhere)

Anyway, instead of
class Cfgtor: public libecap::NamedValueVisitor {
 public:
  Cfgtor(Service &aSvc): svc(aSvc) {}
  virtual void visit(const libecap::Name &name, const libecap::Area &value) {
   svc.setOne(name, value);
  }
  Service &svc;
};

I created
class HeaderReader: public libecap::NamedValueVisitor {
    public:
  HeaderReader(Xaction &xAct): xact(xAct) {}
        virtual void visit(const libecap::Name &name, const libecap::Area &value)
        {
   xact.writeHeader.writeHeader(name, value);
            //svc.setOne(name, value);
        }
        Xaction &xact;
    };

SetOne() defined as
void Adapter::Service::setOne(const libecap::Name &name, const libecap::Area &valArea) {
 const std::string value = valArea.toString();
 if (name == "victim")
  setVictim(value);
 else
 if (name == "replacement")
  replacement = value; // no checks needed, even an empty value is OK
 else
 if (name.assignedHostId())
  ; // skip host-standard options we do not know or care about
 else
  throw libecap::TextException(CfgErrorPrefix +
   "unsupported configuration parameter: " + name.image());
}

I cloned as
void Adapter::Xaction::writeHeader(const libecap::Name &name, const libecap::Area &valArea) {
 const std::string value = valArea.toString();
 ofs << value;
}
where ofs is my opened file instance opened earlier.

And instead of
void Adapter::Xaction::visitEachOption(libecap::NamedValueVisitor &) const {
 // this transaction has no meta-information to pass to the visitor
}

I wrote
void Adapter::Xaction::readHeaders(libecap::NamedValueVisitor &) const {
 HttpHeaderPos pos = HttpHeaderInitPos;
  while (HttpHeaderEntry *e = theHeader.getEntry(&pos)) {
   const Name name(std::string(e->name.rawContent(), e->name.length())); // optimize: find std Names
   name.assignHostId(e->id);
   visitor.visit(name, Value(e->value.rawBuf(), e->value.size()));
 }
}

But now I have no idea how to call any of these new functions :(
Cfgtor is called in

void Adapter::Service::configure(const libecap::Options &cfg) {
 Cfgtor cfgtor(*this);
 cfg.visitEachOption(cfgtor);
       ....
}

And I suppose I should call
 if (hostx->virgin().headers()) {
 HeaderReader hr(hostx->virgin().headers());
 }

but it doesn't work and I definetely miss something :(

Evgeny Petrov (groosha) said : #3

All examples have visitEachOption method however it's empty and not even called anywhere.

when I try to call libecap::Header &header = hostx().virgin().header(); (as seen in Clamav's adapter Xaction.cc)

My program doesn't compile at all.

Evgeny Petrov (groosha) said : #4

Oh, and strangely programm doesn't compile if I try to access hostx->virgin().header();

It says "no matching function to call". So where are headers stored?

Evgeny Petrov (groosha) said : #5

Well, for now I solved my problem using the following code:

        ofstream ofs;
 ofs.open("file.txt", std::ofstream::app);

 libecap::Area area = hostx->virgin().header().image();
 ofs << area << std::endl;

however I still don't understand how to properly call NamedVisitor and how to access headers in proper way.

Best Alex Rousskov (rousskov) said : #6

Please note that you are asking questions about a standard design pattern:
https://en.wikipedia.org/wiki/Visitor_pattern

Copy-pasting code and programming by trial and error often "works", but understanding basic design patterns may lower overheads a lot!

This Q&A interface is not a good place to teach or learn programming, but I will answer one specific question:

> But now I have no idea how to call any of these new functions

To read virgin HTTP header fields in your Adapter transaction, you can do something like this:

  HeaderReader reader(*this);
  hostx->virgin().header().visitEach(reader);

> And instead of Adapter::Xaction::visitEachOption
> I wrote void Adapter::Xaction::readHeaders

Replacing a virtual method called A with your own method called B is usually not what you want to do. Virtual methods are a part of the API. You either ignore them or implement/override them, but you cannot rename them (without changing the API). This concept is OO programming 101, which we should not be discussing here. In your particular case, the Xaction::visitEachOption() method is for supplying adapted meta headers to the host application. Your question is unrelated to that method, so you should leave it as is.

Evgeny Petrov (groosha) said : #7

Thanks Alex Rousskov, that solved my question.