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