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