]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpReply.cc
SourceFormat Enforcement
[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
582c2af2
FC
36#include "squid.h"
37#include "acl/FilledChecklist.h"
38#include "globals.h"
0521f8be 39#include "HttpBody.h"
7ebe76de 40#include "HttpHdrCc.h"
582c2af2 41#include "HttpHdrContRange.h"
25b6a907 42#include "HttpHdrSc.h"
582c2af2 43#include "HttpReply.h"
0667cbfb 44#include "HttpRequest.h"
0eb49b6d 45#include "MemBuf.h"
582c2af2
FC
46#include "protos.h"
47#include "SquidTime.h"
48#include "Store.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{
8596962e 79 assert(HTTP_STATUS_NONE == 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();
101 httpStatusLineInit(&sline);
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();
06a5ae20 129 httpStatusLineClean(&sline);
0667cbfb 130 bodySizeMax = -2; // hack: make calculatedBodySizeMax() false
cb69b4c7 131}
132
cb69b4c7 133void
06a5ae20 134HttpReply::packHeadersInto(Packer * p) const
cb69b4c7 135{
06a5ae20 136 httpStatusLinePackInto(&sline, 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
11992b6f 162#if DEAD_CODE
032785bf 163MemBuf *
11992b6f 164httpPackedReply(http_status status, const char *ctype, int64_t clen, time_t lmt, time_t expires)
cb69b4c7 165{
06a5ae20 166 HttpReply *rep = new HttpReply;
11992b6f 167 rep->setHeaders(status, ctype, NULL, clen, lmt, expires);
06a5ae20 168 MemBuf *mb = rep->pack();
169 delete rep;
cb69b4c7 170 return mb;
171}
11992b6f 172#endif
cb69b4c7 173
528b2c61 174HttpReply *
11992b6f 175HttpReply::make304() const
cb69b4c7 176{
62e76326 177 static const http_hdr_type ImsEntries[] = {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER};
178
06a5ae20 179 HttpReply *rv = new HttpReply;
728da2ee 180 int t;
de336bbe 181 HttpHeaderEntry *e;
cb69b4c7 182
528b2c61 183 /* rv->content_length; */
06a5ae20 184 rv->date = date;
185 rv->last_modified = last_modified;
186 rv->expires = expires;
187 rv->content_type = content_type;
528b2c61 188 /* rv->cache_control */
189 /* rv->content_range */
190 /* rv->keep_alive */
871c031f 191 HttpVersion ver(1,1);
62fb4c4f 192 httpStatusLineSet(&rv->sline, ver, HTTP_NOT_MODIFIED, NULL);
62e76326 193
de336bbe 194 for (t = 0; ImsEntries[t] != HDR_OTHER; ++t)
a9925b40 195 if ((e = header.findEntry(ImsEntries[t])))
eede25e7 196 rv->header.addEntry(e->clone());
62e76326 197
528b2c61 198 /* rv->body */
199 return rv;
200}
201
032785bf 202MemBuf *
06a5ae20 203HttpReply::packed304Reply()
528b2c61 204{
205 /* Not as efficient as skipping the header duplication,
206 * but easier to maintain
207 */
871c031f 208 HttpReply *temp = make304();
06a5ae20 209 MemBuf *rv = temp->pack();
210 delete temp;
528b2c61 211 return rv;
cb69b4c7 212}
213
214void
11992b6f 215HttpReply::setHeaders(http_status status, const char *reason,
350e2aec 216 const char *ctype, int64_t clen, time_t lmt, time_t expiresTime)
cb69b4c7 217{
218 HttpHeader *hdr;
871c031f 219 HttpVersion ver(1,1);
06a5ae20 220 httpStatusLineSet(&sline, ver, status, reason);
221 hdr = &header;
a9925b40 222 hdr->putStr(HDR_SERVER, visible_appname_string);
223 hdr->putStr(HDR_MIME_VERSION, "1.0");
224 hdr->putTime(HDR_DATE, squid_curtime);
62e76326 225
d8b249ef 226 if (ctype) {
a9925b40 227 hdr->putStr(HDR_CONTENT_TYPE, ctype);
06a5ae20 228 content_type = ctype;
d8b249ef 229 } else
30abd221 230 content_type = String();
62e76326 231
de336bbe 232 if (clen >= 0)
47f6e231 233 hdr->putInt64(HDR_CONTENT_LENGTH, clen);
62e76326 234
350e2aec
FC
235 if (expiresTime >= 0)
236 hdr->putTime(HDR_EXPIRES, expiresTime);
62e76326 237
2ac76861 238 if (lmt > 0) /* this used to be lmt != 0 @?@ */
a9925b40 239 hdr->putTime(HDR_LAST_MODIFIED, lmt);
62e76326 240
06a5ae20 241 date = squid_curtime;
62e76326 242
06a5ae20 243 content_length = clen;
62e76326 244
350e2aec 245 expires = expiresTime;
62e76326 246
06a5ae20 247 last_modified = lmt;
cb69b4c7 248}
249
6d38ef86 250void
06a5ae20 251HttpReply::redirect(http_status status, const char *loc)
6d38ef86 252{
253 HttpHeader *hdr;
871c031f 254 HttpVersion ver(1,1);
06a5ae20 255 httpStatusLineSet(&sline, ver, status, httpStatusString(status));
256 hdr = &header;
7dbca7a4 257 hdr->putStr(HDR_SERVER, APP_FULLNAME);
a9925b40 258 hdr->putTime(HDR_DATE, squid_curtime);
47f6e231 259 hdr->putInt64(HDR_CONTENT_LENGTH, 0);
a9925b40 260 hdr->putStr(HDR_LOCATION, loc);
06a5ae20 261 date = squid_curtime;
262 content_length = 0;
6d38ef86 263}
264
528b2c61 265/* compare the validators of two replies.
266 * 1 = they match
267 * 0 = they do not match
268 */
269int
06a5ae20 270HttpReply::validatorsMatch(HttpReply const * otherRep) const
62e76326 271{
30abd221 272 String one,two;
06a5ae20 273 assert (otherRep);
528b2c61 274 /* Numbers first - easiest to check */
275 /* Content-Length */
276 /* TODO: remove -1 bypass */
62e76326 277
06a5ae20 278 if (content_length != otherRep->content_length
279 && content_length > -1 &&
62e76326 280 otherRep->content_length > -1)
281 return 0;
282
528b2c61 283 /* ETag */
a9925b40 284 one = header.getStrOrList(HDR_ETAG);
62e76326 285
a9925b40 286 two = otherRep->header.getStrOrList(HDR_ETAG);
62e76326 287
9d7a89a5 288 if (one.undefined() || two.undefined() || one.caseCmp(two)!=0 ) {
30abd221 289 one.clean();
290 two.clean();
62e76326 291 return 0;
528b2c61 292 }
62e76326 293
06a5ae20 294 if (last_modified != otherRep->last_modified)
62e76326 295 return 0;
296
528b2c61 297 /* MD5 */
a9925b40 298 one = header.getStrOrList(HDR_CONTENT_MD5);
62e76326 299
a9925b40 300 two = otherRep->header.getStrOrList(HDR_CONTENT_MD5);
62e76326 301
9d7a89a5 302 if (one.undefined() || two.undefined() || one.caseCmp(two) != 0 ) {
30abd221 303 one.clean();
304 two.clean();
62e76326 305 return 0;
528b2c61 306 }
62e76326 307
528b2c61 308 return 1;
309}
310
cb69b4c7 311void
06a5ae20 312HttpReply::updateOnNotModified(HttpReply const * freshRep)
cb69b4c7 313{
07947ad8 314 assert(freshRep);
1d7ab0f4 315
d8b249ef 316 /* clean cache */
06a5ae20 317 hdrCacheClean();
d8b249ef 318 /* update raw headers */
a9925b40 319 header.update(&freshRep->header,
320 (const HttpHeaderMask *) &Denied304HeadersMask);
1d7ab0f4 321
394499bd 322 header.compact();
d8b249ef 323 /* init cache */
07947ad8 324 hdrCacheInit();
cb69b4c7 325}
326
d8b249ef 327/* internal routines */
cb69b4c7 328
06a5ae20 329time_t
330HttpReply::hdrExpirationTime()
d20b1cd0 331{
332 /* The s-maxage and max-age directive takes priority over Expires */
62e76326 333
06a5ae20 334 if (cache_control) {
335 if (date >= 0) {
d74ad83f 336 if (cache_control->hasSMaxAge())
cf7c2e94 337 return date + cache_control->sMaxAge();
62e76326 338
d74ad83f 339 if (cache_control->hasMaxAge())
cf7c2e94 340 return date + cache_control->maxAge();
62e76326 341 } else {
342 /*
343 * Conservatively handle the case when we have a max-age
344 * header, but no Date for reference?
345 */
346
d74ad83f 347 if (cache_control->hasSMaxAge())
62e76326 348 return squid_curtime;
349
d74ad83f 350 if (cache_control->hasMaxAge())
62e76326 351 return squid_curtime;
352 }
d20b1cd0 353 }
62e76326 354
f66a9ef4 355 if (Config.onoff.vary_ignore_expire &&
a9925b40 356 header.has(HDR_VARY)) {
357 const time_t d = header.getTime(HDR_DATE);
358 const time_t e = header.getTime(HDR_EXPIRES);
62e76326 359
360 if (d == e)
361 return -1;
f66a9ef4 362 }
62e76326 363
a9925b40 364 if (header.has(HDR_EXPIRES)) {
365 const time_t e = header.getTime(HDR_EXPIRES);
62e76326 366 /*
367 * HTTP/1.0 says that robust implementations should consider
368 * bad or malformed Expires header as equivalent to "expires
369 * immediately."
370 */
371 return e < 0 ? squid_curtime : e;
d20b1cd0 372 }
62e76326 373
d20b1cd0 374 return -1;
375}
376
d8b249ef 377/* sync this routine when you update HttpReply struct */
8596962e 378void
07947ad8 379HttpReply::hdrCacheInit()
cb69b4c7 380{
07947ad8 381 HttpMsg::hdrCacheInit();
382
4a1acc56 383 http_ver = sline.version;
47f6e231 384 content_length = header.getInt64(HDR_CONTENT_LENGTH);
a9925b40 385 date = header.getTime(HDR_DATE);
386 last_modified = header.getTime(HDR_LAST_MODIFIED);
387 surrogate_control = header.getSc();
388 content_range = header.getContRange();
4a1acc56 389 keep_alive = persistent() ? 1 : 0;
a9925b40 390 const char *str = header.getStr(HDR_CONTENT_TYPE);
62e76326 391
d8b249ef 392 if (str)
07947ad8 393 content_type.limitInit(str, strcspn(str, ";\t "));
d8b249ef 394 else
30abd221 395 content_type = String();
62e76326 396
d20b1cd0 397 /* be sure to set expires after date and cache-control */
06a5ae20 398 expires = hdrExpirationTime();
cb69b4c7 399}
400
d8b249ef 401/* sync this routine when you update HttpReply struct */
06a5ae20 402void
403HttpReply::hdrCacheClean()
2ac76861 404{
30abd221 405 content_type.clean();
62e76326 406
06a5ae20 407 if (cache_control) {
3d7782c1 408 delete cache_control;
06a5ae20 409 cache_control = NULL;
07947ad8 410 }
62e76326 411
06a5ae20 412 if (surrogate_control) {
45a58345 413 delete surrogate_control;
06a5ae20 414 surrogate_control = NULL;
07947ad8 415 }
43ae1d95 416
06a5ae20 417 if (content_range) {
418 httpHdrContRangeDestroy(content_range);
419 content_range = NULL;
07947ad8 420 }
63259c34 421}
cb69b4c7 422
35282fbf 423/*
424 * Returns the body size of a HTTP response
425 */
47f6e231 426int64_t
60745f24 427HttpReply::bodySize(const HttpRequestMethod& method) const
35282fbf 428{
06a5ae20 429 if (sline.version.major < 1)
1bda350e 430 return -1;
914b89a2 431 else if (method.id() == METHOD_HEAD)
62e76326 432 return 0;
06a5ae20 433 else if (sline.status == HTTP_OK)
62e76326 434 (void) 0; /* common case, continue */
06a5ae20 435 else if (sline.status == HTTP_NO_CONTENT)
62e76326 436 return 0;
06a5ae20 437 else if (sline.status == HTTP_NOT_MODIFIED)
62e76326 438 return 0;
06a5ae20 439 else if (sline.status < HTTP_OK)
62e76326 440 return 0;
441
06a5ae20 442 return content_length;
35282fbf 443}
8596962e 444
96ee497f
AJ
445/**
446 * Checks the first line of an HTTP Reply is valid.
447 * currently only checks "HTTP/" exists.
448 *
449 * NP: not all error cases are detected yet. Some are left for detection later in parse.
450 */
451bool
452HttpReply::sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, http_status *error)
8596962e 453{
96ee497f
AJ
454 // hack warning: using psize instead of size here due to type mismatches with MemBuf.
455
456 // content is long enough to possibly hold a reply
457 // 4 being magic size of a 3-digit number plus space delimiter
458 if ( buf->contentSize() < (protoPrefix.psize() + 4) ) {
0246f6b8
AJ
459 if (hdr_len > 0) {
460 debugs(58, 3, HERE << "Too small reply header (" << hdr_len << " bytes)");
96ee497f 461 *error = HTTP_INVALID_HEADER;
0246f6b8 462 }
96ee497f
AJ
463 return false;
464 }
465
e77d7ef0 466 int pos;
96ee497f 467 // catch missing or mismatched protocol identifier
e77d7ef0
AJ
468 // allow special-case for ICY protocol (non-HTTP identifier) in response to faked HTTP request.
469 if (strncmp(buf->content(), "ICY", 3) == 0) {
470 protoPrefix = "ICY";
471 pos = protoPrefix.psize();
dd20bfd3 472 } else {
8596962e 473
e77d7ef0
AJ
474 if (protoPrefix.cmp(buf->content(), protoPrefix.size()) != 0) {
475 debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol prefix (" << protoPrefix << ") in '" << buf->content() << "'");
476 *error = HTTP_INVALID_HEADER;
477 return false;
478 }
96ee497f 479
e77d7ef0
AJ
480 // catch missing or negative status value (negative '-' is not a digit)
481 pos = protoPrefix.psize();
dd20bfd3 482
e77d7ef0
AJ
483 // skip arbitrary number of digits and a dot in the verion portion
484 while ( pos <= buf->contentSize() && (*(buf->content()+pos) == '.' || xisdigit(*(buf->content()+pos)) ) ) ++pos;
96ee497f 485
e77d7ef0
AJ
486 // catch missing version info
487 if (pos == protoPrefix.psize()) {
488 debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol version numbers (ie. " << protoPrefix << "/1.0) in '" << buf->content() << "'");
489 *error = HTTP_INVALID_HEADER;
490 return false;
491 }
96ee497f
AJ
492 }
493
494 // skip arbitrary number of spaces...
495 while (pos <= buf->contentSize() && (char)*(buf->content()+pos) == ' ') ++pos;
496
6934ca51 497 if (pos < buf->contentSize() && !xisdigit(*(buf->content()+pos))) {
96ee497f
AJ
498 debugs(58, 3, "HttpReply::sanityCheckStartLine: missing or invalid status number in '" << buf->content() << "'");
499 *error = HTTP_INVALID_HEADER;
500 return false;
501 }
502
8596962e 503 return true;
504}
505
506void HttpReply::packFirstLineInto(Packer *p, bool unused) const
507{
508 httpStatusLinePackInto(&sline, p);
509}
429f7150 510
511bool HttpReply::parseFirstLine(const char *blk_start, const char *blk_end)
512{
513 return httpStatusLineParse(&sline, protoPrefix, blk_start, blk_end);
514}
5c09dcb8 515
fb525683 516/* handy: resets and returns -1 */
517int
518HttpReply::httpMsgParseError()
519{
520 int result(HttpMsg::httpMsgParseError());
521 /* indicate an error in the status line */
522 sline.status = HTTP_INVALID_HEADER;
523 return result;
524}
525
5c09dcb8 526/*
527 * Indicate whether or not we would usually expect an entity-body
528 * along with this response
529 */
530bool
60745f24 531HttpReply::expectingBody(const HttpRequestMethod& req_method, int64_t& theSize) const
5c09dcb8 532{
533 bool expectBody = true;
534
535 if (req_method == METHOD_HEAD)
536 expectBody = false;
537 else if (sline.status == HTTP_NO_CONTENT)
538 expectBody = false;
539 else if (sline.status == HTTP_NOT_MODIFIED)
540 expectBody = false;
541 else if (sline.status < HTTP_OK)
542 expectBody = false;
543 else
544 expectBody = true;
545
546 if (expectBody) {
c3d0ba0c 547 if (header.chunked())
5c09dcb8 548 theSize = -1;
549 else if (content_length >= 0)
550 theSize = content_length;
551 else
552 theSize = -1;
553 }
554
555 return expectBody;
556}
0667cbfb 557
558bool
559HttpReply::receivedBodyTooLarge(HttpRequest& request, int64_t receivedSize)
560{
561 calcMaxBodySize(request);
562 debugs(58, 3, HERE << receivedSize << " >? " << bodySizeMax);
563 return bodySizeMax >= 0 && receivedSize > bodySizeMax;
564}
565
566bool
567HttpReply::expectedBodyTooLarge(HttpRequest& request)
568{
569 calcMaxBodySize(request);
570 debugs(58, 7, HERE << "bodySizeMax=" << bodySizeMax);
571
572 if (bodySizeMax < 0) // no body size limit
573 return false;
574
575 int64_t expectedSize = -1;
576 if (!expectingBody(request.method, expectedSize))
577 return false;
26ac0430 578
0667cbfb 579 debugs(58, 6, HERE << expectedSize << " >? " << bodySizeMax);
580
581 if (expectedSize < 0) // expecting body of an unknown length
582 return false;
583
584 return expectedSize > bodySizeMax;
585}
586
587void
588HttpReply::calcMaxBodySize(HttpRequest& request)
589{
590 // hack: -2 is used as "we have not calculated max body size yet" state
591 if (bodySizeMax != -2) // already tried
592 return;
593 bodySizeMax = -1;
594
4194f58d
AJ
595 // short-circuit ACL testing if there are none configured
596 if (!Config.ReplyBodySize)
597 return;
598
c0941a6a 599 ACLFilledChecklist ch(NULL, &request, NULL);
0667cbfb 600 ch.reply = HTTPMSGLOCK(this); // XXX: this lock makes method non-const
0667cbfb 601 for (acl_size_t *l = Config.ReplyBodySize; l; l = l -> next) {
b50e327b 602 /* if there is no ACL list or if the ACLs listed match use this size value */
2efeb0b7 603 if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
0667cbfb 604 debugs(58, 4, HERE << "bodySizeMax=" << bodySizeMax);
605 bodySizeMax = l->size; // may be -1
606 break;
607 }
608 }
609}
da33c835 610
fa0e6114 611// XXX: check that this is sufficient for eCAP cloning
da33c835
HN
612HttpReply *
613HttpReply::clone() const
614{
615 HttpReply *rep = new HttpReply();
66363092 616 rep->sline = sline; // used in hdrCacheInit() call below
da33c835
HN
617 rep->header.append(&header);
618 rep->hdrCacheInit();
619 rep->hdr_sz = hdr_sz;
f230832f
HN
620 rep->http_ver = http_ver;
621 rep->pstate = pstate;
fa0e6114
AR
622 rep->body_pipe = body_pipe;
623
f230832f 624 rep->protocol = protocol;
66363092 625 // keep_alive is handled in hdrCacheInit()
da33c835
HN
626 return rep;
627}
d67acb4e 628
d67acb4e
AJ
629bool HttpReply::inheritProperties(const HttpMsg *aMsg)
630{
631 const HttpReply *aRep = dynamic_cast<const HttpReply*>(aMsg);
26ac0430
AJ
632 if (!aRep)
633 return false;
d67acb4e
AJ
634 keep_alive = aRep->keep_alive;
635 return true;
636}
c679653d
AR
637
638void HttpReply::removeStaleWarnings()
639{
640 String warning;
641 if (header.getList(HDR_WARNING, &warning)) {
642 const String newWarning = removeStaleWarningValues(warning);
643 if (warning.size() && warning.size() == newWarning.size())
644 return; // some warnings are there and none changed
645 header.delById(HDR_WARNING);
646 if (newWarning.size()) { // some warnings left
647 HttpHeaderEntry *const e =
648 new HttpHeaderEntry(HDR_WARNING, NULL, newWarning.termedBuf());
649 header.addEntry(e);
650 }
651 }
652}
653
654/**
655 * Remove warning-values with warn-date different from Date value from
656 * a single header entry. Returns a string with all valid warning-values.
657 */
658String HttpReply::removeStaleWarningValues(const String &value)
659{
660 String newValue;
661 const char *item = 0;
662 int len = 0;
663 const char *pos = 0;
664 while (strListGetItem(&value, ',', &item, &len, &pos)) {
665 bool keep = true;
666 // Does warning-value have warn-date (which contains quoted date)?
667 // We scan backwards, looking for two quoted strings.
668 // warning-value = warn-code SP warn-agent SP warn-text [SP warn-date]
669 const char *p = item + len - 1;
670
671 while (p >= item && xisspace(*p)) --p; // skip whitespace
672
673 // warning-value MUST end with quote
674 if (p >= item && *p == '"') {
675 const char *const warnDateEnd = p;
676 --p;
677 while (p >= item && *p != '"') --p; // find the next quote
678
679 const char *warnDateBeg = p + 1;
680 --p;
681 while (p >= item && xisspace(*p)) --p; // skip whitespace
682
683 if (p >= item && *p == '"' && warnDateBeg - p > 2) {
684 // found warn-text
685 String warnDate;
686 warnDate.append(warnDateBeg, warnDateEnd - warnDateBeg);
687 const time_t time = parse_rfc1123(warnDate.termedBuf());
688 keep = (time > 0 && time == date); // keep valid and matching date
689 }
690 }
691
692 if (keep) {
693 if (newValue.size())
694 newValue.append(", ");
695 newValue.append(item, len);
696 }
697 }
698
699 return newValue;
700}