]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpReply.cc
Various audit updates
[thirdparty/squid.git] / src / HttpReply.cc
CommitLineData
2ac76861 1
cb69b4c7 2/*
123abbe1 3 * DEBUG: section 58 HTTP Reply (Response)
cb69b4c7 4 * AUTHOR: Alex Rousskov
5 *
2b6662ba 6 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 7 * ----------------------------------------------------------
cb69b4c7 8 *
2b6662ba 9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
cb69b4c7 17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
26ac0430 22 *
cb69b4c7 23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
26ac0430 27 *
cb69b4c7 28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
cbdec147 30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 31 *
cb69b4c7 32 */
33
582c2af2 34#include "squid.h"
1328cfb7 35#include "acl/AclSizeLimit.h"
582c2af2
FC
36#include "acl/FilledChecklist.h"
37#include "globals.h"
0521f8be 38#include "HttpBody.h"
7ebe76de 39#include "HttpHdrCc.h"
582c2af2 40#include "HttpHdrContRange.h"
25b6a907 41#include "HttpHdrSc.h"
582c2af2 42#include "HttpReply.h"
0667cbfb 43#include "HttpRequest.h"
0eb49b6d 44#include "MemBuf.h"
4d5904f7 45#include "SquidConfig.h"
582c2af2
FC
46#include "SquidTime.h"
47#include "Store.h"
28204b3b 48#include "StrList.h"
cb69b4c7 49
50/* local constants */
51
1d7ab0f4 52/* If we receive a 304 from the origin during a cache revalidation, we must
53 * update the headers of the existing entry. Specifically, we need to update all
54 * end-to-end headers and not any hop-by-hop headers (rfc2616 13.5.3).
55 *
56 * This is not the whole story though: since it is possible for a faulty/malicious
57 * origin server to set headers it should not in a 304, we must explicitly ignore
58 * these too. Specifically all entity-headers except those permitted in a 304
59 * (rfc2616 10.3.5) must be ignored.
26ac0430 60 *
1d7ab0f4 61 * The list of headers we don't update is made up of:
62 * all hop-by-hop headers
63 * all entity-headers except Expires and Content-Location
64 */
2246b732 65static HttpHeaderMask Denied304HeadersMask;
26ac0430
AJ
66static http_hdr_type Denied304HeadersArr[] = {
67 // hop-by-hop headers
68 HDR_CONNECTION, HDR_KEEP_ALIVE, HDR_PROXY_AUTHENTICATE, HDR_PROXY_AUTHORIZATION,
a1651bad 69 HDR_TE, HDR_TRAILER, HDR_TRANSFER_ENCODING, HDR_UPGRADE,
26ac0430
AJ
70 // entity headers
71 HDR_ALLOW, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, HDR_CONTENT_LENGTH,
72 HDR_CONTENT_MD5, HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_LAST_MODIFIED
73};
2246b732 74
2246b732 75/* module initialization */
76void
9bc73deb 77httpReplyInitModule(void)
2246b732 78{
955394ce 79 assert(Http::scNone == 0); // HttpReply::parse() interface assumes that
97474590 80 httpHeaderMaskInit(&Denied304HeadersMask, 0);
8abf232c 81 httpHeaderCalcMask(&Denied304HeadersMask, Denied304HeadersArr, countof(Denied304HeadersArr));
2246b732 82}
83
26ac0430
AJ
84HttpReply::HttpReply() : HttpMsg(hoReply), date (0), last_modified (0),
85 expires (0), surrogate_control (NULL), content_range (NULL), keep_alive (0),
86 protoPrefix("HTTP/"), bodySizeMax(-2)
cb69b4c7 87{
06a5ae20 88 init();
cb69b4c7 89}
90
06a5ae20 91HttpReply::~HttpReply()
cb69b4c7 92{
06a5ae20 93 if (do_clean)
94 clean();
cb69b4c7 95}
96
97void
06a5ae20 98HttpReply::init()
cb69b4c7 99{
06a5ae20 100 hdrCacheInit();
9b769c67 101 sline.init();
c99de607 102 pstate = psReadyToParseStartLine;
06a5ae20 103 do_clean = true;
cb69b4c7 104}
105
06a5ae20 106void HttpReply::reset()
cb69b4c7 107{
06a5ae20 108
8596962e 109 // reset should not reset the protocol; could have made protoPrefix a
110 // virtual function instead, but it is not clear whether virtual methods
111 // are allowed with MEMPROXY_CLASS() and whether some cbdata void*
112 // conversions are not going to kill virtual tables
30abd221 113 const String pfx = protoPrefix;
06a5ae20 114 clean();
115 init();
116 protoPrefix = pfx;
117}
118
119void
120HttpReply::clean()
121{
26ac0430 122 // we used to assert that the pipe is NULL, but now the message only
5f8252d2 123 // points to a pipe that is owned and initiated by another object.
124 body_pipe = NULL;
125
0521f8be 126 body.clear();
06a5ae20 127 hdrCacheClean();
519e0948 128 header.clean();
9b769c67 129 sline.clean();
0667cbfb 130 bodySizeMax = -2; // hack: make calculatedBodySizeMax() false
cb69b4c7 131}
132
cb69b4c7 133void
06a5ae20 134HttpReply::packHeadersInto(Packer * p) const
cb69b4c7 135{
9b769c67 136 sline.packInto(p);
a9925b40 137 header.packInto(p);
cb69b4c7 138 packerAppend(p, "\r\n", 2);
528b2c61 139}
140
141void
06a5ae20 142HttpReply::packInto(Packer * p)
528b2c61 143{
06a5ae20 144 packHeadersInto(p);
0521f8be 145 body.packInto(p);
cb69b4c7 146}
147
06a5ae20 148/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */
032785bf 149MemBuf *
06a5ae20 150HttpReply::pack()
cb69b4c7 151{
032785bf 152 MemBuf *mb = new MemBuf;
cb69b4c7 153 Packer p;
cb69b4c7 154
2fe7eff9 155 mb->init();
032785bf 156 packerToMemInit(&p, mb);
06a5ae20 157 packInto(&p);
cb69b4c7 158 packerClean(&p);
159 return mb;
160}
161
528b2c61 162HttpReply *
11992b6f 163HttpReply::make304() const
cb69b4c7 164{
62e76326 165 static const http_hdr_type ImsEntries[] = {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER};
166
06a5ae20 167 HttpReply *rv = new HttpReply;
728da2ee 168 int t;
de336bbe 169 HttpHeaderEntry *e;
cb69b4c7 170
528b2c61 171 /* rv->content_length; */
06a5ae20 172 rv->date = date;
173 rv->last_modified = last_modified;
174 rv->expires = expires;
175 rv->content_type = content_type;
528b2c61 176 /* rv->cache_control */
177 /* rv->content_range */
178 /* rv->keep_alive */
9b769c67 179 rv->sline.set(Http::ProtocolVersion(1,1), Http::scNotModified, NULL);
62e76326 180
de336bbe 181 for (t = 0; ImsEntries[t] != HDR_OTHER; ++t)
a9925b40 182 if ((e = header.findEntry(ImsEntries[t])))
eede25e7 183 rv->header.addEntry(e->clone());
62e76326 184
528b2c61 185 /* rv->body */
186 return rv;
187}
188
032785bf 189MemBuf *
06a5ae20 190HttpReply::packed304Reply()
528b2c61 191{
192 /* Not as efficient as skipping the header duplication,
193 * but easier to maintain
194 */
871c031f 195 HttpReply *temp = make304();
06a5ae20 196 MemBuf *rv = temp->pack();
197 delete temp;
528b2c61 198 return rv;
cb69b4c7 199}
200
201void
955394ce 202HttpReply::setHeaders(Http::StatusCode status, const char *reason,
350e2aec 203 const char *ctype, int64_t clen, time_t lmt, time_t expiresTime)
cb69b4c7 204{
205 HttpHeader *hdr;
9b769c67 206 sline.set(Http::ProtocolVersion(1,1), status, reason);
06a5ae20 207 hdr = &header;
a9925b40 208 hdr->putStr(HDR_SERVER, visible_appname_string);
209 hdr->putStr(HDR_MIME_VERSION, "1.0");
210 hdr->putTime(HDR_DATE, squid_curtime);
62e76326 211
d8b249ef 212 if (ctype) {
a9925b40 213 hdr->putStr(HDR_CONTENT_TYPE, ctype);
06a5ae20 214 content_type = ctype;
d8b249ef 215 } else
30abd221 216 content_type = String();
62e76326 217
de336bbe 218 if (clen >= 0)
47f6e231 219 hdr->putInt64(HDR_CONTENT_LENGTH, clen);
62e76326 220
350e2aec
FC
221 if (expiresTime >= 0)
222 hdr->putTime(HDR_EXPIRES, expiresTime);
62e76326 223
2ac76861 224 if (lmt > 0) /* this used to be lmt != 0 @?@ */
a9925b40 225 hdr->putTime(HDR_LAST_MODIFIED, lmt);
62e76326 226
06a5ae20 227 date = squid_curtime;
62e76326 228
06a5ae20 229 content_length = clen;
62e76326 230
350e2aec 231 expires = expiresTime;
62e76326 232
06a5ae20 233 last_modified = lmt;
cb69b4c7 234}
235
6d38ef86 236void
955394ce 237HttpReply::redirect(Http::StatusCode status, const char *loc)
6d38ef86 238{
239 HttpHeader *hdr;
9b769c67 240 sline.set(Http::ProtocolVersion(1,1), status, NULL);
06a5ae20 241 hdr = &header;
7dbca7a4 242 hdr->putStr(HDR_SERVER, APP_FULLNAME);
a9925b40 243 hdr->putTime(HDR_DATE, squid_curtime);
47f6e231 244 hdr->putInt64(HDR_CONTENT_LENGTH, 0);
a9925b40 245 hdr->putStr(HDR_LOCATION, loc);
06a5ae20 246 date = squid_curtime;
247 content_length = 0;
6d38ef86 248}
249
528b2c61 250/* compare the validators of two replies.
251 * 1 = they match
252 * 0 = they do not match
253 */
254int
06a5ae20 255HttpReply::validatorsMatch(HttpReply const * otherRep) const
62e76326 256{
30abd221 257 String one,two;
06a5ae20 258 assert (otherRep);
528b2c61 259 /* Numbers first - easiest to check */
260 /* Content-Length */
261 /* TODO: remove -1 bypass */
62e76326 262
06a5ae20 263 if (content_length != otherRep->content_length
264 && content_length > -1 &&
62e76326 265 otherRep->content_length > -1)
266 return 0;
267
528b2c61 268 /* ETag */
a9925b40 269 one = header.getStrOrList(HDR_ETAG);
62e76326 270
a9925b40 271 two = otherRep->header.getStrOrList(HDR_ETAG);
62e76326 272
a1377698 273 if (one.size()==0 || two.size()==0 || one.caseCmp(two)!=0 ) {
30abd221 274 one.clean();
275 two.clean();
62e76326 276 return 0;
528b2c61 277 }
62e76326 278
06a5ae20 279 if (last_modified != otherRep->last_modified)
62e76326 280 return 0;
281
528b2c61 282 /* MD5 */
a9925b40 283 one = header.getStrOrList(HDR_CONTENT_MD5);
62e76326 284
a9925b40 285 two = otherRep->header.getStrOrList(HDR_CONTENT_MD5);
62e76326 286
a1377698 287 if (one.size()==0 || two.size()==0 || one.caseCmp(two)!=0 ) {
30abd221 288 one.clean();
289 two.clean();
62e76326 290 return 0;
528b2c61 291 }
62e76326 292
528b2c61 293 return 1;
294}
295
cb69b4c7 296void
06a5ae20 297HttpReply::updateOnNotModified(HttpReply const * freshRep)
cb69b4c7 298{
07947ad8 299 assert(freshRep);
1d7ab0f4 300
d8b249ef 301 /* clean cache */
06a5ae20 302 hdrCacheClean();
d8b249ef 303 /* update raw headers */
a9925b40 304 header.update(&freshRep->header,
305 (const HttpHeaderMask *) &Denied304HeadersMask);
1d7ab0f4 306
394499bd 307 header.compact();
d8b249ef 308 /* init cache */
07947ad8 309 hdrCacheInit();
cb69b4c7 310}
311
d8b249ef 312/* internal routines */
cb69b4c7 313
06a5ae20 314time_t
315HttpReply::hdrExpirationTime()
d20b1cd0 316{
317 /* The s-maxage and max-age directive takes priority over Expires */
62e76326 318
06a5ae20 319 if (cache_control) {
320 if (date >= 0) {
d74ad83f 321 if (cache_control->hasSMaxAge())
cf7c2e94 322 return date + cache_control->sMaxAge();
62e76326 323
d74ad83f 324 if (cache_control->hasMaxAge())
cf7c2e94 325 return date + cache_control->maxAge();
62e76326 326 } else {
327 /*
328 * Conservatively handle the case when we have a max-age
329 * header, but no Date for reference?
330 */
331
d74ad83f 332 if (cache_control->hasSMaxAge())
62e76326 333 return squid_curtime;
334
d74ad83f 335 if (cache_control->hasMaxAge())
62e76326 336 return squid_curtime;
337 }
d20b1cd0 338 }
62e76326 339
f66a9ef4 340 if (Config.onoff.vary_ignore_expire &&
a9925b40 341 header.has(HDR_VARY)) {
342 const time_t d = header.getTime(HDR_DATE);
343 const time_t e = header.getTime(HDR_EXPIRES);
62e76326 344
345 if (d == e)
346 return -1;
f66a9ef4 347 }
62e76326 348
a9925b40 349 if (header.has(HDR_EXPIRES)) {
350 const time_t e = header.getTime(HDR_EXPIRES);
62e76326 351 /*
352 * HTTP/1.0 says that robust implementations should consider
353 * bad or malformed Expires header as equivalent to "expires
354 * immediately."
355 */
356 return e < 0 ? squid_curtime : e;
d20b1cd0 357 }
62e76326 358
d20b1cd0 359 return -1;
360}
361
d8b249ef 362/* sync this routine when you update HttpReply struct */
8596962e 363void
07947ad8 364HttpReply::hdrCacheInit()
cb69b4c7 365{
07947ad8 366 HttpMsg::hdrCacheInit();
367
4a1acc56 368 http_ver = sline.version;
47f6e231 369 content_length = header.getInt64(HDR_CONTENT_LENGTH);
a9925b40 370 date = header.getTime(HDR_DATE);
371 last_modified = header.getTime(HDR_LAST_MODIFIED);
372 surrogate_control = header.getSc();
373 content_range = header.getContRange();
4a1acc56 374 keep_alive = persistent() ? 1 : 0;
a9925b40 375 const char *str = header.getStr(HDR_CONTENT_TYPE);
62e76326 376
d8b249ef 377 if (str)
07947ad8 378 content_type.limitInit(str, strcspn(str, ";\t "));
d8b249ef 379 else
30abd221 380 content_type = String();
62e76326 381
d20b1cd0 382 /* be sure to set expires after date and cache-control */
06a5ae20 383 expires = hdrExpirationTime();
cb69b4c7 384}
385
d8b249ef 386/* sync this routine when you update HttpReply struct */
06a5ae20 387void
388HttpReply::hdrCacheClean()
2ac76861 389{
30abd221 390 content_type.clean();
62e76326 391
06a5ae20 392 if (cache_control) {
3d7782c1 393 delete cache_control;
06a5ae20 394 cache_control = NULL;
07947ad8 395 }
62e76326 396
06a5ae20 397 if (surrogate_control) {
45a58345 398 delete surrogate_control;
06a5ae20 399 surrogate_control = NULL;
07947ad8 400 }
43ae1d95 401
06a5ae20 402 if (content_range) {
403 httpHdrContRangeDestroy(content_range);
404 content_range = NULL;
07947ad8 405 }
63259c34 406}
cb69b4c7 407
35282fbf 408/*
409 * Returns the body size of a HTTP response
410 */
47f6e231 411int64_t
60745f24 412HttpReply::bodySize(const HttpRequestMethod& method) const
35282fbf 413{
06a5ae20 414 if (sline.version.major < 1)
1bda350e 415 return -1;
c2a7cefd 416 else if (method.id() == Http::METHOD_HEAD)
62e76326 417 return 0;
9b769c67 418 else if (sline.status() == Http::scOkay)
62e76326 419 (void) 0; /* common case, continue */
9b769c67 420 else if (sline.status() == Http::scNoContent)
62e76326 421 return 0;
9b769c67 422 else if (sline.status() == Http::scNotModified)
62e76326 423 return 0;
9b769c67 424 else if (sline.status() < Http::scOkay)
62e76326 425 return 0;
426
06a5ae20 427 return content_length;
35282fbf 428}
8596962e 429
96ee497f
AJ
430/**
431 * Checks the first line of an HTTP Reply is valid.
432 * currently only checks "HTTP/" exists.
433 *
434 * NP: not all error cases are detected yet. Some are left for detection later in parse.
435 */
436bool
955394ce 437HttpReply::sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, Http::StatusCode *error)
8596962e 438{
96ee497f
AJ
439 // hack warning: using psize instead of size here due to type mismatches with MemBuf.
440
441 // content is long enough to possibly hold a reply
442 // 4 being magic size of a 3-digit number plus space delimiter
443 if ( buf->contentSize() < (protoPrefix.psize() + 4) ) {
0246f6b8
AJ
444 if (hdr_len > 0) {
445 debugs(58, 3, HERE << "Too small reply header (" << hdr_len << " bytes)");
955394ce 446 *error = Http::scInvalidHeader;
0246f6b8 447 }
96ee497f
AJ
448 return false;
449 }
450
e77d7ef0 451 int pos;
96ee497f 452 // catch missing or mismatched protocol identifier
e77d7ef0
AJ
453 // allow special-case for ICY protocol (non-HTTP identifier) in response to faked HTTP request.
454 if (strncmp(buf->content(), "ICY", 3) == 0) {
455 protoPrefix = "ICY";
456 pos = protoPrefix.psize();
dd20bfd3 457 } else {
8596962e 458
e77d7ef0
AJ
459 if (protoPrefix.cmp(buf->content(), protoPrefix.size()) != 0) {
460 debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol prefix (" << protoPrefix << ") in '" << buf->content() << "'");
955394ce 461 *error = Http::scInvalidHeader;
e77d7ef0
AJ
462 return false;
463 }
96ee497f 464
e77d7ef0
AJ
465 // catch missing or negative status value (negative '-' is not a digit)
466 pos = protoPrefix.psize();
dd20bfd3 467
e77d7ef0
AJ
468 // skip arbitrary number of digits and a dot in the verion portion
469 while ( pos <= buf->contentSize() && (*(buf->content()+pos) == '.' || xisdigit(*(buf->content()+pos)) ) ) ++pos;
96ee497f 470
e77d7ef0
AJ
471 // catch missing version info
472 if (pos == protoPrefix.psize()) {
473 debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol version numbers (ie. " << protoPrefix << "/1.0) in '" << buf->content() << "'");
955394ce 474 *error = Http::scInvalidHeader;
e77d7ef0
AJ
475 return false;
476 }
96ee497f
AJ
477 }
478
479 // skip arbitrary number of spaces...
480 while (pos <= buf->contentSize() && (char)*(buf->content()+pos) == ' ') ++pos;
481
6934ca51 482 if (pos < buf->contentSize() && !xisdigit(*(buf->content()+pos))) {
96ee497f 483 debugs(58, 3, "HttpReply::sanityCheckStartLine: missing or invalid status number in '" << buf->content() << "'");
955394ce 484 *error = Http::scInvalidHeader;
96ee497f
AJ
485 return false;
486 }
487
8596962e 488 return true;
489}
490
9b769c67
AJ
491bool
492HttpReply::parseFirstLine(const char *blk_start, const char *blk_end)
429f7150 493{
9b769c67 494 return sline.parse(protoPrefix, blk_start, blk_end);
429f7150 495}
5c09dcb8 496
fb525683 497/* handy: resets and returns -1 */
498int
499HttpReply::httpMsgParseError()
500{
501 int result(HttpMsg::httpMsgParseError());
502 /* indicate an error in the status line */
9b769c67 503 sline.set(Http::ProtocolVersion(1,1), Http::scInvalidHeader);
fb525683 504 return result;
505}
506
5c09dcb8 507/*
508 * Indicate whether or not we would usually expect an entity-body
509 * along with this response
510 */
511bool
60745f24 512HttpReply::expectingBody(const HttpRequestMethod& req_method, int64_t& theSize) const
5c09dcb8 513{
514 bool expectBody = true;
515
c2a7cefd 516 if (req_method == Http::METHOD_HEAD)
5c09dcb8 517 expectBody = false;
9b769c67 518 else if (sline.status() == Http::scNoContent)
5c09dcb8 519 expectBody = false;
9b769c67 520 else if (sline.status() == Http::scNotModified)
5c09dcb8 521 expectBody = false;
9b769c67 522 else if (sline.status() < Http::scOkay)
5c09dcb8 523 expectBody = false;
524 else
525 expectBody = true;
526
527 if (expectBody) {
c3d0ba0c 528 if (header.chunked())
5c09dcb8 529 theSize = -1;
530 else if (content_length >= 0)
531 theSize = content_length;
532 else
533 theSize = -1;
534 }
535
536 return expectBody;
537}
0667cbfb 538
539bool
540HttpReply::receivedBodyTooLarge(HttpRequest& request, int64_t receivedSize)
541{
542 calcMaxBodySize(request);
543 debugs(58, 3, HERE << receivedSize << " >? " << bodySizeMax);
544 return bodySizeMax >= 0 && receivedSize > bodySizeMax;
545}
546
547bool
548HttpReply::expectedBodyTooLarge(HttpRequest& request)
549{
550 calcMaxBodySize(request);
551 debugs(58, 7, HERE << "bodySizeMax=" << bodySizeMax);
552
553 if (bodySizeMax < 0) // no body size limit
554 return false;
555
556 int64_t expectedSize = -1;
557 if (!expectingBody(request.method, expectedSize))
558 return false;
26ac0430 559
0667cbfb 560 debugs(58, 6, HERE << expectedSize << " >? " << bodySizeMax);
561
562 if (expectedSize < 0) // expecting body of an unknown length
563 return false;
564
565 return expectedSize > bodySizeMax;
566}
567
568void
b248c2a3 569HttpReply::calcMaxBodySize(HttpRequest& request) const
0667cbfb 570{
571 // hack: -2 is used as "we have not calculated max body size yet" state
572 if (bodySizeMax != -2) // already tried
573 return;
574 bodySizeMax = -1;
575
4194f58d
AJ
576 // short-circuit ACL testing if there are none configured
577 if (!Config.ReplyBodySize)
578 return;
579
c0941a6a 580 ACLFilledChecklist ch(NULL, &request, NULL);
b248c2a3
AJ
581 // XXX: cont-cast becomes irrelevant when checklist is HttpReply::Pointer
582 ch.reply = const_cast<HttpReply *>(this);
583 HTTPMSGLOCK(ch.reply);
1328cfb7 584 for (AclSizeLimit *l = Config.ReplyBodySize; l; l = l -> next) {
b50e327b 585 /* if there is no ACL list or if the ACLs listed match use this size value */
2efeb0b7 586 if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
0667cbfb 587 debugs(58, 4, HERE << "bodySizeMax=" << bodySizeMax);
588 bodySizeMax = l->size; // may be -1
589 break;
590 }
591 }
592}
da33c835 593
fa0e6114 594// XXX: check that this is sufficient for eCAP cloning
da33c835
HN
595HttpReply *
596HttpReply::clone() const
597{
598 HttpReply *rep = new HttpReply();
66363092 599 rep->sline = sline; // used in hdrCacheInit() call below
da33c835
HN
600 rep->header.append(&header);
601 rep->hdrCacheInit();
602 rep->hdr_sz = hdr_sz;
f230832f
HN
603 rep->http_ver = http_ver;
604 rep->pstate = pstate;
fa0e6114
AR
605 rep->body_pipe = body_pipe;
606
66363092 607 // keep_alive is handled in hdrCacheInit()
da33c835
HN
608 return rep;
609}
d67acb4e 610
d67acb4e
AJ
611bool HttpReply::inheritProperties(const HttpMsg *aMsg)
612{
613 const HttpReply *aRep = dynamic_cast<const HttpReply*>(aMsg);
26ac0430
AJ
614 if (!aRep)
615 return false;
d67acb4e
AJ
616 keep_alive = aRep->keep_alive;
617 return true;
618}
c679653d
AR
619
620void HttpReply::removeStaleWarnings()
621{
622 String warning;
623 if (header.getList(HDR_WARNING, &warning)) {
624 const String newWarning = removeStaleWarningValues(warning);
625 if (warning.size() && warning.size() == newWarning.size())
626 return; // some warnings are there and none changed
627 header.delById(HDR_WARNING);
628 if (newWarning.size()) { // some warnings left
629 HttpHeaderEntry *const e =
630 new HttpHeaderEntry(HDR_WARNING, NULL, newWarning.termedBuf());
631 header.addEntry(e);
632 }
633 }
634}
635
636/**
637 * Remove warning-values with warn-date different from Date value from
638 * a single header entry. Returns a string with all valid warning-values.
639 */
640String HttpReply::removeStaleWarningValues(const String &value)
641{
642 String newValue;
643 const char *item = 0;
644 int len = 0;
645 const char *pos = 0;
646 while (strListGetItem(&value, ',', &item, &len, &pos)) {
647 bool keep = true;
648 // Does warning-value have warn-date (which contains quoted date)?
649 // We scan backwards, looking for two quoted strings.
650 // warning-value = warn-code SP warn-agent SP warn-text [SP warn-date]
651 const char *p = item + len - 1;
652
653 while (p >= item && xisspace(*p)) --p; // skip whitespace
654
655 // warning-value MUST end with quote
656 if (p >= item && *p == '"') {
657 const char *const warnDateEnd = p;
658 --p;
659 while (p >= item && *p != '"') --p; // find the next quote
660
661 const char *warnDateBeg = p + 1;
662 --p;
663 while (p >= item && xisspace(*p)) --p; // skip whitespace
664
665 if (p >= item && *p == '"' && warnDateBeg - p > 2) {
666 // found warn-text
667 String warnDate;
668 warnDate.append(warnDateBeg, warnDateEnd - warnDateBeg);
669 const time_t time = parse_rfc1123(warnDate.termedBuf());
670 keep = (time > 0 && time == date); // keep valid and matching date
671 }
672 }
673
674 if (keep) {
675 if (newValue.size())
676 newValue.append(", ");
677 newValue.append(item, len);
678 }
679 }
680
681 return newValue;
682}