]>
Commit | Line | Data |
---|---|---|
2ac76861 | 1 | |
cb69b4c7 | 2 | /* |
0353e724 | 3 | * $Id: HttpReply.cc,v 1.56 2003/03/06 11:51:55 robertc Exp $ |
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. | |
24 | * | |
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. | |
29 | * | |
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 | ||
528b2c61 | 36 | #include "HttpReply.h" |
cb69b4c7 | 37 | #include "squid.h" |
528b2c61 | 38 | #include "Store.h" |
39 | #include "HttpHeader.h" | |
40 | #include "HttpHdrContRange.h" | |
4fb35c3c | 41 | #include "ACLChecklist.h" |
cb69b4c7 | 42 | |
43 | /* local constants */ | |
44 | ||
2246b732 | 45 | /* these entity-headers must be ignored if a bogus server sends them in 304 */ |
46 | static HttpHeaderMask Denied304HeadersMask; | |
47 | static http_hdr_type Denied304HeadersArr[] = | |
62e76326 | 48 | { |
49 | HDR_ALLOW, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, HDR_CONTENT_LENGTH, | |
50 | HDR_CONTENT_LOCATION, HDR_CONTENT_RANGE, HDR_LAST_MODIFIED, HDR_LINK, | |
51 | HDR_OTHER | |
52 | }; | |
2246b732 | 53 | |
e6ccf245 | 54 | HttpMsgParseState &operator++ (HttpMsgParseState &aState) |
55 | { | |
56 | aState = (HttpMsgParseState)(++(int)aState); | |
57 | return aState; | |
58 | } | |
59 | ||
60 | ||
cb69b4c7 | 61 | /* local routines */ |
2246b732 | 62 | static void httpReplyInit(HttpReply * rep); |
63 | static void httpReplyClean(HttpReply * rep); | |
2ac76861 | 64 | static void httpReplyDoDestroy(HttpReply * rep); |
d8b249ef | 65 | static void httpReplyHdrCacheInit(HttpReply * rep); |
66 | static void httpReplyHdrCacheClean(HttpReply * rep); | |
2ac76861 | 67 | static int httpReplyParseStep(HttpReply * rep, const char *parse_start, int atEnd); |
68 | static int httpReplyParseError(HttpReply * rep); | |
cb69b4c7 | 69 | static int httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end); |
d20b1cd0 | 70 | static time_t httpReplyHdrExpirationTime(const HttpReply * rep); |
cb69b4c7 | 71 | |
72 | ||
2246b732 | 73 | /* module initialization */ |
74 | void | |
9bc73deb | 75 | httpReplyInitModule(void) |
2246b732 | 76 | { |
97474590 | 77 | httpHeaderMaskInit(&Denied304HeadersMask, 0); |
2246b732 | 78 | httpHeaderCalcMask(&Denied304HeadersMask, (const int *) Denied304HeadersArr, countof(Denied304HeadersArr)); |
79 | } | |
80 | ||
81 | ||
cb69b4c7 | 82 | HttpReply * |
9bc73deb | 83 | httpReplyCreate(void) |
cb69b4c7 | 84 | { |
0353e724 | 85 | HttpReply *rep = new HttpReply; |
399e85ea | 86 | debug(58, 7) ("creating rep: %p\n", rep); |
cb69b4c7 | 87 | httpReplyInit(rep); |
88 | return rep; | |
89 | } | |
90 | ||
5446b331 | 91 | static void |
2ac76861 | 92 | httpReplyInit(HttpReply * rep) |
cb69b4c7 | 93 | { |
94 | assert(rep); | |
95 | rep->hdr_sz = 0; | |
a560ee93 | 96 | rep->maxBodySize = 0; |
cb69b4c7 | 97 | rep->pstate = psReadyToParseStartLine; |
98 | httpBodyInit(&rep->body); | |
2246b732 | 99 | httpHeaderInit(&rep->header, hoReply); |
d8b249ef | 100 | httpReplyHdrCacheInit(rep); |
cb69b4c7 | 101 | httpStatusLineInit(&rep->sline); |
102 | } | |
103 | ||
5446b331 | 104 | static void |
2ac76861 | 105 | httpReplyClean(HttpReply * rep) |
cb69b4c7 | 106 | { |
107 | assert(rep); | |
108 | httpBodyClean(&rep->body); | |
d8b249ef | 109 | httpReplyHdrCacheClean(rep); |
110 | httpHeaderClean(&rep->header); | |
cb69b4c7 | 111 | httpStatusLineClean(&rep->sline); |
112 | } | |
113 | ||
114 | void | |
2ac76861 | 115 | httpReplyDestroy(HttpReply * rep) |
cb69b4c7 | 116 | { |
117 | assert(rep); | |
399e85ea | 118 | debug(58, 7) ("destroying rep: %p\n", rep); |
cb69b4c7 | 119 | httpReplyClean(rep); |
63259c34 | 120 | httpReplyDoDestroy(rep); |
cb69b4c7 | 121 | } |
122 | ||
123 | void | |
2ac76861 | 124 | httpReplyReset(HttpReply * rep) |
cb69b4c7 | 125 | { |
126 | httpReplyClean(rep); | |
127 | httpReplyInit(rep); | |
128 | } | |
129 | ||
63259c34 | 130 | /* absorb: copy the contents of a new reply to the old one, destroy new one */ |
131 | void | |
2ac76861 | 132 | httpReplyAbsorb(HttpReply * rep, HttpReply * new_rep) |
63259c34 | 133 | { |
134 | assert(rep && new_rep); | |
135 | httpReplyClean(rep); | |
136 | *rep = *new_rep; | |
528b2c61 | 137 | new_rep->header.entries.clean(); |
63259c34 | 138 | /* cannot use Clean() on new reply now! */ |
139 | httpReplyDoDestroy(new_rep); | |
140 | } | |
141 | ||
9bc73deb | 142 | /* |
143 | * httpReplyParse takes character buffer of HTTP headers (buf), | |
144 | * which may not be NULL-terminated, and fills in an HttpReply | |
145 | * structure (rep). The parameter 'end' specifies the offset to | |
146 | * the end of the reply headers. The caller may know where the | |
147 | * end is, but is unable to NULL-terminate the buffer. This function | |
148 | * returns true on success. | |
149 | */ | |
cb69b4c7 | 150 | int |
9bc73deb | 151 | httpReplyParse(HttpReply * rep, const char *buf, ssize_t end) |
cb69b4c7 | 152 | { |
153 | /* | |
9bc73deb | 154 | * this extra buffer/copy will be eliminated when headers become |
155 | * meta-data in store. Currently we have to xstrncpy the buffer | |
156 | * becuase somebody may feed a non NULL-terminated buffer to | |
157 | * us. | |
cb69b4c7 | 158 | */ |
e6ccf245 | 159 | char *headers = (char *)memAllocate(MEM_4K_BUF); |
cb69b4c7 | 160 | int success; |
25b0856c | 161 | size_t s = XMIN(end + 1, 4096); |
cb69b4c7 | 162 | /* reset current state, because we are not used in incremental fashion */ |
163 | httpReplyReset(rep); | |
25b0856c | 164 | /* put a string terminator. s is how many bytes to touch in |
165 | * 'buf' including the terminating NULL. */ | |
166 | xstrncpy(headers, buf, s); | |
cb69b4c7 | 167 | success = httpReplyParseStep(rep, headers, 0); |
db1cd23c | 168 | memFree(headers, MEM_4K_BUF); |
cb69b4c7 | 169 | return success == 1; |
170 | } | |
171 | ||
172 | void | |
528b2c61 | 173 | httpReplyPackHeadersInto(const HttpReply * rep, Packer * p) |
cb69b4c7 | 174 | { |
175 | assert(rep); | |
176 | httpStatusLinePackInto(&rep->sline, p); | |
d8b249ef | 177 | httpHeaderPackInto(&rep->header, p); |
cb69b4c7 | 178 | packerAppend(p, "\r\n", 2); |
528b2c61 | 179 | } |
180 | ||
181 | void | |
182 | httpReplyPackInto(const HttpReply * rep, Packer * p) | |
183 | { | |
184 | httpReplyPackHeadersInto(rep, p); | |
cb69b4c7 | 185 | httpBodyPackInto(&rep->body, p); |
186 | } | |
187 | ||
188 | /* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */ | |
189 | MemBuf | |
2ac76861 | 190 | httpReplyPack(const HttpReply * rep) |
cb69b4c7 | 191 | { |
192 | MemBuf mb; | |
193 | Packer p; | |
194 | assert(rep); | |
195 | ||
196 | memBufDefInit(&mb); | |
197 | packerToMemInit(&p, &mb); | |
198 | httpReplyPackInto(rep, &p); | |
199 | packerClean(&p); | |
200 | return mb; | |
201 | } | |
202 | ||
528b2c61 | 203 | /* swap: create swap-based packer, pack, destroy packer |
204 | * This eats the reply. | |
205 | */ | |
cb69b4c7 | 206 | void |
528b2c61 | 207 | httpReplySwapOut(HttpReply * rep, StoreEntry * e) |
cb69b4c7 | 208 | { |
cb69b4c7 | 209 | assert(rep && e); |
210 | ||
528b2c61 | 211 | storeEntryReplaceObject(e, rep); |
cb69b4c7 | 212 | } |
213 | ||
214 | MemBuf | |
ccf44862 | 215 | httpPackedReply(http_version_t ver, http_status status, const char *ctype, |
62e76326 | 216 | int clen, time_t lmt, time_t expires) |
cb69b4c7 | 217 | { |
218 | HttpReply *rep = httpReplyCreate(); | |
219 | MemBuf mb; | |
220 | httpReplySetHeaders(rep, ver, status, ctype, NULL, clen, lmt, expires); | |
221 | mb = httpReplyPack(rep); | |
222 | httpReplyDestroy(rep); | |
223 | return mb; | |
224 | } | |
225 | ||
528b2c61 | 226 | HttpReply * |
227 | httpReplyMake304 (const HttpReply * rep) | |
cb69b4c7 | 228 | { |
62e76326 | 229 | static const http_hdr_type ImsEntries[] = {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER}; |
230 | ||
528b2c61 | 231 | HttpReply *rv; |
728da2ee | 232 | int t; |
de336bbe | 233 | HttpHeaderEntry *e; |
528b2c61 | 234 | http_version_t ver; |
cb69b4c7 | 235 | assert(rep); |
236 | ||
528b2c61 | 237 | rv = httpReplyCreate (); |
238 | /* rv->content_length; */ | |
239 | rv->date = rep->date; | |
240 | rv->last_modified = rep->last_modified; | |
241 | rv->expires = rep->expires; | |
242 | rv->content_type = rep->content_type; | |
243 | /* rv->cache_control */ | |
244 | /* rv->content_range */ | |
245 | /* rv->keep_alive */ | |
246 | httpBuildVersion(&ver, 1, 0); | |
247 | httpStatusLineSet(&rv->sline, ver, | |
62e76326 | 248 | HTTP_NOT_MODIFIED, ""); |
249 | ||
de336bbe | 250 | for (t = 0; ImsEntries[t] != HDR_OTHER; ++t) |
62e76326 | 251 | if ((e = httpHeaderFindEntry(&rep->header, ImsEntries[t]))) |
252 | httpHeaderAddEntry(&rv->header, httpHeaderEntryClone(e)); | |
253 | ||
528b2c61 | 254 | /* rv->body */ |
255 | return rv; | |
256 | } | |
257 | ||
258 | MemBuf | |
259 | httpPacked304Reply(const HttpReply * rep) | |
260 | { | |
261 | /* Not as efficient as skipping the header duplication, | |
262 | * but easier to maintain | |
263 | */ | |
264 | HttpReply *temp; | |
265 | MemBuf rv; | |
266 | assert (rep); | |
267 | temp = httpReplyMake304 (rep); | |
268 | rv = httpReplyPack(temp); | |
269 | httpReplyDestroy (temp); | |
270 | return rv; | |
cb69b4c7 | 271 | } |
272 | ||
273 | void | |
ccf44862 | 274 | httpReplySetHeaders(HttpReply * reply, http_version_t ver, http_status status, const char *reason, |
62e76326 | 275 | const char *ctype, int clen, time_t lmt, time_t expires) |
cb69b4c7 | 276 | { |
277 | HttpHeader *hdr; | |
278 | assert(reply); | |
279 | httpStatusLineSet(&reply->sline, ver, status, reason); | |
d8b249ef | 280 | hdr = &reply->header; |
281 | httpHeaderPutStr(hdr, HDR_SERVER, full_appname_string); | |
282 | httpHeaderPutStr(hdr, HDR_MIME_VERSION, "1.0"); | |
283 | httpHeaderPutTime(hdr, HDR_DATE, squid_curtime); | |
62e76326 | 284 | |
d8b249ef | 285 | if (ctype) { |
62e76326 | 286 | httpHeaderPutStr(hdr, HDR_CONTENT_TYPE, ctype); |
287 | reply->content_type = ctype; | |
d8b249ef | 288 | } else |
650c4b88 | 289 | reply->content_type = String(); |
62e76326 | 290 | |
de336bbe | 291 | if (clen >= 0) |
62e76326 | 292 | httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, clen); |
293 | ||
cb69b4c7 | 294 | if (expires >= 0) |
62e76326 | 295 | httpHeaderPutTime(hdr, HDR_EXPIRES, expires); |
296 | ||
2ac76861 | 297 | if (lmt > 0) /* this used to be lmt != 0 @?@ */ |
62e76326 | 298 | httpHeaderPutTime(hdr, HDR_LAST_MODIFIED, lmt); |
299 | ||
d8b249ef | 300 | reply->date = squid_curtime; |
62e76326 | 301 | |
d8b249ef | 302 | reply->content_length = clen; |
62e76326 | 303 | |
d8b249ef | 304 | reply->expires = expires; |
62e76326 | 305 | |
d8b249ef | 306 | reply->last_modified = lmt; |
cb69b4c7 | 307 | } |
308 | ||
6d38ef86 | 309 | void |
310 | httpRedirectReply(HttpReply * reply, http_status status, const char *loc) | |
311 | { | |
312 | HttpHeader *hdr; | |
ccf44862 | 313 | http_version_t ver; |
6d38ef86 | 314 | assert(reply); |
bffee5af | 315 | httpBuildVersion(&ver, 1, 0); |
ccf44862 | 316 | httpStatusLineSet(&reply->sline, ver, status, httpStatusString(status)); |
6d38ef86 | 317 | hdr = &reply->header; |
318 | httpHeaderPutStr(hdr, HDR_SERVER, full_appname_string); | |
319 | httpHeaderPutTime(hdr, HDR_DATE, squid_curtime); | |
320 | httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, 0); | |
321 | httpHeaderPutStr(hdr, HDR_LOCATION, loc); | |
322 | reply->date = squid_curtime; | |
323 | reply->content_length = 0; | |
324 | } | |
325 | ||
528b2c61 | 326 | /* compare the validators of two replies. |
327 | * 1 = they match | |
328 | * 0 = they do not match | |
329 | */ | |
330 | int | |
62e76326 | 331 | httpReplyValidatorsMatch(HttpReply const * rep, HttpReply const * otherRep) |
332 | { | |
528b2c61 | 333 | String one,two; |
334 | assert (rep && otherRep); | |
335 | /* Numbers first - easiest to check */ | |
336 | /* Content-Length */ | |
337 | /* TODO: remove -1 bypass */ | |
62e76326 | 338 | |
528b2c61 | 339 | if (rep->content_length != otherRep->content_length |
62e76326 | 340 | && rep->content_length > -1 && |
341 | otherRep->content_length > -1) | |
342 | return 0; | |
343 | ||
528b2c61 | 344 | /* ETag */ |
345 | one = httpHeaderGetStrOrList(&rep->header, HDR_ETAG); | |
62e76326 | 346 | |
528b2c61 | 347 | two = httpHeaderGetStrOrList(&otherRep->header, HDR_ETAG); |
62e76326 | 348 | |
528b2c61 | 349 | if (!one.buf() || !two.buf() || strcasecmp (one.buf(), two.buf())) { |
62e76326 | 350 | one.clean(); |
351 | two.clean(); | |
352 | return 0; | |
528b2c61 | 353 | } |
62e76326 | 354 | |
528b2c61 | 355 | if (rep->last_modified != otherRep->last_modified) |
62e76326 | 356 | return 0; |
357 | ||
528b2c61 | 358 | /* MD5 */ |
359 | one = httpHeaderGetStrOrList(&rep->header, HDR_CONTENT_MD5); | |
62e76326 | 360 | |
528b2c61 | 361 | two = httpHeaderGetStrOrList(&otherRep->header, HDR_CONTENT_MD5); |
62e76326 | 362 | |
528b2c61 | 363 | if (strcasecmp (one.buf(), two.buf())) { |
62e76326 | 364 | one.clean(); |
365 | two.clean(); | |
366 | return 0; | |
528b2c61 | 367 | } |
62e76326 | 368 | |
528b2c61 | 369 | return 1; |
370 | } | |
371 | ||
372 | ||
cb69b4c7 | 373 | void |
528b2c61 | 374 | httpReplyUpdateOnNotModified(HttpReply * rep, HttpReply const * freshRep) |
cb69b4c7 | 375 | { |
cb69b4c7 | 376 | assert(rep && freshRep); |
528b2c61 | 377 | /* Can not update modified headers that don't match! */ |
378 | assert (httpReplyValidatorsMatch(rep, freshRep)); | |
d8b249ef | 379 | /* clean cache */ |
380 | httpReplyHdrCacheClean(rep); | |
381 | /* update raw headers */ | |
c68e9c6b | 382 | httpHeaderUpdate(&rep->header, &freshRep->header, |
62e76326 | 383 | (const HttpHeaderMask *) &Denied304HeadersMask); |
d8b249ef | 384 | /* init cache */ |
385 | httpReplyHdrCacheInit(rep); | |
cb69b4c7 | 386 | } |
387 | ||
cb69b4c7 | 388 | |
d8b249ef | 389 | /* internal routines */ |
cb69b4c7 | 390 | |
d8b249ef | 391 | /* internal function used by Destroy and Absorb */ |
392 | static void | |
393 | httpReplyDoDestroy(HttpReply * rep) | |
cb69b4c7 | 394 | { |
0353e724 | 395 | delete rep; |
cb69b4c7 | 396 | } |
397 | ||
d20b1cd0 | 398 | static time_t |
399 | httpReplyHdrExpirationTime(const HttpReply * rep) | |
400 | { | |
401 | /* The s-maxage and max-age directive takes priority over Expires */ | |
62e76326 | 402 | |
d20b1cd0 | 403 | if (rep->cache_control) { |
62e76326 | 404 | if (rep->date >= 0) { |
405 | if (rep->cache_control->s_maxage >= 0) | |
406 | return rep->date + rep->cache_control->s_maxage; | |
407 | ||
408 | if (rep->cache_control->max_age >= 0) | |
409 | return rep->date + rep->cache_control->max_age; | |
410 | } else { | |
411 | /* | |
412 | * Conservatively handle the case when we have a max-age | |
413 | * header, but no Date for reference? | |
414 | */ | |
415 | ||
416 | if (rep->cache_control->s_maxage >= 0) | |
417 | return squid_curtime; | |
418 | ||
419 | if (rep->cache_control->max_age >= 0) | |
420 | return squid_curtime; | |
421 | } | |
d20b1cd0 | 422 | } |
62e76326 | 423 | |
f66a9ef4 | 424 | if (Config.onoff.vary_ignore_expire && |
62e76326 | 425 | httpHeaderHas(&rep->header, HDR_VARY)) { |
426 | const time_t d = httpHeaderGetTime(&rep->header, HDR_DATE); | |
427 | const time_t e = httpHeaderGetTime(&rep->header, HDR_EXPIRES); | |
428 | ||
429 | if (d == e) | |
430 | return -1; | |
f66a9ef4 | 431 | } |
62e76326 | 432 | |
d20b1cd0 | 433 | if (httpHeaderHas(&rep->header, HDR_EXPIRES)) { |
62e76326 | 434 | const time_t e = httpHeaderGetTime(&rep->header, HDR_EXPIRES); |
435 | /* | |
436 | * HTTP/1.0 says that robust implementations should consider | |
437 | * bad or malformed Expires header as equivalent to "expires | |
438 | * immediately." | |
439 | */ | |
440 | return e < 0 ? squid_curtime : e; | |
d20b1cd0 | 441 | } |
62e76326 | 442 | |
d20b1cd0 | 443 | return -1; |
444 | } | |
445 | ||
d8b249ef | 446 | /* sync this routine when you update HttpReply struct */ |
447 | static void | |
448 | httpReplyHdrCacheInit(HttpReply * rep) | |
cb69b4c7 | 449 | { |
d8b249ef | 450 | const HttpHeader *hdr = &rep->header; |
451 | const char *str; | |
452 | rep->content_length = httpHeaderGetInt(hdr, HDR_CONTENT_LENGTH); | |
453 | rep->date = httpHeaderGetTime(hdr, HDR_DATE); | |
454 | rep->last_modified = httpHeaderGetTime(hdr, HDR_LAST_MODIFIED); | |
d8b249ef | 455 | str = httpHeaderGetStr(hdr, HDR_CONTENT_TYPE); |
62e76326 | 456 | |
d8b249ef | 457 | if (str) |
62e76326 | 458 | rep->content_type.limitInit(str, strcspn(str, ";\t ")); |
d8b249ef | 459 | else |
650c4b88 | 460 | rep->content_type = String(); |
62e76326 | 461 | |
d8b249ef | 462 | rep->cache_control = httpHeaderGetCc(hdr); |
62e76326 | 463 | |
d8b249ef | 464 | rep->content_range = httpHeaderGetContRange(hdr); |
62e76326 | 465 | |
99edd1c3 | 466 | rep->keep_alive = httpMsgIsPersistent(rep->sline.version, &rep->header); |
62e76326 | 467 | |
d20b1cd0 | 468 | /* be sure to set expires after date and cache-control */ |
469 | rep->expires = httpReplyHdrExpirationTime(rep); | |
cb69b4c7 | 470 | } |
471 | ||
d8b249ef | 472 | /* sync this routine when you update HttpReply struct */ |
63259c34 | 473 | static void |
d8b249ef | 474 | httpReplyHdrCacheClean(HttpReply * rep) |
2ac76861 | 475 | { |
528b2c61 | 476 | rep->content_type.clean(); |
62e76326 | 477 | |
d8b249ef | 478 | if (rep->cache_control) |
62e76326 | 479 | httpHdrCcDestroy(rep->cache_control); |
480 | ||
d8b249ef | 481 | if (rep->content_range) |
62e76326 | 482 | httpHdrContRangeDestroy(rep->content_range); |
63259c34 | 483 | } |
cb69b4c7 | 484 | |
485 | /* | |
486 | * parses a 0-terminating buffer into HttpReply. | |
487 | * Returns: | |
528b2c61 | 488 | * 1 -- success |
cb69b4c7 | 489 | * 0 -- need more data (partial parse) |
490 | * -1 -- parse error | |
491 | */ | |
492 | static int | |
2ac76861 | 493 | httpReplyParseStep(HttpReply * rep, const char *buf, int atEnd) |
cb69b4c7 | 494 | { |
495 | const char *parse_start = buf; | |
496 | const char *blk_start, *blk_end; | |
497 | const char **parse_end_ptr = &blk_end; | |
498 | assert(rep); | |
499 | assert(parse_start); | |
500 | assert(rep->pstate < psParsed); | |
501 | ||
502 | *parse_end_ptr = parse_start; | |
62e76326 | 503 | |
cb69b4c7 | 504 | if (rep->pstate == psReadyToParseStartLine) { |
62e76326 | 505 | if (!httpReplyIsolateStart(&parse_start, &blk_start, &blk_end)) |
506 | return 0; | |
507 | ||
508 | if (!httpStatusLineParse(&rep->sline, blk_start, blk_end)) | |
509 | return httpReplyParseError(rep); | |
510 | ||
511 | *parse_end_ptr = parse_start; | |
512 | ||
513 | rep->hdr_sz = *parse_end_ptr - buf; | |
514 | ||
515 | ++rep->pstate; | |
cb69b4c7 | 516 | } |
62e76326 | 517 | |
cb69b4c7 | 518 | if (rep->pstate == psReadyToParseHeaders) { |
62e76326 | 519 | if (!httpMsgIsolateHeaders(&parse_start, &blk_start, &blk_end)) { |
520 | if (atEnd) | |
521 | blk_start = parse_start, blk_end = blk_start + strlen(blk_start); | |
522 | else | |
523 | return 0; | |
524 | } | |
525 | ||
526 | if (!httpHeaderParse(&rep->header, blk_start, blk_end)) | |
527 | return httpReplyParseError(rep); | |
528 | ||
529 | httpReplyHdrCacheInit(rep); | |
530 | ||
531 | *parse_end_ptr = parse_start; | |
532 | ||
533 | rep->hdr_sz = *parse_end_ptr - buf; | |
534 | ||
535 | ++rep->pstate; | |
cb69b4c7 | 536 | } |
62e76326 | 537 | |
cb69b4c7 | 538 | return 1; |
539 | } | |
540 | ||
cb69b4c7 | 541 | /* handy: resets and returns -1 */ |
542 | static int | |
2ac76861 | 543 | httpReplyParseError(HttpReply * rep) |
cb69b4c7 | 544 | { |
545 | assert(rep); | |
546 | /* reset */ | |
547 | httpReplyReset(rep); | |
548 | /* indicate an error */ | |
549 | rep->sline.status = HTTP_INVALID_HEADER; | |
550 | return -1; | |
551 | } | |
552 | ||
553 | /* find first CRLF */ | |
554 | static int | |
555 | httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end) | |
556 | { | |
557 | int slen = strcspn(*parse_start, "\r\n"); | |
62e76326 | 558 | |
2ac76861 | 559 | if (!(*parse_start)[slen]) /* no CRLF found */ |
62e76326 | 560 | return 0; |
cb69b4c7 | 561 | |
562 | *blk_start = *parse_start; | |
62e76326 | 563 | |
cb69b4c7 | 564 | *blk_end = *blk_start + slen; |
62e76326 | 565 | |
52639d06 | 566 | while (**blk_end == '\r') /* CR */ |
62e76326 | 567 | (*blk_end)++; |
568 | ||
2ac76861 | 569 | if (**blk_end == '\n') /* LF */ |
62e76326 | 570 | (*blk_end)++; |
cb69b4c7 | 571 | |
572 | *parse_start = *blk_end; | |
62e76326 | 573 | |
cb69b4c7 | 574 | return 1; |
575 | } | |
35282fbf | 576 | |
577 | /* | |
578 | * Returns the body size of a HTTP response | |
579 | */ | |
580 | int | |
528b2c61 | 581 | httpReplyBodySize(method_t method, HttpReply const * reply) |
35282fbf | 582 | { |
583 | if (METHOD_HEAD == method) | |
62e76326 | 584 | return 0; |
35282fbf | 585 | else if (reply->sline.status == HTTP_OK) |
62e76326 | 586 | (void) 0; /* common case, continue */ |
35282fbf | 587 | else if (reply->sline.status == HTTP_NO_CONTENT) |
62e76326 | 588 | return 0; |
35282fbf | 589 | else if (reply->sline.status == HTTP_NOT_MODIFIED) |
62e76326 | 590 | return 0; |
35282fbf | 591 | else if (reply->sline.status < HTTP_OK) |
62e76326 | 592 | return 0; |
593 | ||
35282fbf | 594 | return reply->content_length; |
595 | } | |
a560ee93 | 596 | |
597 | /* | |
598 | * Calculates the maximum size allowed for an HTTP response | |
599 | */ | |
600 | void | |
b671cc68 | 601 | httpReplyBodyBuildSize(request_t * request, HttpReply * reply, dlink_list * bodylist) |
a560ee93 | 602 | { |
603 | body_size *bs; | |
4fb35c3c | 604 | ACLChecklist *checklist; |
a560ee93 | 605 | bs = (body_size *) bodylist->head; |
62e76326 | 606 | |
a560ee93 | 607 | while (bs) { |
62e76326 | 608 | checklist = aclChecklistCreate(bs->access_list, request, NULL); |
609 | checklist->reply = reply; | |
610 | ||
611 | if (1 != aclCheckFast(bs->access_list, checklist)) { | |
612 | /* deny - skip this entry */ | |
613 | bs = (body_size *) bs->node.next; | |
614 | } else { | |
615 | /* Allow - use this entry */ | |
616 | reply->maxBodySize = bs->maxsize; | |
617 | bs = NULL; | |
618 | debug(58, 3) ("httpReplyBodyBuildSize: Setting maxBodySize to %ld\n", (long int) reply->maxBodySize); | |
619 | } | |
620 | ||
621 | delete checklist; | |
a560ee93 | 622 | } |
623 | } | |
0353e724 | 624 | |
625 | MemPool *HttpReply::Pool(NULL); | |
626 | void * | |
627 | HttpReply::operator new (size_t byteCount) | |
628 | { | |
629 | /* derived classes with different sizes must implement their own new */ | |
630 | assert (byteCount == sizeof (HttpReply)); | |
631 | ||
632 | if (!Pool) | |
633 | Pool = memPoolCreate("HttpReply", sizeof (HttpReply)); | |
634 | ||
635 | return memPoolAlloc(Pool); | |
636 | } | |
637 | ||
638 | void | |
639 | HttpReply::operator delete (void *address) | |
640 | { | |
641 | memPoolFree (Pool, address); | |
642 | } |