]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpReply.cc
Simplify appending SBuf to String (#2108)
[thirdparty/squid.git] / src / HttpReply.cc
CommitLineData
cb69b4c7 1/*
1f7b830e 2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
cb69b4c7 7 */
8
bbc27441
AJ
9/* DEBUG: section 58 HTTP Reply (Response) */
10
582c2af2 11#include "squid.h"
1328cfb7 12#include "acl/AclSizeLimit.h"
582c2af2 13#include "acl/FilledChecklist.h"
81ab22b6 14#include "base/EnumIterator.h"
582c2af2 15#include "globals.h"
4f1c93a7 16#include "http/ContentLengthInterpreter.h"
0521f8be 17#include "HttpBody.h"
7ebe76de 18#include "HttpHdrCc.h"
582c2af2 19#include "HttpHdrContRange.h"
25b6a907 20#include "HttpHdrSc.h"
582c2af2 21#include "HttpReply.h"
0667cbfb 22#include "HttpRequest.h"
0eb49b6d 23#include "MemBuf.h"
122a6e3c 24#include "sbuf/Stream.h"
4d5904f7 25#include "SquidConfig.h"
122a6e3c 26#include "SquidMath.h"
582c2af2 27#include "Store.h"
28204b3b 28#include "StrList.h"
cb69b4c7 29
8341f96d
EB
30HttpReply::HttpReply():
31 Http::Message(hoReply),
32 date(0),
33 last_modified(0),
34 expires(0),
35 surrogate_control(nullptr),
36 keep_alive(0),
37 protoPrefix("HTTP/"),
38 bodySizeMax(-2),
39 content_range(nullptr)
cb69b4c7 40{
06a5ae20 41 init();
cb69b4c7 42}
43
06a5ae20 44HttpReply::~HttpReply()
cb69b4c7 45{
06a5ae20 46 if (do_clean)
47 clean();
cb69b4c7 48}
49
50void
06a5ae20 51HttpReply::init()
cb69b4c7 52{
06a5ae20 53 hdrCacheInit();
9b769c67 54 sline.init();
fb654382 55 pstate = Http::Message::psReadyToParseStartLine;
06a5ae20 56 do_clean = true;
cb69b4c7 57}
58
06a5ae20 59void HttpReply::reset()
cb69b4c7 60{
06a5ae20 61
8596962e 62 // reset should not reset the protocol; could have made protoPrefix a
63 // virtual function instead, but it is not clear whether virtual methods
64 // are allowed with MEMPROXY_CLASS() and whether some cbdata void*
65 // conversions are not going to kill virtual tables
30abd221 66 const String pfx = protoPrefix;
06a5ae20 67 clean();
68 init();
69 protoPrefix = pfx;
70}
71
72void
73HttpReply::clean()
74{
26ac0430 75 // we used to assert that the pipe is NULL, but now the message only
5f8252d2 76 // points to a pipe that is owned and initiated by another object.
aee3523a 77 body_pipe = nullptr;
5f8252d2 78
0521f8be 79 body.clear();
06a5ae20 80 hdrCacheClean();
519e0948 81 header.clean();
9b769c67 82 sline.clean();
0667cbfb 83 bodySizeMax = -2; // hack: make calculatedBodySizeMax() false
cb69b4c7 84}
85
cb69b4c7 86void
f8432432 87HttpReply::packHeadersUsingFastPacker(Packable &p) const
cb69b4c7 88{
f8432432
EB
89 sline.packInto(&p);
90 header.packInto(&p);
91 p.append("\r\n", 2);
528b2c61 92}
93
94void
f8432432 95HttpReply::packHeadersUsingSlowPacker(Packable &p) const
528b2c61 96{
f8432432
EB
97 MemBuf buf;
98 buf.init();
99 packHeadersUsingFastPacker(buf);
100 p.append(buf.content(), buf.contentSize());
101}
102
103void
104HttpReply::packInto(MemBuf &buf) const
105{
106 packHeadersUsingFastPacker(buf);
107 body.packInto(&buf);
cb69b4c7 108}
109
06a5ae20 110/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */
032785bf 111MemBuf *
1f28a150 112HttpReply::pack() const
cb69b4c7 113{
032785bf 114 MemBuf *mb = new MemBuf;
2fe7eff9 115 mb->init();
f8432432 116 packInto(*mb);
cb69b4c7 117 return mb;
118}
119
ea55799d
EB
120HttpReplyPointer
121HttpReply::MakeConnectionEstablished() {
122
123 HttpReplyPointer rep(new HttpReply);
124 rep->sline.set(Http::ProtocolVersion(), Http::scOkay, "Connection established");
125 return rep;
126}
127
66d51f4f 128HttpReplyPointer
11992b6f 129HttpReply::make304() const
cb69b4c7 130{
789217a2 131 static const Http::HdrType ImsEntries[] = {Http::HdrType::DATE, Http::HdrType::CONTENT_TYPE, Http::HdrType::EXPIRES, Http::HdrType::LAST_MODIFIED, /* eof */ Http::HdrType::OTHER};
62e76326 132
66d51f4f 133 const HttpReplyPointer rv(new HttpReply);
728da2ee 134 int t;
de336bbe 135 HttpHeaderEntry *e;
cb69b4c7 136
528b2c61 137 /* rv->content_length; */
06a5ae20 138 rv->date = date;
139 rv->last_modified = last_modified;
140 rv->expires = expires;
141 rv->content_type = content_type;
528b2c61 142 /* rv->content_range */
143 /* rv->keep_alive */
aee3523a 144 rv->sline.set(Http::ProtocolVersion(), Http::scNotModified, nullptr);
62e76326 145
81ab22b6 146 for (t = 0; ImsEntries[t] != Http::HdrType::OTHER; ++t) {
a9925b40 147 if ((e = header.findEntry(ImsEntries[t])))
eede25e7 148 rv->header.addEntry(e->clone());
81ab22b6 149 }
62e76326 150
182faab8
AR
151 if (cache_control)
152 rv->putCc(*cache_control);
0c90d3b1 153
528b2c61 154 /* rv->body */
155 return rv;
156}
157
032785bf 158MemBuf *
1f28a150 159HttpReply::packed304Reply() const
528b2c61 160{
161 /* Not as efficient as skipping the header duplication,
162 * but easier to maintain
163 */
66d51f4f 164 const auto temp = make304();
06a5ae20 165 MemBuf *rv = temp->pack();
528b2c61 166 return rv;
cb69b4c7 167}
168
169void
955394ce 170HttpReply::setHeaders(Http::StatusCode status, const char *reason,
350e2aec 171 const char *ctype, int64_t clen, time_t lmt, time_t expiresTime)
cb69b4c7 172{
173 HttpHeader *hdr;
2592bc70 174 sline.set(Http::ProtocolVersion(), status, reason);
06a5ae20 175 hdr = &header;
789217a2
FC
176 hdr->putStr(Http::HdrType::SERVER, visible_appname_string);
177 hdr->putStr(Http::HdrType::MIME_VERSION, "1.0");
178 hdr->putTime(Http::HdrType::DATE, squid_curtime);
62e76326 179
d8b249ef 180 if (ctype) {
789217a2 181 hdr->putStr(Http::HdrType::CONTENT_TYPE, ctype);
06a5ae20 182 content_type = ctype;
d8b249ef 183 } else
30abd221 184 content_type = String();
62e76326 185
de336bbe 186 if (clen >= 0)
789217a2 187 hdr->putInt64(Http::HdrType::CONTENT_LENGTH, clen);
62e76326 188
350e2aec 189 if (expiresTime >= 0)
789217a2 190 hdr->putTime(Http::HdrType::EXPIRES, expiresTime);
62e76326 191
f53969cc 192 if (lmt > 0) /* this used to be lmt != 0 @?@ */
789217a2 193 hdr->putTime(Http::HdrType::LAST_MODIFIED, lmt);
62e76326 194
06a5ae20 195 date = squid_curtime;
62e76326 196
06a5ae20 197 content_length = clen;
62e76326 198
350e2aec 199 expires = expiresTime;
62e76326 200
06a5ae20 201 last_modified = lmt;
cb69b4c7 202}
203
6d38ef86 204void
955394ce 205HttpReply::redirect(Http::StatusCode status, const char *loc)
6d38ef86 206{
207 HttpHeader *hdr;
aee3523a 208 sline.set(Http::ProtocolVersion(), status, nullptr);
06a5ae20 209 hdr = &header;
c81de627 210 hdr->putStr(Http::HdrType::SERVER, visible_appname_string);
789217a2
FC
211 hdr->putTime(Http::HdrType::DATE, squid_curtime);
212 hdr->putInt64(Http::HdrType::CONTENT_LENGTH, 0);
213 hdr->putStr(Http::HdrType::LOCATION, loc);
06a5ae20 214 date = squid_curtime;
215 content_length = 0;
6d38ef86 216}
217
528b2c61 218/* compare the validators of two replies.
219 * 1 = they match
220 * 0 = they do not match
221 */
222int
06a5ae20 223HttpReply::validatorsMatch(HttpReply const * otherRep) const
62e76326 224{
30abd221 225 String one,two;
06a5ae20 226 assert (otherRep);
528b2c61 227 /* Numbers first - easiest to check */
228 /* Content-Length */
229 /* TODO: remove -1 bypass */
62e76326 230
06a5ae20 231 if (content_length != otherRep->content_length
232 && content_length > -1 &&
62e76326 233 otherRep->content_length > -1)
234 return 0;
235
528b2c61 236 /* ETag */
789217a2 237 one = header.getStrOrList(Http::HdrType::ETAG);
62e76326 238
789217a2 239 two = otherRep->header.getStrOrList(Http::HdrType::ETAG);
62e76326 240
a1377698 241 if (one.size()==0 || two.size()==0 || one.caseCmp(two)!=0 ) {
30abd221 242 one.clean();
243 two.clean();
62e76326 244 return 0;
528b2c61 245 }
62e76326 246
06a5ae20 247 if (last_modified != otherRep->last_modified)
62e76326 248 return 0;
249
528b2c61 250 /* MD5 */
789217a2 251 one = header.getStrOrList(Http::HdrType::CONTENT_MD5);
62e76326 252
789217a2 253 two = otherRep->header.getStrOrList(Http::HdrType::CONTENT_MD5);
62e76326 254
a1377698 255 if (one.size()==0 || two.size()==0 || one.caseCmp(two)!=0 ) {
30abd221 256 one.clean();
257 two.clean();
62e76326 258 return 0;
528b2c61 259 }
62e76326 260
528b2c61 261 return 1;
262}
263
66d51f4f
AR
264HttpReply::Pointer
265HttpReply::recreateOnNotModified(const HttpReply &reply304) const
cb69b4c7 266{
66d51f4f
AR
267 // If enough 304s do not update, then this expensive checking is cheaper
268 // than blindly storing reply prefix identical to the already stored one.
269 if (!header.needUpdate(&reply304.header))
270 return nullptr;
271
272 const Pointer cloned = clone();
273 cloned->header.update(&reply304.header);
274 cloned->hdrCacheClean();
275 cloned->header.compact();
276 cloned->hdrCacheInit();
277 return cloned;
cb69b4c7 278}
279
d8b249ef 280/* internal routines */
cb69b4c7 281
06a5ae20 282time_t
283HttpReply::hdrExpirationTime()
d20b1cd0 284{
285 /* The s-maxage and max-age directive takes priority over Expires */
62e76326 286
06a5ae20 287 if (cache_control) {
810d879f
EB
288 int maxAge = -1;
289 /*
290 * Conservatively handle the case when we have a max-age
291 * header, but no Date for reference?
292 */
293 if (cache_control->hasSMaxAge(&maxAge) || cache_control->hasMaxAge(&maxAge))
294 return (date >= 0) ? date + maxAge : squid_curtime;
d20b1cd0 295 }
62e76326 296
f66a9ef4 297 if (Config.onoff.vary_ignore_expire &&
789217a2
FC
298 header.has(Http::HdrType::VARY)) {
299 const time_t d = header.getTime(Http::HdrType::DATE);
300 const time_t e = header.getTime(Http::HdrType::EXPIRES);
62e76326 301
302 if (d == e)
303 return -1;
f66a9ef4 304 }
62e76326 305
789217a2
FC
306 if (header.has(Http::HdrType::EXPIRES)) {
307 const time_t e = header.getTime(Http::HdrType::EXPIRES);
62e76326 308 /*
309 * HTTP/1.0 says that robust implementations should consider
310 * bad or malformed Expires header as equivalent to "expires
311 * immediately."
312 */
313 return e < 0 ? squid_curtime : e;
d20b1cd0 314 }
62e76326 315
d20b1cd0 316 return -1;
317}
318
d8b249ef 319/* sync this routine when you update HttpReply struct */
8596962e 320void
07947ad8 321HttpReply::hdrCacheInit()
cb69b4c7 322{
63df1d28 323 Http::Message::hdrCacheInit();
07947ad8 324
4a1acc56 325 http_ver = sline.version;
789217a2
FC
326 content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH);
327 date = header.getTime(Http::HdrType::DATE);
328 last_modified = header.getTime(Http::HdrType::LAST_MODIFIED);
a9925b40 329 surrogate_control = header.getSc();
8341f96d
EB
330 content_range = (sline.status() == Http::scPartialContent) ?
331 header.getContRange() : nullptr;
4a1acc56 332 keep_alive = persistent() ? 1 : 0;
789217a2 333 const char *str = header.getStr(Http::HdrType::CONTENT_TYPE);
62e76326 334
d8b249ef 335 if (str)
2fe0439c 336 content_type.assign(str, strcspn(str, ";\t "));
d8b249ef 337 else
30abd221 338 content_type = String();
62e76326 339
d20b1cd0 340 /* be sure to set expires after date and cache-control */
06a5ae20 341 expires = hdrExpirationTime();
cb69b4c7 342}
343
8341f96d
EB
344const HttpHdrContRange *
345HttpReply::contentRange() const
346{
347 assert(!content_range || sline.status() == Http::scPartialContent);
348 return content_range;
349}
350
d8b249ef 351/* sync this routine when you update HttpReply struct */
06a5ae20 352void
353HttpReply::hdrCacheClean()
2ac76861 354{
30abd221 355 content_type.clean();
62e76326 356
06a5ae20 357 if (cache_control) {
3d7782c1 358 delete cache_control;
aee3523a 359 cache_control = nullptr;
07947ad8 360 }
62e76326 361
06a5ae20 362 if (surrogate_control) {
45a58345 363 delete surrogate_control;
aee3523a 364 surrogate_control = nullptr;
07947ad8 365 }
43ae1d95 366
06a5ae20 367 if (content_range) {
3c670b50 368 delete content_range;
aee3523a 369 content_range = nullptr;
07947ad8 370 }
63259c34 371}
cb69b4c7 372
35282fbf 373/*
374 * Returns the body size of a HTTP response
375 */
47f6e231 376int64_t
60745f24 377HttpReply::bodySize(const HttpRequestMethod& method) const
35282fbf 378{
06a5ae20 379 if (sline.version.major < 1)
1bda350e 380 return -1;
c2a7cefd 381 else if (method.id() == Http::METHOD_HEAD)
62e76326 382 return 0;
9b769c67 383 else if (sline.status() == Http::scOkay)
f53969cc 384 (void) 0; /* common case, continue */
9b769c67 385 else if (sline.status() == Http::scNoContent)
62e76326 386 return 0;
9b769c67 387 else if (sline.status() == Http::scNotModified)
62e76326 388 return 0;
9b769c67 389 else if (sline.status() < Http::scOkay)
62e76326 390 return 0;
391
06a5ae20 392 return content_length;
35282fbf 393}
8596962e 394
96ee497f
AJ
395/**
396 * Checks the first line of an HTTP Reply is valid.
397 * currently only checks "HTTP/" exists.
398 *
399 * NP: not all error cases are detected yet. Some are left for detection later in parse.
400 */
401bool
84ae6223 402HttpReply::sanityCheckStartLine(const char *buf, const size_t hdr_len, Http::StatusCode *error)
8596962e 403{
96ee497f
AJ
404 // hack warning: using psize instead of size here due to type mismatches with MemBuf.
405
406 // content is long enough to possibly hold a reply
407 // 4 being magic size of a 3-digit number plus space delimiter
84ae6223 408 if (hdr_len < (size_t)(protoPrefix.psize() + 4)) {
0246f6b8 409 if (hdr_len > 0) {
84ae6223 410 debugs(58, 3, "Too small reply header (" << hdr_len << " bytes)");
955394ce 411 *error = Http::scInvalidHeader;
0246f6b8 412 }
96ee497f
AJ
413 return false;
414 }
415
e77d7ef0 416 int pos;
96ee497f 417 // catch missing or mismatched protocol identifier
e77d7ef0 418 // allow special-case for ICY protocol (non-HTTP identifier) in response to faked HTTP request.
84ae6223 419 if (strncmp(buf, "ICY", 3) == 0) {
e77d7ef0
AJ
420 protoPrefix = "ICY";
421 pos = protoPrefix.psize();
dd20bfd3 422 } else {
8596962e 423
84ae6223
AJ
424 if (protoPrefix.cmp(buf, protoPrefix.size()) != 0) {
425 debugs(58, 3, "missing protocol prefix (" << protoPrefix << ") in '" << buf << "'");
955394ce 426 *error = Http::scInvalidHeader;
e77d7ef0
AJ
427 return false;
428 }
96ee497f 429
e77d7ef0
AJ
430 // catch missing or negative status value (negative '-' is not a digit)
431 pos = protoPrefix.psize();
dd20bfd3 432
2f8abb64 433 // skip arbitrary number of digits and a dot in the version portion
84ae6223 434 while ((size_t)pos <= hdr_len && (*(buf+pos) == '.' || xisdigit(*(buf+pos)) ) ) ++pos;
96ee497f 435
e77d7ef0
AJ
436 // catch missing version info
437 if (pos == protoPrefix.psize()) {
84ae6223 438 debugs(58, 3, "missing protocol version numbers (ie. " << protoPrefix << "/1.0) in '" << buf << "'");
955394ce 439 *error = Http::scInvalidHeader;
e77d7ef0
AJ
440 return false;
441 }
96ee497f
AJ
442 }
443
444 // skip arbitrary number of spaces...
84ae6223 445 while ((size_t)pos <= hdr_len && (char)*(buf+pos) == ' ') ++pos;
96ee497f 446
84ae6223
AJ
447 if ((size_t)pos < hdr_len && !xisdigit(*(buf+pos))) {
448 debugs(58, 3, "missing or invalid status number in '" << buf << "'");
955394ce 449 *error = Http::scInvalidHeader;
96ee497f
AJ
450 return false;
451 }
452
8596962e 453 return true;
454}
455
9b769c67
AJ
456bool
457HttpReply::parseFirstLine(const char *blk_start, const char *blk_end)
429f7150 458{
9b769c67 459 return sline.parse(protoPrefix, blk_start, blk_end);
429f7150 460}
5c09dcb8 461
122a6e3c
AR
462size_t
463HttpReply::parseTerminatedPrefix(const char * const terminatedBuf, const size_t bufSize)
464{
465 auto error = Http::scNone;
466 const bool eof = false; // TODO: Remove after removing atEnd from HttpHeader::parse()
467 if (parse(terminatedBuf, bufSize, eof, &error)) {
468 debugs(58, 7, "success after accumulating " << bufSize << " bytes and parsing " << hdr_sz);
469 Assure(pstate == Http::Message::psParsed);
470 Assure(hdr_sz > 0);
471 Assure(!Less(bufSize, hdr_sz)); // cannot parse more bytes than we have
472 return hdr_sz; // success
473 }
474
475 Assure(pstate != Http::Message::psParsed);
476 hdr_sz = 0;
477
478 if (error) {
479 throw TextException(ToSBuf("failed to parse HTTP headers",
480 Debug::Extra, "parser error code: ", error,
481 Debug::Extra, "accumulated unparsed bytes: ", bufSize,
482 Debug::Extra, "reply_header_max_size: ", Config.maxReplyHeaderSize),
483 Here());
484 }
485
486 debugs(58, 3, "need more bytes after accumulating " << bufSize << " out of " << Config.maxReplyHeaderSize);
487
488 // the parse() call above enforces Config.maxReplyHeaderSize limit
489 // XXX: Make this a strict comparison after fixing Http::Message::parse() enforcement
490 Assure(bufSize <= Config.maxReplyHeaderSize);
491 return 0; // parsed nothing, need more data
492}
493
55e1c6e8
EB
494size_t
495HttpReply::prefixLen() const
496{
497 return sline.packedLength() + header.len + 2;
498}
499
4f1c93a7
EB
500void
501HttpReply::configureContentLengthInterpreter(Http::ContentLengthInterpreter &interpreter)
502{
503 interpreter.applyStatusCodeRules(sline.status());
504}
505
506bool
507HttpReply::parseHeader(Http1::Parser &hp)
508{
509 Http::ContentLengthInterpreter clen;
510 return Message::parseHeader(hp, clen);
511}
512
fb525683 513/* handy: resets and returns -1 */
514int
515HttpReply::httpMsgParseError()
516{
63df1d28 517 int result(Http::Message::httpMsgParseError());
fb525683 518 /* indicate an error in the status line */
2592bc70 519 sline.set(Http::ProtocolVersion(), Http::scInvalidHeader);
fb525683 520 return result;
521}
522
5c09dcb8 523/*
524 * Indicate whether or not we would usually expect an entity-body
525 * along with this response
526 */
527bool
60745f24 528HttpReply::expectingBody(const HttpRequestMethod& req_method, int64_t& theSize) const
5c09dcb8 529{
530 bool expectBody = true;
531
c2a7cefd 532 if (req_method == Http::METHOD_HEAD)
5c09dcb8 533 expectBody = false;
9b769c67 534 else if (sline.status() == Http::scNoContent)
5c09dcb8 535 expectBody = false;
9b769c67 536 else if (sline.status() == Http::scNotModified)
5c09dcb8 537 expectBody = false;
26e3e640 538 // TODO: Consider assuming that gray-area 0xx responses have bodies, like 9xx responses.
9b769c67 539 else if (sline.status() < Http::scOkay)
5c09dcb8 540 expectBody = false;
541 else
542 expectBody = true;
543
544 if (expectBody) {
c3d0ba0c 545 if (header.chunked())
5c09dcb8 546 theSize = -1;
547 else if (content_length >= 0)
548 theSize = content_length;
549 else
550 theSize = -1;
551 }
552
553 return expectBody;
554}
0667cbfb 555
556bool
557HttpReply::receivedBodyTooLarge(HttpRequest& request, int64_t receivedSize)
558{
559 calcMaxBodySize(request);
bf95c10a 560 debugs(58, 3, receivedSize << " >? " << bodySizeMax);
0667cbfb 561 return bodySizeMax >= 0 && receivedSize > bodySizeMax;
562}
563
564bool
565HttpReply::expectedBodyTooLarge(HttpRequest& request)
566{
567 calcMaxBodySize(request);
bf95c10a 568 debugs(58, 7, "bodySizeMax=" << bodySizeMax);
0667cbfb 569
570 if (bodySizeMax < 0) // no body size limit
571 return false;
572
573 int64_t expectedSize = -1;
574 if (!expectingBody(request.method, expectedSize))
575 return false;
26ac0430 576
bf95c10a 577 debugs(58, 6, expectedSize << " >? " << bodySizeMax);
0667cbfb 578
579 if (expectedSize < 0) // expecting body of an unknown length
580 return false;
581
582 return expectedSize > bodySizeMax;
583}
584
585void
b248c2a3 586HttpReply::calcMaxBodySize(HttpRequest& request) const
0667cbfb 587{
588 // hack: -2 is used as "we have not calculated max body size yet" state
589 if (bodySizeMax != -2) // already tried
590 return;
591 bodySizeMax = -1;
592
4194f58d
AJ
593 // short-circuit ACL testing if there are none configured
594 if (!Config.ReplyBodySize)
595 return;
596
e94ff527 597 ACLFilledChecklist ch(nullptr, &request);
b1c2ea7a 598 ch.updateReply(this);
1328cfb7 599 for (AclSizeLimit *l = Config.ReplyBodySize; l; l = l -> next) {
b50e327b 600 /* if there is no ACL list or if the ACLs listed match use this size value */
06bf5384 601 if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
bf95c10a 602 debugs(58, 4, "bodySizeMax=" << bodySizeMax);
0667cbfb 603 bodySizeMax = l->size; // may be -1
604 break;
605 }
606 }
607}
da33c835 608
fa0e6114 609// XXX: check that this is sufficient for eCAP cloning
da33c835
HN
610HttpReply *
611HttpReply::clone() const
612{
613 HttpReply *rep = new HttpReply();
66363092 614 rep->sline = sline; // used in hdrCacheInit() call below
da33c835
HN
615 rep->header.append(&header);
616 rep->hdrCacheInit();
617 rep->hdr_sz = hdr_sz;
f230832f
HN
618 rep->http_ver = http_ver;
619 rep->pstate = pstate;
fa0e6114
AR
620 rep->body_pipe = body_pipe;
621
66363092 622 // keep_alive is handled in hdrCacheInit()
da33c835
HN
623 return rep;
624}
d67acb4e 625
63df1d28
AJ
626bool
627HttpReply::inheritProperties(const Http::Message *aMsg)
d67acb4e
AJ
628{
629 const HttpReply *aRep = dynamic_cast<const HttpReply*>(aMsg);
26ac0430
AJ
630 if (!aRep)
631 return false;
d67acb4e 632 keep_alive = aRep->keep_alive;
88df846b 633 sources = aRep->sources;
d67acb4e
AJ
634 return true;
635}
c679653d 636
eace013e
EB
637bool
638HttpReply::olderThan(const HttpReply *them) const
639{
640 if (!them || !them->date || !date)
641 return false;
642 return date < them->date;
643}
644
4f1c93a7
EB
645void
646HttpReply::removeIrrelevantContentLength() {
647 if (Http::ProhibitsContentLength(sline.status()))
648 if (header.delById(Http::HdrType::CONTENT_LENGTH))
649 debugs(58, 3, "Removing unexpected Content-Length header");
650}
651