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