]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/icap/Xaction.cc
Roll Comm::Connection into client-facing
[thirdparty/squid.git] / src / adaptation / icap / Xaction.cc
CommitLineData
774c051c 1/*
507d0a78 2 * DEBUG: section 93 ICAP (RFC 3507) Client
774c051c 3 */
4
5#include "squid.h"
6#include "comm.h"
d6327017 7#include "comm/Connection.h"
aed188fd 8#include "comm/ConnOpener.h"
bd7f2ede 9#include "CommCalls.h"
5f8252d2 10#include "HttpMsg.h"
26cc52cb 11#include "adaptation/icap/Xaction.h"
3ff65596 12#include "adaptation/icap/Launcher.h"
26cc52cb 13#include "adaptation/icap/Config.h"
3d93a84d 14#include "base/TextException.h"
781ce8ff 15#include "pconn.h"
3ff65596
AR
16#include "HttpRequest.h"
17#include "HttpReply.h"
18#include "acl/FilledChecklist.h"
19#include "icap_log.h"
781ce8ff 20#include "fde.h"
3ff65596 21#include "SquidTime.h"
781ce8ff 22
23static PconnPool *icapPconnPool = new PconnPool("ICAP Servers");
774c051c 24
5f8252d2 25
26cc52cb 26//CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Icap, Xaction);
5f8252d2 27
26cc52cb 28Adaptation::Icap::Xaction::Xaction(const char *aTypeName, Adaptation::Initiator *anInitiator, Adaptation::Icap::ServiceRep::Pointer &aService):
bd7f2ede 29 AsyncJob(aTypeName),
a22e6cd3 30 Adaptation::Initiate(aTypeName, anInitiator),
3ff65596
AR
31 icapRequest(NULL),
32 icapReply(NULL),
33 attempts(0),
2413d60a 34 connection(NULL),
a22e6cd3 35 theService(aService),
774c051c 36 commBuf(NULL), commBufSize(0),
37 commEof(false),
2dfede9e 38 reuseConnection(true),
c824c43b 39 isRetriable(true),
3ff65596 40 isRepeatable(true),
cfc68405 41 ignoreLastWrite(false),
c824c43b 42 connector(NULL), reader(NULL), writer(NULL), closer(NULL)
774c051c 43{
5f8252d2 44 debugs(93,3, typeName << " constructed, this=" << this <<
9e008dda 45 " [icapx" << id << ']'); // we should not call virtual status() here
3ff65596
AR
46 icapRequest = HTTPMSGLOCK(new HttpRequest);
47 icap_tr_start = current_time;
774c051c 48}
49
26cc52cb 50Adaptation::Icap::Xaction::~Xaction()
774c051c 51{
5f8252d2 52 debugs(93,3, typeName << " destructed, this=" << this <<
9e008dda 53 " [icapx" << id << ']'); // we should not call virtual status() here
3ff65596
AR
54 HTTPMSGUNLOCK(icapRequest);
55 HTTPMSGUNLOCK(icapReply);
5f8252d2 56}
57
26cc52cb
AR
58Adaptation::Icap::ServiceRep &
59Adaptation::Icap::Xaction::service()
0bef8dd7 60{
a22e6cd3
AR
61 Must(theService != NULL);
62 return *theService;
0bef8dd7
AR
63}
64
26cc52cb 65void Adaptation::Icap::Xaction::disableRetries()
9e008dda 66{
3ff65596
AR
67 debugs(93,5, typeName << (isRetriable ? " from now on" : " still") <<
68 " cannot be retried " << status());
c824c43b 69 isRetriable = false;
70}
71
3ff65596
AR
72void Adaptation::Icap::Xaction::disableRepeats(const char *reason)
73{
74 debugs(93,5, typeName << (isRepeatable ? " from now on" : " still") <<
75 " cannot be repeated because " << reason << status());
76 isRepeatable = false;
77}
78
26cc52cb 79void Adaptation::Icap::Xaction::start()
5f8252d2 80{
0bef8dd7 81 Adaptation::Initiate::start();
5f8252d2 82
83 readBuf.init(SQUID_TCP_SO_RCVBUF, SQUID_TCP_SO_RCVBUF);
84 commBuf = (char*)memAllocBuf(SQUID_TCP_SO_RCVBUF, &commBufSize);
85 // make sure maximum readBuf space does not exceed commBuf size
86 Must(static_cast<size_t>(readBuf.potentialSpaceSize()) <= commBufSize);
774c051c 87}
88
89// TODO: obey service-specific, OPTIONS-reported connection limit
26cc52cb 90void Adaptation::Icap::Xaction::openConnection()
774c051c 91{
b7ac5457 92 Ip::Address client_addr;
7aa6b7cb 93
aed188fd 94 Must(!haveConnection());
c824c43b 95
0bef8dd7 96 const Adaptation::Service &s = service();
774c051c 97
26cc52cb 98 if (!TheConfig.reuse_connections)
560d7d2d 99 disableRetries(); // this will also safely drain pconn pool
100
2413d60a
AJ
101 connection = new Comm::Connection;
102
103 /* NP: set these here because it applies whether a pconn or a new conn is used */
104
aed188fd 105 // TODO: Avoid blocking lookup if s.cfg().host is a hostname
2413d60a
AJ
106 connection->remote = s.cfg().host.termedBuf();
107 connection->remote.SetPort(s.cfg().port);
108
c8ceec27 109 // TODO: check whether NULL domain is appropriate here
2413d60a
AJ
110 connection->fd = icapPconnPool->pop(s.cfg().host.termedBuf(), s.cfg().port, NULL, client_addr, isRetriable);
111 if (connection->isOpen()) {
112 debugs(93,3, HERE << "reused pconn FD " << connection->fd);
bd7f2ede 113
114 // fake the connect callback
26cc52cb
AR
115 // TODO: can we sync call Adaptation::Icap::Xaction::noteCommConnected here instead?
116 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> Dialer;
117 Dialer dialer(this, &Adaptation::Icap::Xaction::noteCommConnected);
2413d60a 118 dialer.params.fd = connection->fd;
bd7f2ede 119 dialer.params.flag = COMM_OK;
120 // fake other parameters by copying from the existing connection
26cc52cb 121 connector = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommConnected", dialer);
9e008dda 122 ScheduleCallHere(connector);
c8ceec27 123 return;
2dfede9e 124 }
125
c8ceec27 126 disableRetries(); // we only retry pconn failures
127
26cc52cb
AR
128 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> ConnectDialer;
129 connector = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommConnected",
130 ConnectDialer(this, &Adaptation::Icap::Xaction::noteCommConnected));
cfd66529 131
4accd6d8 132 Comm::ConnOpener *cs = new Comm::ConnOpener(connection, connector, TheConfig.connect_timeout(service().cfg().bypass));
aed188fd 133 cs->setHost(s.cfg().host.termedBuf());
294775b5 134 AsyncJob::AsyncStart(cs);
774c051c 135}
136
2dfede9e 137/*
138 * This event handler is necessary to work around the no-rentry policy
26cc52cb 139 * of Adaptation::Icap::Xaction::callStart()
2dfede9e 140 */
bd7f2ede 141#if 0
2dfede9e 142void
26cc52cb 143Adaptation::Icap::Xaction::reusedConnection(void *data)
2dfede9e 144{
192378eb 145 debugs(93, 5, HERE << "reused connection");
26cc52cb 146 Adaptation::Icap::Xaction *x = (Adaptation::Icap::Xaction*)data;
2dfede9e 147 x->noteCommConnected(COMM_OK);
148}
bd7f2ede 149#endif
2dfede9e 150
26cc52cb 151void Adaptation::Icap::Xaction::closeConnection()
774c051c 152{
aed188fd 153 if (haveConnection()) {
774c051c 154
bd7f2ede 155 if (closer != NULL) {
2413d60a 156 comm_remove_close_handler(connection->fd, closer);
774c051c 157 closer = NULL;
158 }
159
c99de607 160 cancelRead(); // may not work
161
5f8252d2 162 if (reuseConnection && !doneWithIo()) {
dd6e6148 163 //status() adds leading spaces.
5f8252d2 164 debugs(93,5, HERE << "not reusing pconn due to pending I/O" << status());
c99de607 165 reuseConnection = false;
166 }
774c051c 167
2dfede9e 168 if (reuseConnection) {
b7ac5457 169 Ip::Address client_addr;
dd6e6148 170 //status() adds leading spaces.
5f8252d2 171 debugs(93,3, HERE << "pushing pconn" << status());
9e008dda 172 AsyncCall::Pointer call = NULL;
2413d60a
AJ
173 commSetTimeout(connection->fd, -1, call);
174 icapPconnPool->push(connection->fd, theService->cfg().host.termedBuf(),
9e008dda 175 theService->cfg().port, NULL, client_addr);
c824c43b 176 disableRetries();
2413d60a 177 connection->fd = -1; // prevent premature real closing.
2dfede9e 178 } else {
dd6e6148 179 //status() adds leading spaces.
5f8252d2 180 debugs(93,3, HERE << "closing pconn" << status());
c99de607 181 // comm_close will clear timeout
2413d60a 182 connection->close();
2dfede9e 183 }
774c051c 184
c99de607 185 writer = NULL;
186 reader = NULL;
774c051c 187 connector = NULL;
5d779c44 188 connection = NULL;
774c051c 189 }
190}
191
192// connection with the ICAP service established
26cc52cb 193void Adaptation::Icap::Xaction::noteCommConnected(const CommConnectCbParams &io)
774c051c 194{
cfd66529
AJ
195 if (io.flag == COMM_TIMEOUT) {
196 handleCommTimedout();
197 return;
198 }
199
bd7f2ede 200 Must(connector != NULL);
774c051c 201 connector = NULL;
c99de607 202
bd7f2ede 203 if (io.flag != COMM_OK)
c99de607 204 dieOnConnectionFailure(); // throws
205
cfd66529
AJ
206 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommCloseCbParams> CloseDialer;
207 closer = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommClosed",
208 CloseDialer(this,&Adaptation::Icap::Xaction::noteCommClosed));
209 comm_add_close_handler(io.conn->fd, closer);
210
211 fd_table[io.conn->fd].noteUse(icapPconnPool);
774c051c 212
213 handleCommConnected();
774c051c 214}
215
26cc52cb 216void Adaptation::Icap::Xaction::dieOnConnectionFailure()
9e008dda 217{
4932ad93 218 debugs(93, 2, HERE << typeName <<
9e008dda 219 " failed to connect to " << service().cfg().uri);
c99de607 220 throw TexcHere("cannot connect to the ICAP service");
221}
222
26cc52cb 223void Adaptation::Icap::Xaction::scheduleWrite(MemBuf &buf)
774c051c 224{
aed188fd
AJ
225 Must(haveConnection());
226
774c051c 227 // comm module will free the buffer
26cc52cb
AR
228 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommIoCbParams> Dialer;
229 writer = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommWrote",
230 Dialer(this, &Adaptation::Icap::Xaction::noteCommWrote));
bd7f2ede 231
2413d60a 232 comm_write_mbuf(connection->fd, &buf, writer);
c99de607 233 updateTimeout();
774c051c 234}
235
26cc52cb 236void Adaptation::Icap::Xaction::noteCommWrote(const CommIoCbParams &io)
774c051c 237{
bd7f2ede 238 Must(writer != NULL);
774c051c 239 writer = NULL;
9e008dda 240
cfc68405 241 if (ignoreLastWrite) {
242 // a hack due to comm inability to cancel a pending write
9e008dda 243 ignoreLastWrite = false;
bd7f2ede 244 debugs(93, 7, HERE << "ignoring last write; status: " << io.flag);
cfc68405 245 } else {
bd7f2ede 246 Must(io.flag == COMM_OK);
3ff65596 247 al.icap.bytesSent += io.size;
cfc68405 248 updateTimeout();
bd7f2ede 249 handleCommWrote(io.size);
cfc68405 250 }
774c051c 251}
252
253// communication timeout with the ICAP service
26cc52cb 254void Adaptation::Icap::Xaction::noteCommTimedout(const CommTimeoutCbParams &io)
774c051c 255{
774c051c 256 handleCommTimedout();
774c051c 257}
258
26cc52cb 259void Adaptation::Icap::Xaction::handleCommTimedout()
774c051c 260{
4932ad93 261 debugs(93, 2, HERE << typeName << " failed: timeout with " <<
9e008dda 262 theService->cfg().methodStr() << " " <<
a7a42b14 263 theService->cfg().uri << status());
fe3e2600 264 reuseConnection = false;
bd7f2ede 265 throw TexcHere(connector != NULL ?
9e008dda
AJ
266 "timed out while connecting to the ICAP service" :
267 "timed out while talking to the ICAP service");
774c051c 268}
269
270// unexpected connection close while talking to the ICAP service
26cc52cb 271void Adaptation::Icap::Xaction::noteCommClosed(const CommCloseCbParams &io)
774c051c 272{
273 closer = NULL;
774c051c 274 handleCommClosed();
774c051c 275}
276
26cc52cb 277void Adaptation::Icap::Xaction::handleCommClosed()
774c051c 278{
279 mustStop("ICAP service connection externally closed");
280}
281
3ff65596
AR
282void Adaptation::Icap::Xaction::callException(const std::exception &e)
283{
284 setOutcome(xoError);
8277060a 285 service().noteFailure();
3ff65596
AR
286 Adaptation::Initiate::callException(e);
287}
288
289
26cc52cb 290void Adaptation::Icap::Xaction::callEnd()
774c051c 291{
c824c43b 292 if (doneWithIo()) {
293 debugs(93, 5, HERE << typeName << " done with I/O" << status());
294 closeConnection();
295 }
0bef8dd7 296 Adaptation::Initiate::callEnd(); // may destroy us
774c051c 297}
298
26cc52cb 299bool Adaptation::Icap::Xaction::doneAll() const
774c051c 300{
0bef8dd7 301 return !connector && !reader && !writer && Adaptation::Initiate::doneAll();
774c051c 302}
303
26cc52cb 304void Adaptation::Icap::Xaction::updateTimeout()
9e008dda 305{
aed188fd
AJ
306 Must(haveConnection());
307
bd7f2ede 308 if (reader != NULL || writer != NULL) {
c99de607 309 // restart the timeout before each I/O
310 // XXX: why does Config.Timeout lacks a write timeout?
cfc68405 311 // TODO: service bypass status may differ from that of a transaction
26cc52cb
AR
312 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommTimeoutCbParams> TimeoutDialer;
313 AsyncCall::Pointer call = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommTimedout",
314 TimeoutDialer(this,&Adaptation::Icap::Xaction::noteCommTimedout));
bd7f2ede 315
2413d60a 316 commSetTimeout(connection->fd,
26cc52cb 317 TheConfig.io_timeout(service().cfg().bypass), call);
c99de607 318 } else {
319 // clear timeout when there is no I/O
320 // Do we need a lifetime timeout?
9e008dda 321 AsyncCall::Pointer call = NULL;
2413d60a 322 commSetTimeout(connection->fd, -1, call);
c99de607 323 }
324}
325
26cc52cb 326void Adaptation::Icap::Xaction::scheduleRead()
774c051c 327{
aed188fd 328 Must(haveConnection());
774c051c 329 Must(!reader);
330 Must(readBuf.hasSpace());
331
774c051c 332 /*
26cc52cb 333 * See comments in Adaptation::Icap::Xaction.h about why we use commBuf
bb790702 334 * here instead of reading directly into readBuf.buf.
774c051c 335 */
26cc52cb
AR
336 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommIoCbParams> Dialer;
337 reader = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommRead",
338 Dialer(this, &Adaptation::Icap::Xaction::noteCommRead));
774c051c 339
2413d60a 340 comm_read(connection->fd, commBuf, readBuf.spaceSize(), reader);
c99de607 341 updateTimeout();
774c051c 342}
343
344// comm module read a portion of the ICAP response for us
26cc52cb 345void Adaptation::Icap::Xaction::noteCommRead(const CommIoCbParams &io)
774c051c 346{
bd7f2ede 347 Must(reader != NULL);
774c051c 348 reader = NULL;
349
bd7f2ede 350 Must(io.flag == COMM_OK);
351 Must(io.size >= 0);
774c051c 352
3ff65596
AR
353 al.icap.bytesRead+=io.size;
354
c99de607 355 updateTimeout();
356
bd7f2ede 357 debugs(93, 3, HERE << "read " << io.size << " bytes");
774c051c 358
359 /*
26cc52cb 360 * See comments in Adaptation::Icap::Xaction.h about why we use commBuf
bb790702 361 * here instead of reading directly into readBuf.buf.
774c051c 362 */
363
bd7f2ede 364 if (io.size > 0) {
365 readBuf.append(commBuf, io.size);
c824c43b 366 disableRetries(); // because pconn did not fail
367 } else {
368 reuseConnection = false;
774c051c 369 commEof = true;
c824c43b 370 }
774c051c 371
bd7f2ede 372 handleCommRead(io.size);
774c051c 373}
374
26cc52cb 375void Adaptation::Icap::Xaction::cancelRead()
774c051c 376{
bd7f2ede 377 if (reader != NULL) {
aed188fd 378 Must(haveConnection());
2413d60a 379 comm_read_cancel(connection->fd, reader);
bd7f2ede 380 reader = NULL;
774c051c 381 }
382}
383
26cc52cb 384bool Adaptation::Icap::Xaction::parseHttpMsg(HttpMsg *msg)
774c051c 385{
def17b6a 386 debugs(93, 5, HERE << "have " << readBuf.contentSize() << " head bytes to parse");
774c051c 387
388 http_status error = HTTP_STATUS_NONE;
389 const bool parsed = msg->parse(&readBuf, commEof, &error);
390 Must(parsed || !error); // success or need more data
391
392 if (!parsed) { // need more data
393 Must(mayReadMore());
394 msg->reset();
395 return false;
396 }
397
398 readBuf.consume(msg->hdr_sz);
399 return true;
400}
401
26cc52cb 402bool Adaptation::Icap::Xaction::mayReadMore() const
774c051c 403{
404 return !doneReading() && // will read more data
405 readBuf.hasSpace(); // have space for more data
406}
407
26cc52cb 408bool Adaptation::Icap::Xaction::doneReading() const
774c051c 409{
410 return commEof;
411}
412
26cc52cb 413bool Adaptation::Icap::Xaction::doneWriting() const
c99de607 414{
415 return !writer;
416}
417
26cc52cb 418bool Adaptation::Icap::Xaction::doneWithIo() const
c99de607 419{
aed188fd 420 return haveConnection() &&
9e008dda
AJ
421 !connector && !reader && !writer && // fast checks, some redundant
422 doneReading() && doneWriting();
c99de607 423}
424
aed188fd
AJ
425bool Adaptation::Icap::Xaction::haveConnection() const
426{
427 return connection != NULL && connection->isOpen();
428}
429
c824c43b 430// initiator aborted
26cc52cb 431void Adaptation::Icap::Xaction::noteInitiatorAborted()
774c051c 432{
c824c43b 433
434 if (theInitiator) {
435 clearInitiator();
436 mustStop("initiator aborted");
c99de607 437 }
c824c43b 438
774c051c 439}
440
3ff65596
AR
441void Adaptation::Icap::Xaction::setOutcome(const Adaptation::Icap::XactOutcome &xo)
442{
443 if (al.icap.outcome != xoUnknown) {
444 debugs(93, 3, HERE << "Warning: reseting outcome: from " <<
e1381638 445 al.icap.outcome << " to " << xo);
3ff65596
AR
446 } else {
447 debugs(93, 4, HERE << xo);
448 }
449 al.icap.outcome = xo;
450}
451
5f8252d2 452// This 'last chance' method is called before a 'done' transaction is deleted.
453// It is wrong to call virtual methods from a destructor. Besides, this call
454// indicates that the transaction will terminate as planned.
26cc52cb 455void Adaptation::Icap::Xaction::swanSong()
774c051c 456{
5f8252d2 457 // kids should sing first and then call the parent method.
458
459 closeConnection(); // TODO: rename because we do not always close
460
461 if (!readBuf.isNull())
462 readBuf.clean();
463
464 if (commBuf)
465 memFreeBuf(commBufSize, commBuf);
774c051c 466
c824c43b 467 if (theInitiator)
3ff65596
AR
468 tellQueryAborted();
469
470 maybeLog();
774c051c 471
0bef8dd7 472 Adaptation::Initiate::swanSong();
774c051c 473}
474
e1381638
AJ
475void Adaptation::Icap::Xaction::tellQueryAborted()
476{
3ff65596
AR
477 Adaptation::Icap::Launcher *l = dynamic_cast<Adaptation::Icap::Launcher*>(theInitiator.ptr());
478 Adaptation::Icap::XactAbortInfo abortInfo(icapRequest, icapReply, retriable(), repeatable());
e1381638 479 CallJob(91, 5, __FILE__, __LINE__,
3ff65596
AR
480 "Adaptation::Icap::Launcher::noteXactAbort",
481 XactAbortCall(l, &Adaptation::Icap::Launcher::noteXactAbort, abortInfo) );
482 clearInitiator();
483}
484
485
e1381638
AJ
486void Adaptation::Icap::Xaction::maybeLog()
487{
488 if (IcapLogfileStatus == LOG_ENABLE) {
3ff65596
AR
489 ACLChecklist *checklist = new ACLFilledChecklist(::Config.accessList.icap, al.request, dash_str);
490 if (!::Config.accessList.icap || checklist->fastCheck()) {
491 finalizeLogInfo();
492 icapLogLog(&al, checklist);
493 }
494 accessLogFreeMemory(&al);
495 delete checklist;
496 }
497}
498
499void Adaptation::Icap::Xaction::finalizeLogInfo()
500{
501 //prepare log data
502 al.icp.opcode = ICP_INVALID;
e1381638 503
3ff65596
AR
504 const Adaptation::Icap::ServiceRep &s = service();
505 al.icap.hostAddr = s.cfg().host.termedBuf();
506 al.icap.serviceName = s.cfg().key;
507 al.icap.reqUri = s.cfg().uri;
e1381638 508
3ff65596
AR
509 al.icap.ioTime = tvSubMsec(icap_tio_start, icap_tio_finish);
510 al.icap.trTime = tvSubMsec(icap_tr_start, current_time);
511
512 al.icap.request = HTTPMSGLOCK(icapRequest);
513 if (icapReply) {
514 al.icap.reply = HTTPMSGLOCK(icapReply);
515 al.icap.resStatus = icapReply->sline.status;
516 }
517}
518
774c051c 519// returns a temporary string depicting transaction status, for debugging
26cc52cb 520const char *Adaptation::Icap::Xaction::status() const
774c051c 521{
522 static MemBuf buf;
523 buf.reset();
524
5f8252d2 525 buf.append(" [", 2);
774c051c 526
527 fillPendingStatus(buf);
528 buf.append("/", 1);
529 fillDoneStatus(buf);
530
5f8252d2 531 buf.Printf(" icapx%d]", id);
774c051c 532
533 buf.terminate();
534
535 return buf.content();
536}
537
26cc52cb 538void Adaptation::Icap::Xaction::fillPendingStatus(MemBuf &buf) const
774c051c 539{
aed188fd 540 if (haveConnection()) {
2413d60a 541 buf.Printf("FD %d", connection->fd);
774c051c 542
bd7f2ede 543 if (writer != NULL)
774c051c 544 buf.append("w", 1);
545
bd7f2ede 546 if (reader != NULL)
774c051c 547 buf.append("r", 1);
548
5f8252d2 549 buf.append(";", 1);
774c051c 550 }
551}
552
26cc52cb 553void Adaptation::Icap::Xaction::fillDoneStatus(MemBuf &buf) const
774c051c 554{
aed188fd 555 if (haveConnection() && commEof)
2413d60a 556 buf.Printf("Comm(%d)", connection->fd);
774c051c 557
558 if (stopReason != NULL)
559 buf.Printf("Stopped");
560}
3cfc19b3 561
26cc52cb 562bool Adaptation::Icap::Xaction::fillVirginHttpHeader(MemBuf &buf) const
3cfc19b3 563{
564 return false;
565}