]>
Commit | Line | Data |
---|---|---|
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 | ||
cb69b4c7 | 36 | #include "squid.h" |
985c86bc | 37 | #include "SquidTime.h" |
528b2c61 | 38 | #include "Store.h" |
8596962e | 39 | #include "HttpReply.h" |
528b2c61 | 40 | #include "HttpHdrContRange.h" |
25b6a907 | 41 | #include "HttpHdrSc.h" |
c0941a6a | 42 | #include "acl/FilledChecklist.h" |
0667cbfb | 43 | #include "HttpRequest.h" |
0eb49b6d | 44 | #include "MemBuf.h" |
cb69b4c7 | 45 | |
46 | /* local constants */ | |
47 | ||
1d7ab0f4 | 48 | /* If we receive a 304 from the origin during a cache revalidation, we must |
49 | * update the headers of the existing entry. Specifically, we need to update all | |
50 | * end-to-end headers and not any hop-by-hop headers (rfc2616 13.5.3). | |
51 | * | |
52 | * This is not the whole story though: since it is possible for a faulty/malicious | |
53 | * origin server to set headers it should not in a 304, we must explicitly ignore | |
54 | * these too. Specifically all entity-headers except those permitted in a 304 | |
55 | * (rfc2616 10.3.5) must be ignored. | |
26ac0430 | 56 | * |
1d7ab0f4 | 57 | * The list of headers we don't update is made up of: |
58 | * all hop-by-hop headers | |
59 | * all entity-headers except Expires and Content-Location | |
60 | */ | |
2246b732 | 61 | static HttpHeaderMask Denied304HeadersMask; |
26ac0430 AJ |
62 | static http_hdr_type Denied304HeadersArr[] = { |
63 | // hop-by-hop headers | |
64 | HDR_CONNECTION, HDR_KEEP_ALIVE, HDR_PROXY_AUTHENTICATE, HDR_PROXY_AUTHORIZATION, | |
65 | HDR_TE, HDR_TRAILERS, HDR_TRANSFER_ENCODING, HDR_UPGRADE, | |
66 | // entity headers | |
67 | HDR_ALLOW, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, HDR_CONTENT_LENGTH, | |
68 | HDR_CONTENT_MD5, HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_LAST_MODIFIED | |
69 | }; | |
2246b732 | 70 | |
2246b732 | 71 | /* module initialization */ |
72 | void | |
9bc73deb | 73 | httpReplyInitModule(void) |
2246b732 | 74 | { |
8596962e | 75 | assert(HTTP_STATUS_NONE == 0); // HttpReply::parse() interface assumes that |
97474590 | 76 | httpHeaderMaskInit(&Denied304HeadersMask, 0); |
8abf232c | 77 | httpHeaderCalcMask(&Denied304HeadersMask, Denied304HeadersArr, countof(Denied304HeadersArr)); |
2246b732 | 78 | } |
79 | ||
26ac0430 AJ |
80 | HttpReply::HttpReply() : HttpMsg(hoReply), date (0), last_modified (0), |
81 | expires (0), surrogate_control (NULL), content_range (NULL), keep_alive (0), | |
82 | protoPrefix("HTTP/"), bodySizeMax(-2) | |
cb69b4c7 | 83 | { |
06a5ae20 | 84 | init(); |
cb69b4c7 | 85 | } |
86 | ||
06a5ae20 | 87 | HttpReply::~HttpReply() |
cb69b4c7 | 88 | { |
06a5ae20 | 89 | if (do_clean) |
90 | clean(); | |
cb69b4c7 | 91 | } |
92 | ||
93 | void | |
06a5ae20 | 94 | HttpReply::init() |
cb69b4c7 | 95 | { |
06a5ae20 | 96 | httpBodyInit(&body); |
97 | hdrCacheInit(); | |
98 | httpStatusLineInit(&sline); | |
c99de607 | 99 | pstate = psReadyToParseStartLine; |
06a5ae20 | 100 | do_clean = true; |
cb69b4c7 | 101 | } |
102 | ||
06a5ae20 | 103 | void HttpReply::reset() |
cb69b4c7 | 104 | { |
06a5ae20 | 105 | |
8596962e | 106 | // reset should not reset the protocol; could have made protoPrefix a |
107 | // virtual function instead, but it is not clear whether virtual methods | |
108 | // are allowed with MEMPROXY_CLASS() and whether some cbdata void* | |
109 | // conversions are not going to kill virtual tables | |
30abd221 | 110 | const String pfx = protoPrefix; |
06a5ae20 | 111 | clean(); |
112 | init(); | |
113 | protoPrefix = pfx; | |
114 | } | |
115 | ||
116 | void | |
117 | HttpReply::clean() | |
118 | { | |
26ac0430 | 119 | // we used to assert that the pipe is NULL, but now the message only |
5f8252d2 | 120 | // points to a pipe that is owned and initiated by another object. |
121 | body_pipe = NULL; | |
122 | ||
06a5ae20 | 123 | httpBodyClean(&body); |
124 | hdrCacheClean(); | |
519e0948 | 125 | header.clean(); |
06a5ae20 | 126 | httpStatusLineClean(&sline); |
0667cbfb | 127 | bodySizeMax = -2; // hack: make calculatedBodySizeMax() false |
cb69b4c7 | 128 | } |
129 | ||
cb69b4c7 | 130 | void |
06a5ae20 | 131 | HttpReply::packHeadersInto(Packer * p) const |
cb69b4c7 | 132 | { |
06a5ae20 | 133 | httpStatusLinePackInto(&sline, p); |
a9925b40 | 134 | header.packInto(p); |
cb69b4c7 | 135 | packerAppend(p, "\r\n", 2); |
528b2c61 | 136 | } |
137 | ||
138 | void | |
06a5ae20 | 139 | HttpReply::packInto(Packer * p) |
528b2c61 | 140 | { |
06a5ae20 | 141 | packHeadersInto(p); |
142 | httpBodyPackInto(&body, p); | |
cb69b4c7 | 143 | } |
144 | ||
06a5ae20 | 145 | /* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */ |
032785bf | 146 | MemBuf * |
06a5ae20 | 147 | HttpReply::pack() |
cb69b4c7 | 148 | { |
032785bf | 149 | MemBuf *mb = new MemBuf; |
cb69b4c7 | 150 | Packer p; |
cb69b4c7 | 151 | |
2fe7eff9 | 152 | mb->init(); |
032785bf | 153 | packerToMemInit(&p, mb); |
06a5ae20 | 154 | packInto(&p); |
cb69b4c7 | 155 | packerClean(&p); |
156 | return mb; | |
157 | } | |
158 | ||
11992b6f | 159 | #if DEAD_CODE |
032785bf | 160 | MemBuf * |
11992b6f | 161 | httpPackedReply(http_status status, const char *ctype, int64_t clen, time_t lmt, time_t expires) |
cb69b4c7 | 162 | { |
06a5ae20 | 163 | HttpReply *rep = new HttpReply; |
11992b6f | 164 | rep->setHeaders(status, ctype, NULL, clen, lmt, expires); |
06a5ae20 | 165 | MemBuf *mb = rep->pack(); |
166 | delete rep; | |
cb69b4c7 | 167 | return mb; |
168 | } | |
11992b6f | 169 | #endif |
cb69b4c7 | 170 | |
528b2c61 | 171 | HttpReply * |
11992b6f | 172 | HttpReply::make304() const |
cb69b4c7 | 173 | { |
62e76326 | 174 | static const http_hdr_type ImsEntries[] = {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER}; |
175 | ||
06a5ae20 | 176 | HttpReply *rv = new HttpReply; |
728da2ee | 177 | int t; |
de336bbe | 178 | HttpHeaderEntry *e; |
cb69b4c7 | 179 | |
528b2c61 | 180 | /* rv->content_length; */ |
06a5ae20 | 181 | rv->date = date; |
182 | rv->last_modified = last_modified; | |
183 | rv->expires = expires; | |
184 | rv->content_type = content_type; | |
528b2c61 | 185 | /* rv->cache_control */ |
186 | /* rv->content_range */ | |
187 | /* rv->keep_alive */ | |
871c031f | 188 | HttpVersion ver(1,1); |
11992b6f | 189 | httpStatusLineSet(&rv->sline, ver, HTTP_NOT_MODIFIED, ""); |
62e76326 | 190 | |
de336bbe | 191 | for (t = 0; ImsEntries[t] != HDR_OTHER; ++t) |
a9925b40 | 192 | if ((e = header.findEntry(ImsEntries[t]))) |
eede25e7 | 193 | rv->header.addEntry(e->clone()); |
62e76326 | 194 | |
528b2c61 | 195 | /* rv->body */ |
196 | return rv; | |
197 | } | |
198 | ||
032785bf | 199 | MemBuf * |
06a5ae20 | 200 | HttpReply::packed304Reply() |
528b2c61 | 201 | { |
202 | /* Not as efficient as skipping the header duplication, | |
203 | * but easier to maintain | |
204 | */ | |
871c031f | 205 | HttpReply *temp = make304(); |
06a5ae20 | 206 | MemBuf *rv = temp->pack(); |
207 | delete temp; | |
528b2c61 | 208 | return rv; |
cb69b4c7 | 209 | } |
210 | ||
211 | void | |
11992b6f | 212 | HttpReply::setHeaders(http_status status, const char *reason, |
350e2aec | 213 | const char *ctype, int64_t clen, time_t lmt, time_t expiresTime) |
cb69b4c7 | 214 | { |
215 | HttpHeader *hdr; | |
871c031f | 216 | HttpVersion ver(1,1); |
06a5ae20 | 217 | httpStatusLineSet(&sline, ver, status, reason); |
218 | hdr = &header; | |
a9925b40 | 219 | hdr->putStr(HDR_SERVER, visible_appname_string); |
220 | hdr->putStr(HDR_MIME_VERSION, "1.0"); | |
221 | hdr->putTime(HDR_DATE, squid_curtime); | |
62e76326 | 222 | |
d8b249ef | 223 | if (ctype) { |
a9925b40 | 224 | hdr->putStr(HDR_CONTENT_TYPE, ctype); |
06a5ae20 | 225 | content_type = ctype; |
d8b249ef | 226 | } else |
30abd221 | 227 | content_type = String(); |
62e76326 | 228 | |
de336bbe | 229 | if (clen >= 0) |
47f6e231 | 230 | hdr->putInt64(HDR_CONTENT_LENGTH, clen); |
62e76326 | 231 | |
350e2aec FC |
232 | if (expiresTime >= 0) |
233 | hdr->putTime(HDR_EXPIRES, expiresTime); | |
62e76326 | 234 | |
2ac76861 | 235 | if (lmt > 0) /* this used to be lmt != 0 @?@ */ |
a9925b40 | 236 | hdr->putTime(HDR_LAST_MODIFIED, lmt); |
62e76326 | 237 | |
06a5ae20 | 238 | date = squid_curtime; |
62e76326 | 239 | |
06a5ae20 | 240 | content_length = clen; |
62e76326 | 241 | |
350e2aec | 242 | expires = expiresTime; |
62e76326 | 243 | |
06a5ae20 | 244 | last_modified = lmt; |
cb69b4c7 | 245 | } |
246 | ||
6d38ef86 | 247 | void |
06a5ae20 | 248 | HttpReply::redirect(http_status status, const char *loc) |
6d38ef86 | 249 | { |
250 | HttpHeader *hdr; | |
871c031f | 251 | HttpVersion ver(1,1); |
06a5ae20 | 252 | httpStatusLineSet(&sline, ver, status, httpStatusString(status)); |
253 | hdr = &header; | |
7dbca7a4 | 254 | hdr->putStr(HDR_SERVER, APP_FULLNAME); |
a9925b40 | 255 | hdr->putTime(HDR_DATE, squid_curtime); |
47f6e231 | 256 | hdr->putInt64(HDR_CONTENT_LENGTH, 0); |
a9925b40 | 257 | hdr->putStr(HDR_LOCATION, loc); |
06a5ae20 | 258 | date = squid_curtime; |
259 | content_length = 0; | |
6d38ef86 | 260 | } |
261 | ||
528b2c61 | 262 | /* compare the validators of two replies. |
263 | * 1 = they match | |
264 | * 0 = they do not match | |
265 | */ | |
266 | int | |
06a5ae20 | 267 | HttpReply::validatorsMatch(HttpReply const * otherRep) const |
62e76326 | 268 | { |
30abd221 | 269 | String one,two; |
06a5ae20 | 270 | assert (otherRep); |
528b2c61 | 271 | /* Numbers first - easiest to check */ |
272 | /* Content-Length */ | |
273 | /* TODO: remove -1 bypass */ | |
62e76326 | 274 | |
06a5ae20 | 275 | if (content_length != otherRep->content_length |
276 | && content_length > -1 && | |
62e76326 | 277 | otherRep->content_length > -1) |
278 | return 0; | |
279 | ||
528b2c61 | 280 | /* ETag */ |
a9925b40 | 281 | one = header.getStrOrList(HDR_ETAG); |
62e76326 | 282 | |
a9925b40 | 283 | two = otherRep->header.getStrOrList(HDR_ETAG); |
62e76326 | 284 | |
9d7a89a5 | 285 | if (one.undefined() || two.undefined() || one.caseCmp(two)!=0 ) { |
30abd221 | 286 | one.clean(); |
287 | two.clean(); | |
62e76326 | 288 | return 0; |
528b2c61 | 289 | } |
62e76326 | 290 | |
06a5ae20 | 291 | if (last_modified != otherRep->last_modified) |
62e76326 | 292 | return 0; |
293 | ||
528b2c61 | 294 | /* MD5 */ |
a9925b40 | 295 | one = header.getStrOrList(HDR_CONTENT_MD5); |
62e76326 | 296 | |
a9925b40 | 297 | two = otherRep->header.getStrOrList(HDR_CONTENT_MD5); |
62e76326 | 298 | |
9d7a89a5 | 299 | if (one.undefined() || two.undefined() || one.caseCmp(two) != 0 ) { |
30abd221 | 300 | one.clean(); |
301 | two.clean(); | |
62e76326 | 302 | return 0; |
528b2c61 | 303 | } |
62e76326 | 304 | |
528b2c61 | 305 | return 1; |
306 | } | |
307 | ||
cb69b4c7 | 308 | void |
06a5ae20 | 309 | HttpReply::updateOnNotModified(HttpReply const * freshRep) |
cb69b4c7 | 310 | { |
07947ad8 | 311 | assert(freshRep); |
1d7ab0f4 | 312 | |
d8b249ef | 313 | /* clean cache */ |
06a5ae20 | 314 | hdrCacheClean(); |
d8b249ef | 315 | /* update raw headers */ |
a9925b40 | 316 | header.update(&freshRep->header, |
317 | (const HttpHeaderMask *) &Denied304HeadersMask); | |
1d7ab0f4 | 318 | |
394499bd | 319 | header.compact(); |
d8b249ef | 320 | /* init cache */ |
07947ad8 | 321 | hdrCacheInit(); |
cb69b4c7 | 322 | } |
323 | ||
d8b249ef | 324 | /* internal routines */ |
cb69b4c7 | 325 | |
06a5ae20 | 326 | time_t |
327 | HttpReply::hdrExpirationTime() | |
d20b1cd0 | 328 | { |
329 | /* The s-maxage and max-age directive takes priority over Expires */ | |
62e76326 | 330 | |
06a5ae20 | 331 | if (cache_control) { |
332 | if (date >= 0) { | |
333 | if (cache_control->s_maxage >= 0) | |
334 | return date + cache_control->s_maxage; | |
62e76326 | 335 | |
06a5ae20 | 336 | if (cache_control->max_age >= 0) |
337 | return date + cache_control->max_age; | |
62e76326 | 338 | } else { |
339 | /* | |
340 | * Conservatively handle the case when we have a max-age | |
341 | * header, but no Date for reference? | |
342 | */ | |
343 | ||
06a5ae20 | 344 | if (cache_control->s_maxage >= 0) |
62e76326 | 345 | return squid_curtime; |
346 | ||
06a5ae20 | 347 | if (cache_control->max_age >= 0) |
62e76326 | 348 | return squid_curtime; |
349 | } | |
d20b1cd0 | 350 | } |
62e76326 | 351 | |
f66a9ef4 | 352 | if (Config.onoff.vary_ignore_expire && |
a9925b40 | 353 | header.has(HDR_VARY)) { |
354 | const time_t d = header.getTime(HDR_DATE); | |
355 | const time_t e = header.getTime(HDR_EXPIRES); | |
62e76326 | 356 | |
357 | if (d == e) | |
358 | return -1; | |
f66a9ef4 | 359 | } |
62e76326 | 360 | |
a9925b40 | 361 | if (header.has(HDR_EXPIRES)) { |
362 | const time_t e = header.getTime(HDR_EXPIRES); | |
62e76326 | 363 | /* |
364 | * HTTP/1.0 says that robust implementations should consider | |
365 | * bad or malformed Expires header as equivalent to "expires | |
366 | * immediately." | |
367 | */ | |
368 | return e < 0 ? squid_curtime : e; | |
d20b1cd0 | 369 | } |
62e76326 | 370 | |
d20b1cd0 | 371 | return -1; |
372 | } | |
373 | ||
d8b249ef | 374 | /* sync this routine when you update HttpReply struct */ |
8596962e | 375 | void |
07947ad8 | 376 | HttpReply::hdrCacheInit() |
cb69b4c7 | 377 | { |
07947ad8 | 378 | HttpMsg::hdrCacheInit(); |
379 | ||
47f6e231 | 380 | content_length = header.getInt64(HDR_CONTENT_LENGTH); |
a9925b40 | 381 | date = header.getTime(HDR_DATE); |
382 | last_modified = header.getTime(HDR_LAST_MODIFIED); | |
383 | surrogate_control = header.getSc(); | |
384 | content_range = header.getContRange(); | |
07947ad8 | 385 | keep_alive = httpMsgIsPersistent(sline.version, &header); |
a9925b40 | 386 | const char *str = header.getStr(HDR_CONTENT_TYPE); |
62e76326 | 387 | |
d8b249ef | 388 | if (str) |
07947ad8 | 389 | content_type.limitInit(str, strcspn(str, ";\t ")); |
d8b249ef | 390 | else |
30abd221 | 391 | content_type = String(); |
62e76326 | 392 | |
d20b1cd0 | 393 | /* be sure to set expires after date and cache-control */ |
06a5ae20 | 394 | expires = hdrExpirationTime(); |
cb69b4c7 | 395 | } |
396 | ||
d8b249ef | 397 | /* sync this routine when you update HttpReply struct */ |
06a5ae20 | 398 | void |
399 | HttpReply::hdrCacheClean() | |
2ac76861 | 400 | { |
30abd221 | 401 | content_type.clean(); |
62e76326 | 402 | |
06a5ae20 | 403 | if (cache_control) { |
404 | httpHdrCcDestroy(cache_control); | |
405 | cache_control = NULL; | |
07947ad8 | 406 | } |
62e76326 | 407 | |
06a5ae20 | 408 | if (surrogate_control) { |
409 | httpHdrScDestroy(surrogate_control); | |
410 | surrogate_control = NULL; | |
07947ad8 | 411 | } |
43ae1d95 | 412 | |
06a5ae20 | 413 | if (content_range) { |
414 | httpHdrContRangeDestroy(content_range); | |
415 | content_range = NULL; | |
07947ad8 | 416 | } |
63259c34 | 417 | } |
cb69b4c7 | 418 | |
35282fbf | 419 | /* |
420 | * Returns the body size of a HTTP response | |
421 | */ | |
47f6e231 | 422 | int64_t |
60745f24 | 423 | HttpReply::bodySize(const HttpRequestMethod& method) const |
35282fbf | 424 | { |
06a5ae20 | 425 | if (sline.version.major < 1) |
1bda350e | 426 | return -1; |
914b89a2 | 427 | else if (method.id() == METHOD_HEAD) |
62e76326 | 428 | return 0; |
06a5ae20 | 429 | else if (sline.status == HTTP_OK) |
62e76326 | 430 | (void) 0; /* common case, continue */ |
06a5ae20 | 431 | else if (sline.status == HTTP_NO_CONTENT) |
62e76326 | 432 | return 0; |
06a5ae20 | 433 | else if (sline.status == HTTP_NOT_MODIFIED) |
62e76326 | 434 | return 0; |
06a5ae20 | 435 | else if (sline.status < HTTP_OK) |
62e76326 | 436 | return 0; |
437 | ||
06a5ae20 | 438 | return content_length; |
35282fbf | 439 | } |
8596962e | 440 | |
96ee497f AJ |
441 | /** |
442 | * Checks the first line of an HTTP Reply is valid. | |
443 | * currently only checks "HTTP/" exists. | |
444 | * | |
445 | * NP: not all error cases are detected yet. Some are left for detection later in parse. | |
446 | */ | |
447 | bool | |
448 | HttpReply::sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, http_status *error) | |
8596962e | 449 | { |
96ee497f AJ |
450 | // hack warning: using psize instead of size here due to type mismatches with MemBuf. |
451 | ||
452 | // content is long enough to possibly hold a reply | |
453 | // 4 being magic size of a 3-digit number plus space delimiter | |
454 | if ( buf->contentSize() < (protoPrefix.psize() + 4) ) { | |
0246f6b8 AJ |
455 | if (hdr_len > 0) { |
456 | debugs(58, 3, HERE << "Too small reply header (" << hdr_len << " bytes)"); | |
96ee497f | 457 | *error = HTTP_INVALID_HEADER; |
0246f6b8 | 458 | } |
96ee497f AJ |
459 | return false; |
460 | } | |
461 | ||
e77d7ef0 | 462 | int pos; |
96ee497f | 463 | // catch missing or mismatched protocol identifier |
e77d7ef0 AJ |
464 | // allow special-case for ICY protocol (non-HTTP identifier) in response to faked HTTP request. |
465 | if (strncmp(buf->content(), "ICY", 3) == 0) { | |
466 | protoPrefix = "ICY"; | |
467 | pos = protoPrefix.psize(); | |
dd20bfd3 | 468 | } else { |
8596962e | 469 | |
e77d7ef0 AJ |
470 | if (protoPrefix.cmp(buf->content(), protoPrefix.size()) != 0) { |
471 | debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol prefix (" << protoPrefix << ") in '" << buf->content() << "'"); | |
472 | *error = HTTP_INVALID_HEADER; | |
473 | return false; | |
474 | } | |
96ee497f | 475 | |
e77d7ef0 AJ |
476 | // catch missing or negative status value (negative '-' is not a digit) |
477 | pos = protoPrefix.psize(); | |
dd20bfd3 | 478 | |
e77d7ef0 AJ |
479 | // skip arbitrary number of digits and a dot in the verion portion |
480 | while ( pos <= buf->contentSize() && (*(buf->content()+pos) == '.' || xisdigit(*(buf->content()+pos)) ) ) ++pos; | |
96ee497f | 481 | |
e77d7ef0 AJ |
482 | // catch missing version info |
483 | if (pos == protoPrefix.psize()) { | |
484 | debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol version numbers (ie. " << protoPrefix << "/1.0) in '" << buf->content() << "'"); | |
485 | *error = HTTP_INVALID_HEADER; | |
486 | return false; | |
487 | } | |
96ee497f AJ |
488 | } |
489 | ||
490 | // skip arbitrary number of spaces... | |
491 | while (pos <= buf->contentSize() && (char)*(buf->content()+pos) == ' ') ++pos; | |
492 | ||
6934ca51 | 493 | if (pos < buf->contentSize() && !xisdigit(*(buf->content()+pos))) { |
96ee497f AJ |
494 | debugs(58, 3, "HttpReply::sanityCheckStartLine: missing or invalid status number in '" << buf->content() << "'"); |
495 | *error = HTTP_INVALID_HEADER; | |
496 | return false; | |
497 | } | |
498 | ||
8596962e | 499 | return true; |
500 | } | |
501 | ||
502 | void HttpReply::packFirstLineInto(Packer *p, bool unused) const | |
503 | { | |
504 | httpStatusLinePackInto(&sline, p); | |
505 | } | |
429f7150 | 506 | |
507 | bool HttpReply::parseFirstLine(const char *blk_start, const char *blk_end) | |
508 | { | |
509 | return httpStatusLineParse(&sline, protoPrefix, blk_start, blk_end); | |
510 | } | |
5c09dcb8 | 511 | |
fb525683 | 512 | /* handy: resets and returns -1 */ |
513 | int | |
514 | HttpReply::httpMsgParseError() | |
515 | { | |
516 | int result(HttpMsg::httpMsgParseError()); | |
517 | /* indicate an error in the status line */ | |
518 | sline.status = HTTP_INVALID_HEADER; | |
519 | return result; | |
520 | } | |
521 | ||
5c09dcb8 | 522 | /* |
523 | * Indicate whether or not we would usually expect an entity-body | |
524 | * along with this response | |
525 | */ | |
526 | bool | |
60745f24 | 527 | HttpReply::expectingBody(const HttpRequestMethod& req_method, int64_t& theSize) const |
5c09dcb8 | 528 | { |
529 | bool expectBody = true; | |
530 | ||
531 | if (req_method == METHOD_HEAD) | |
532 | expectBody = false; | |
533 | else if (sline.status == HTTP_NO_CONTENT) | |
534 | expectBody = false; | |
535 | else if (sline.status == HTTP_NOT_MODIFIED) | |
536 | expectBody = false; | |
537 | else if (sline.status < HTTP_OK) | |
538 | expectBody = false; | |
539 | else | |
540 | expectBody = true; | |
541 | ||
542 | if (expectBody) { | |
a9925b40 | 543 | if (header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ',')) |
5c09dcb8 | 544 | theSize = -1; |
545 | else if (content_length >= 0) | |
546 | theSize = content_length; | |
547 | else | |
548 | theSize = -1; | |
549 | } | |
550 | ||
551 | return expectBody; | |
552 | } | |
0667cbfb | 553 | |
554 | bool | |
555 | HttpReply::receivedBodyTooLarge(HttpRequest& request, int64_t receivedSize) | |
556 | { | |
557 | calcMaxBodySize(request); | |
558 | debugs(58, 3, HERE << receivedSize << " >? " << bodySizeMax); | |
559 | return bodySizeMax >= 0 && receivedSize > bodySizeMax; | |
560 | } | |
561 | ||
562 | bool | |
563 | HttpReply::expectedBodyTooLarge(HttpRequest& request) | |
564 | { | |
565 | calcMaxBodySize(request); | |
566 | debugs(58, 7, HERE << "bodySizeMax=" << bodySizeMax); | |
567 | ||
568 | if (bodySizeMax < 0) // no body size limit | |
569 | return false; | |
570 | ||
571 | int64_t expectedSize = -1; | |
572 | if (!expectingBody(request.method, expectedSize)) | |
573 | return false; | |
26ac0430 | 574 | |
0667cbfb | 575 | debugs(58, 6, HERE << expectedSize << " >? " << bodySizeMax); |
576 | ||
577 | if (expectedSize < 0) // expecting body of an unknown length | |
578 | return false; | |
579 | ||
580 | return expectedSize > bodySizeMax; | |
581 | } | |
582 | ||
583 | void | |
584 | HttpReply::calcMaxBodySize(HttpRequest& request) | |
585 | { | |
586 | // hack: -2 is used as "we have not calculated max body size yet" state | |
587 | if (bodySizeMax != -2) // already tried | |
588 | return; | |
589 | bodySizeMax = -1; | |
590 | ||
c0941a6a | 591 | ACLFilledChecklist ch(NULL, &request, NULL); |
0667cbfb | 592 | ch.src_addr = request.client_addr; |
593 | ch.my_addr = request.my_addr; | |
594 | ch.reply = HTTPMSGLOCK(this); // XXX: this lock makes method non-const | |
0667cbfb | 595 | for (acl_size_t *l = Config.ReplyBodySize; l; l = l -> next) { |
b50e327b AJ |
596 | /* if there is no ACL list or if the ACLs listed match use this size value */ |
597 | if (!l->aclList || ch.matchAclListFast(l->aclList)) { | |
0667cbfb | 598 | debugs(58, 4, HERE << "bodySizeMax=" << bodySizeMax); |
599 | bodySizeMax = l->size; // may be -1 | |
600 | break; | |
601 | } | |
602 | } | |
603 | } | |
da33c835 | 604 | |
fa0e6114 | 605 | // XXX: check that this is sufficient for eCAP cloning |
da33c835 HN |
606 | HttpReply * |
607 | HttpReply::clone() const | |
608 | { | |
609 | HttpReply *rep = new HttpReply(); | |
610 | rep->header.append(&header); | |
611 | rep->hdrCacheInit(); | |
612 | rep->hdr_sz = hdr_sz; | |
f230832f HN |
613 | rep->http_ver = http_ver; |
614 | rep->pstate = pstate; | |
fa0e6114 AR |
615 | rep->body_pipe = body_pipe; |
616 | ||
f230832f HN |
617 | rep->protocol = protocol; |
618 | rep->sline = sline; | |
d67acb4e | 619 | rep->keep_alive = keep_alive; |
da33c835 HN |
620 | return rep; |
621 | } | |
d67acb4e AJ |
622 | |
623 | ||
624 | bool HttpReply::inheritProperties(const HttpMsg *aMsg) | |
625 | { | |
626 | const HttpReply *aRep = dynamic_cast<const HttpReply*>(aMsg); | |
26ac0430 AJ |
627 | if (!aRep) |
628 | return false; | |
d67acb4e AJ |
629 | keep_alive = aRep->keep_alive; |
630 | return true; | |
631 | } |