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