]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpReply.cc
consistent formatting
[thirdparty/squid.git] / src / HttpReply.cc
CommitLineData
2ac76861 1
cb69b4c7 2/*
bffee5af 3 * $Id: HttpReply.cc,v 1.44 2000/12/05 09:15:57 wessels Exp $
cb69b4c7 4 *
123abbe1 5 * DEBUG: section 58 HTTP Reply (Response)
cb69b4c7 6 * AUTHOR: Alex Rousskov
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
e25c139f 9 * ----------------------------------------------------------
cb69b4c7 10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
e25c139f 13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
efd900cb 15 * the Regents of the University of California. Please see the
16 * COPYRIGHT file for full details. Squid incorporates software
17 * developed and/or copyrighted by other sources. Please see the
18 * 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
cb69b4c7 36#include "squid.h"
37
38
39/* local constants */
40
2246b732 41/* these entity-headers must be ignored if a bogus server sends them in 304 */
42static HttpHeaderMask Denied304HeadersMask;
43static http_hdr_type Denied304HeadersArr[] =
44{
45 HDR_ALLOW, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, HDR_CONTENT_LENGTH,
46 HDR_CONTENT_LOCATION, HDR_CONTENT_RANGE, HDR_LAST_MODIFIED, HDR_LINK,
47 HDR_OTHER
48};
49
cb69b4c7 50/* local routines */
2246b732 51static void httpReplyInit(HttpReply * rep);
52static void httpReplyClean(HttpReply * rep);
2ac76861 53static void httpReplyDoDestroy(HttpReply * rep);
d8b249ef 54static void httpReplyHdrCacheInit(HttpReply * rep);
55static void httpReplyHdrCacheClean(HttpReply * rep);
2ac76861 56static int httpReplyParseStep(HttpReply * rep, const char *parse_start, int atEnd);
57static int httpReplyParseError(HttpReply * rep);
cb69b4c7 58static int httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end);
d20b1cd0 59static time_t httpReplyHdrExpirationTime(const HttpReply * rep);
cb69b4c7 60
61
2246b732 62/* module initialization */
63void
9bc73deb 64httpReplyInitModule(void)
2246b732 65{
97474590 66 httpHeaderMaskInit(&Denied304HeadersMask, 0);
2246b732 67 httpHeaderCalcMask(&Denied304HeadersMask, (const int *) Denied304HeadersArr, countof(Denied304HeadersArr));
68}
69
70
cb69b4c7 71HttpReply *
9bc73deb 72httpReplyCreate(void)
cb69b4c7 73{
d8b249ef 74 HttpReply *rep = memAllocate(MEM_HTTP_REPLY);
399e85ea 75 debug(58, 7) ("creating rep: %p\n", rep);
cb69b4c7 76 httpReplyInit(rep);
77 return rep;
78}
79
5446b331 80static void
2ac76861 81httpReplyInit(HttpReply * rep)
cb69b4c7 82{
83 assert(rep);
84 rep->hdr_sz = 0;
85 rep->pstate = psReadyToParseStartLine;
86 httpBodyInit(&rep->body);
2246b732 87 httpHeaderInit(&rep->header, hoReply);
d8b249ef 88 httpReplyHdrCacheInit(rep);
cb69b4c7 89 httpStatusLineInit(&rep->sline);
90}
91
5446b331 92static void
2ac76861 93httpReplyClean(HttpReply * rep)
cb69b4c7 94{
95 assert(rep);
96 httpBodyClean(&rep->body);
d8b249ef 97 httpReplyHdrCacheClean(rep);
98 httpHeaderClean(&rep->header);
cb69b4c7 99 httpStatusLineClean(&rep->sline);
100}
101
102void
2ac76861 103httpReplyDestroy(HttpReply * rep)
cb69b4c7 104{
105 assert(rep);
399e85ea 106 debug(58, 7) ("destroying rep: %p\n", rep);
cb69b4c7 107 httpReplyClean(rep);
63259c34 108 httpReplyDoDestroy(rep);
cb69b4c7 109}
110
111void
2ac76861 112httpReplyReset(HttpReply * rep)
cb69b4c7 113{
114 httpReplyClean(rep);
115 httpReplyInit(rep);
116}
117
63259c34 118/* absorb: copy the contents of a new reply to the old one, destroy new one */
119void
2ac76861 120httpReplyAbsorb(HttpReply * rep, HttpReply * new_rep)
63259c34 121{
122 assert(rep && new_rep);
123 httpReplyClean(rep);
124 *rep = *new_rep;
125 /* cannot use Clean() on new reply now! */
126 httpReplyDoDestroy(new_rep);
127}
128
9bc73deb 129/*
130 * httpReplyParse takes character buffer of HTTP headers (buf),
131 * which may not be NULL-terminated, and fills in an HttpReply
132 * structure (rep). The parameter 'end' specifies the offset to
133 * the end of the reply headers. The caller may know where the
134 * end is, but is unable to NULL-terminate the buffer. This function
135 * returns true on success.
136 */
cb69b4c7 137int
9bc73deb 138httpReplyParse(HttpReply * rep, const char *buf, ssize_t end)
cb69b4c7 139{
140 /*
9bc73deb 141 * this extra buffer/copy will be eliminated when headers become
142 * meta-data in store. Currently we have to xstrncpy the buffer
143 * becuase somebody may feed a non NULL-terminated buffer to
144 * us.
cb69b4c7 145 */
7021844c 146 char *headers = memAllocate(MEM_4K_BUF);
cb69b4c7 147 int success;
25b0856c 148 size_t s = XMIN(end + 1, 4096);
cb69b4c7 149 /* reset current state, because we are not used in incremental fashion */
150 httpReplyReset(rep);
25b0856c 151 /* put a string terminator. s is how many bytes to touch in
152 * 'buf' including the terminating NULL. */
153 xstrncpy(headers, buf, s);
cb69b4c7 154 success = httpReplyParseStep(rep, headers, 0);
db1cd23c 155 memFree(headers, MEM_4K_BUF);
cb69b4c7 156 return success == 1;
157}
158
159void
2ac76861 160httpReplyPackInto(const HttpReply * rep, Packer * p)
cb69b4c7 161{
162 assert(rep);
163 httpStatusLinePackInto(&rep->sline, p);
d8b249ef 164 httpHeaderPackInto(&rep->header, p);
cb69b4c7 165 packerAppend(p, "\r\n", 2);
166 httpBodyPackInto(&rep->body, p);
167}
168
169/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */
170MemBuf
2ac76861 171httpReplyPack(const HttpReply * rep)
cb69b4c7 172{
173 MemBuf mb;
174 Packer p;
175 assert(rep);
176
177 memBufDefInit(&mb);
178 packerToMemInit(&p, &mb);
179 httpReplyPackInto(rep, &p);
180 packerClean(&p);
181 return mb;
182}
183
184/* swap: create swap-based packer, pack, destroy packer */
185void
2ac76861 186httpReplySwapOut(const HttpReply * rep, StoreEntry * e)
cb69b4c7 187{
188 Packer p;
189 assert(rep && e);
190
191 packerToStoreInit(&p, e);
192 httpReplyPackInto(rep, &p);
193 packerClean(&p);
194}
195
196MemBuf
ccf44862 197httpPackedReply(http_version_t ver, http_status status, const char *ctype,
cb69b4c7 198 int clen, time_t lmt, time_t expires)
199{
200 HttpReply *rep = httpReplyCreate();
201 MemBuf mb;
202 httpReplySetHeaders(rep, ver, status, ctype, NULL, clen, lmt, expires);
203 mb = httpReplyPack(rep);
204 httpReplyDestroy(rep);
205 return mb;
206}
207
208MemBuf
2ac76861 209httpPacked304Reply(const HttpReply * rep)
cb69b4c7 210{
b644367b 211 static const http_hdr_type ImsEntries[] =
6b8e7481 212 {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER};
728da2ee 213 int t;
cb69b4c7 214 MemBuf mb;
de336bbe 215 Packer p;
216 HttpHeaderEntry *e;
cb69b4c7 217 assert(rep);
218
cb69b4c7 219 memBufDefInit(&mb);
de336bbe 220 packerToMemInit(&p, &mb);
cb69b4c7 221 memBufPrintf(&mb, "%s", "HTTP/1.0 304 Not Modified\r\n");
de336bbe 222 for (t = 0; ImsEntries[t] != HDR_OTHER; ++t)
d8b249ef 223 if ((e = httpHeaderFindEntry(&rep->header, ImsEntries[t])))
de336bbe 224 httpHeaderEntryPackInto(e, &p);
cb69b4c7 225 memBufAppend(&mb, "\r\n", 2);
de336bbe 226 packerClean(&p);
cb69b4c7 227 return mb;
228}
229
230void
ccf44862 231httpReplySetHeaders(HttpReply * reply, http_version_t ver, http_status status, const char *reason,
cb69b4c7 232 const char *ctype, int clen, time_t lmt, time_t expires)
233{
234 HttpHeader *hdr;
235 assert(reply);
236 httpStatusLineSet(&reply->sline, ver, status, reason);
d8b249ef 237 hdr = &reply->header;
238 httpHeaderPutStr(hdr, HDR_SERVER, full_appname_string);
239 httpHeaderPutStr(hdr, HDR_MIME_VERSION, "1.0");
240 httpHeaderPutTime(hdr, HDR_DATE, squid_curtime);
241 if (ctype) {
242 httpHeaderPutStr(hdr, HDR_CONTENT_TYPE, ctype);
243 stringInit(&reply->content_type, ctype);
244 } else
245 reply->content_type = StringNull;
de336bbe 246 if (clen >= 0)
d8b249ef 247 httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, clen);
cb69b4c7 248 if (expires >= 0)
d8b249ef 249 httpHeaderPutTime(hdr, HDR_EXPIRES, expires);
2ac76861 250 if (lmt > 0) /* this used to be lmt != 0 @?@ */
d8b249ef 251 httpHeaderPutTime(hdr, HDR_LAST_MODIFIED, lmt);
252 reply->date = squid_curtime;
253 reply->content_length = clen;
254 reply->expires = expires;
255 reply->last_modified = lmt;
cb69b4c7 256}
257
6d38ef86 258void
259httpRedirectReply(HttpReply * reply, http_status status, const char *loc)
260{
261 HttpHeader *hdr;
ccf44862 262 http_version_t ver;
6d38ef86 263 assert(reply);
bffee5af 264 httpBuildVersion(&ver, 1, 0);
ccf44862 265 httpStatusLineSet(&reply->sline, ver, status, httpStatusString(status));
6d38ef86 266 hdr = &reply->header;
267 httpHeaderPutStr(hdr, HDR_SERVER, full_appname_string);
268 httpHeaderPutTime(hdr, HDR_DATE, squid_curtime);
269 httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, 0);
270 httpHeaderPutStr(hdr, HDR_LOCATION, loc);
271 reply->date = squid_curtime;
272 reply->content_length = 0;
273}
274
cb69b4c7 275void
2ac76861 276httpReplyUpdateOnNotModified(HttpReply * rep, HttpReply * freshRep)
cb69b4c7 277{
cb69b4c7 278 assert(rep && freshRep);
d8b249ef 279 /* clean cache */
280 httpReplyHdrCacheClean(rep);
281 /* update raw headers */
c68e9c6b 282 httpHeaderUpdate(&rep->header, &freshRep->header,
283 (const HttpHeaderMask *) &Denied304HeadersMask);
d8b249ef 284 /* init cache */
285 httpReplyHdrCacheInit(rep);
cb69b4c7 286}
287
cb69b4c7 288
d8b249ef 289/* internal routines */
cb69b4c7 290
d8b249ef 291/* internal function used by Destroy and Absorb */
292static void
293httpReplyDoDestroy(HttpReply * rep)
cb69b4c7 294{
db1cd23c 295 memFree(rep, MEM_HTTP_REPLY);
cb69b4c7 296}
297
d20b1cd0 298static time_t
299httpReplyHdrExpirationTime(const HttpReply * rep)
300{
301 /* The s-maxage and max-age directive takes priority over Expires */
302 if (rep->cache_control) {
303 if (rep->date >= 0) {
304 if (rep->cache_control->s_maxage >= 0)
305 return rep->date + rep->cache_control->s_maxage;
306 if (rep->cache_control->max_age >= 0)
307 return rep->date + rep->cache_control->max_age;
308 } else {
309 /*
310 * Conservatively handle the case when we have a max-age
311 * header, but no Date for reference?
312 */
313 if (rep->cache_control->s_maxage >= 0)
314 return squid_curtime;
315 if (rep->cache_control->max_age >= 0)
316 return squid_curtime;
317 }
318 }
319 if (httpHeaderHas(&rep->header, HDR_EXPIRES)) {
320 const time_t e = httpHeaderGetTime(&rep->header, HDR_EXPIRES);
321 /*
322 * HTTP/1.0 says that robust implementations should consider
323 * bad or malformed Expires header as equivalent to "expires
324 * immediately."
325 */
326 return e < 0 ? squid_curtime : e;
327 }
328 return -1;
329}
330
d8b249ef 331/* sync this routine when you update HttpReply struct */
332static void
333httpReplyHdrCacheInit(HttpReply * rep)
cb69b4c7 334{
d8b249ef 335 const HttpHeader *hdr = &rep->header;
336 const char *str;
337 rep->content_length = httpHeaderGetInt(hdr, HDR_CONTENT_LENGTH);
338 rep->date = httpHeaderGetTime(hdr, HDR_DATE);
339 rep->last_modified = httpHeaderGetTime(hdr, HDR_LAST_MODIFIED);
d8b249ef 340 str = httpHeaderGetStr(hdr, HDR_CONTENT_TYPE);
341 if (str)
342 stringLimitInit(&rep->content_type, str, strcspn(str, ";\t "));
343 else
344 rep->content_type = StringNull;
345 rep->cache_control = httpHeaderGetCc(hdr);
346 rep->content_range = httpHeaderGetContRange(hdr);
99edd1c3 347 rep->keep_alive = httpMsgIsPersistent(rep->sline.version, &rep->header);
d20b1cd0 348 /* be sure to set expires after date and cache-control */
349 rep->expires = httpReplyHdrExpirationTime(rep);
cb69b4c7 350}
351
d8b249ef 352/* sync this routine when you update HttpReply struct */
63259c34 353static void
d8b249ef 354httpReplyHdrCacheClean(HttpReply * rep)
2ac76861 355{
d8b249ef 356 stringClean(&rep->content_type);
357 if (rep->cache_control)
358 httpHdrCcDestroy(rep->cache_control);
359 if (rep->content_range)
360 httpHdrContRangeDestroy(rep->content_range);
63259c34 361}
cb69b4c7 362
363/*
364 * parses a 0-terminating buffer into HttpReply.
365 * Returns:
366 * +1 -- success
367 * 0 -- need more data (partial parse)
368 * -1 -- parse error
369 */
370static int
2ac76861 371httpReplyParseStep(HttpReply * rep, const char *buf, int atEnd)
cb69b4c7 372{
373 const char *parse_start = buf;
374 const char *blk_start, *blk_end;
375 const char **parse_end_ptr = &blk_end;
376 assert(rep);
377 assert(parse_start);
378 assert(rep->pstate < psParsed);
379
380 *parse_end_ptr = parse_start;
381 if (rep->pstate == psReadyToParseStartLine) {
382 if (!httpReplyIsolateStart(&parse_start, &blk_start, &blk_end))
383 return 0;
384 if (!httpStatusLineParse(&rep->sline, blk_start, blk_end))
385 return httpReplyParseError(rep);
386
387 *parse_end_ptr = parse_start;
388 rep->hdr_sz = *parse_end_ptr - buf;
2ac76861 389 rep->pstate++;
cb69b4c7 390 }
cb69b4c7 391 if (rep->pstate == psReadyToParseHeaders) {
99edd1c3 392 if (!httpMsgIsolateHeaders(&parse_start, &blk_start, &blk_end)) {
cb69b4c7 393 if (atEnd)
394 blk_start = parse_start, blk_end = blk_start + strlen(blk_start);
395 else
396 return 0;
5999b776 397 }
d8b249ef 398 if (!httpHeaderParse(&rep->header, blk_start, blk_end))
cb69b4c7 399 return httpReplyParseError(rep);
400
d8b249ef 401 httpReplyHdrCacheInit(rep);
402
cb69b4c7 403 *parse_end_ptr = parse_start;
404 rep->hdr_sz = *parse_end_ptr - buf;
405 rep->pstate++;
406 }
cb69b4c7 407 return 1;
408}
409
cb69b4c7 410/* handy: resets and returns -1 */
411static int
2ac76861 412httpReplyParseError(HttpReply * rep)
cb69b4c7 413{
414 assert(rep);
415 /* reset */
416 httpReplyReset(rep);
417 /* indicate an error */
418 rep->sline.status = HTTP_INVALID_HEADER;
419 return -1;
420}
421
422/* find first CRLF */
423static int
424httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end)
425{
426 int slen = strcspn(*parse_start, "\r\n");
2ac76861 427 if (!(*parse_start)[slen]) /* no CRLF found */
cb69b4c7 428 return 0;
429
430 *blk_start = *parse_start;
431 *blk_end = *blk_start + slen;
52639d06 432 while (**blk_end == '\r') /* CR */
cb69b4c7 433 (*blk_end)++;
2ac76861 434 if (**blk_end == '\n') /* LF */
cb69b4c7 435 (*blk_end)++;
436
437 *parse_start = *blk_end;
438 return 1;
439}
35282fbf 440
441/*
442 * Returns the body size of a HTTP response
443 */
444int
445httpReplyBodySize(method_t method, HttpReply * reply)
446{
447 if (METHOD_HEAD == method)
448 return 0;
449 else if (reply->sline.status == HTTP_OK)
450 (void) 0; /* common case, continue */
451 else if (reply->sline.status == HTTP_NO_CONTENT)
452 return 0;
453 else if (reply->sline.status == HTTP_NOT_MODIFIED)
454 return 0;
455 else if (reply->sline.status < HTTP_OK)
456 return 0;
457 return reply->content_length;
458}