]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpReply.cc
gindent
[thirdparty/squid.git] / src / HttpReply.cc
CommitLineData
2ac76861 1
cb69b4c7 2/*
6b8e7481 3 * $Id: HttpReply.cc,v 1.36 1999/04/23 02:57:16 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
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * 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
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);
cb69b4c7 59
60
2246b732 61/* module initialization */
62void
63httpReplyInitModule()
64{
97474590 65 httpHeaderMaskInit(&Denied304HeadersMask, 0);
2246b732 66 httpHeaderCalcMask(&Denied304HeadersMask, (const int *) Denied304HeadersArr, countof(Denied304HeadersArr));
67}
68
69
cb69b4c7 70HttpReply *
71httpReplyCreate()
72{
d8b249ef 73 HttpReply *rep = memAllocate(MEM_HTTP_REPLY);
399e85ea 74 debug(58, 7) ("creating rep: %p\n", rep);
cb69b4c7 75 httpReplyInit(rep);
76 return rep;
77}
78
5446b331 79static void
2ac76861 80httpReplyInit(HttpReply * rep)
cb69b4c7 81{
82 assert(rep);
83 rep->hdr_sz = 0;
84 rep->pstate = psReadyToParseStartLine;
85 httpBodyInit(&rep->body);
2246b732 86 httpHeaderInit(&rep->header, hoReply);
d8b249ef 87 httpReplyHdrCacheInit(rep);
cb69b4c7 88 httpStatusLineInit(&rep->sline);
89}
90
5446b331 91static void
2ac76861 92httpReplyClean(HttpReply * rep)
cb69b4c7 93{
94 assert(rep);
95 httpBodyClean(&rep->body);
d8b249ef 96 httpReplyHdrCacheClean(rep);
97 httpHeaderClean(&rep->header);
cb69b4c7 98 httpStatusLineClean(&rep->sline);
99}
100
101void
2ac76861 102httpReplyDestroy(HttpReply * rep)
cb69b4c7 103{
104 assert(rep);
399e85ea 105 debug(58, 7) ("destroying rep: %p\n", rep);
cb69b4c7 106 httpReplyClean(rep);
63259c34 107 httpReplyDoDestroy(rep);
cb69b4c7 108}
109
110void
2ac76861 111httpReplyReset(HttpReply * rep)
cb69b4c7 112{
113 httpReplyClean(rep);
114 httpReplyInit(rep);
115}
116
63259c34 117/* absorb: copy the contents of a new reply to the old one, destroy new one */
118void
2ac76861 119httpReplyAbsorb(HttpReply * rep, HttpReply * new_rep)
63259c34 120{
121 assert(rep && new_rep);
122 httpReplyClean(rep);
123 *rep = *new_rep;
124 /* cannot use Clean() on new reply now! */
125 httpReplyDoDestroy(new_rep);
126}
127
2246b732 128/* parses a 4K buffer that may not be 0-terminated; returns true on success */
cb69b4c7 129int
2ac76861 130httpReplyParse(HttpReply * rep, const char *buf)
cb69b4c7 131{
132 /*
133 * this extra buffer/copy will be eliminated when headers become meta-data
134 * in store. Currently we have to xstrncpy the buffer becuase store.c may
2246b732 135 * feed a non 0-terminated buffer to us.
cb69b4c7 136 */
7021844c 137 char *headers = memAllocate(MEM_4K_BUF);
cb69b4c7 138 int success;
139 /* reset current state, because we are not used in incremental fashion */
140 httpReplyReset(rep);
141 /* put a 0-terminator */
142 xstrncpy(headers, buf, 4096);
143 success = httpReplyParseStep(rep, headers, 0);
db1cd23c 144 memFree(headers, MEM_4K_BUF);
cb69b4c7 145 return success == 1;
146}
147
148void
2ac76861 149httpReplyPackInto(const HttpReply * rep, Packer * p)
cb69b4c7 150{
151 assert(rep);
152 httpStatusLinePackInto(&rep->sline, p);
d8b249ef 153 httpHeaderPackInto(&rep->header, p);
cb69b4c7 154 packerAppend(p, "\r\n", 2);
155 httpBodyPackInto(&rep->body, p);
156}
157
158/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */
159MemBuf
2ac76861 160httpReplyPack(const HttpReply * rep)
cb69b4c7 161{
162 MemBuf mb;
163 Packer p;
164 assert(rep);
165
166 memBufDefInit(&mb);
167 packerToMemInit(&p, &mb);
168 httpReplyPackInto(rep, &p);
169 packerClean(&p);
170 return mb;
171}
172
173/* swap: create swap-based packer, pack, destroy packer */
174void
2ac76861 175httpReplySwapOut(const HttpReply * rep, StoreEntry * e)
cb69b4c7 176{
177 Packer p;
178 assert(rep && e);
179
180 packerToStoreInit(&p, e);
181 httpReplyPackInto(rep, &p);
182 packerClean(&p);
183}
184
185MemBuf
186httpPackedReply(double ver, http_status status, const char *ctype,
187 int clen, time_t lmt, time_t expires)
188{
189 HttpReply *rep = httpReplyCreate();
190 MemBuf mb;
191 httpReplySetHeaders(rep, ver, status, ctype, NULL, clen, lmt, expires);
192 mb = httpReplyPack(rep);
193 httpReplyDestroy(rep);
194 return mb;
195}
196
197MemBuf
2ac76861 198httpPacked304Reply(const HttpReply * rep)
cb69b4c7 199{
b644367b 200 static const http_hdr_type ImsEntries[] =
6b8e7481 201 {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER};
728da2ee 202 int t;
cb69b4c7 203 MemBuf mb;
de336bbe 204 Packer p;
205 HttpHeaderEntry *e;
cb69b4c7 206 assert(rep);
207
cb69b4c7 208 memBufDefInit(&mb);
de336bbe 209 packerToMemInit(&p, &mb);
cb69b4c7 210 memBufPrintf(&mb, "%s", "HTTP/1.0 304 Not Modified\r\n");
de336bbe 211 for (t = 0; ImsEntries[t] != HDR_OTHER; ++t)
d8b249ef 212 if ((e = httpHeaderFindEntry(&rep->header, ImsEntries[t])))
de336bbe 213 httpHeaderEntryPackInto(e, &p);
cb69b4c7 214 memBufAppend(&mb, "\r\n", 2);
de336bbe 215 packerClean(&p);
cb69b4c7 216 return mb;
217}
218
219void
2ac76861 220httpReplySetHeaders(HttpReply * reply, double ver, http_status status, const char *reason,
cb69b4c7 221 const char *ctype, int clen, time_t lmt, time_t expires)
222{
223 HttpHeader *hdr;
224 assert(reply);
225 httpStatusLineSet(&reply->sline, ver, status, reason);
d8b249ef 226 hdr = &reply->header;
227 httpHeaderPutStr(hdr, HDR_SERVER, full_appname_string);
228 httpHeaderPutStr(hdr, HDR_MIME_VERSION, "1.0");
229 httpHeaderPutTime(hdr, HDR_DATE, squid_curtime);
230 if (ctype) {
231 httpHeaderPutStr(hdr, HDR_CONTENT_TYPE, ctype);
232 stringInit(&reply->content_type, ctype);
233 } else
234 reply->content_type = StringNull;
de336bbe 235 if (clen >= 0)
d8b249ef 236 httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, clen);
cb69b4c7 237 if (expires >= 0)
d8b249ef 238 httpHeaderPutTime(hdr, HDR_EXPIRES, expires);
2ac76861 239 if (lmt > 0) /* this used to be lmt != 0 @?@ */
d8b249ef 240 httpHeaderPutTime(hdr, HDR_LAST_MODIFIED, lmt);
241 reply->date = squid_curtime;
242 reply->content_length = clen;
243 reply->expires = expires;
244 reply->last_modified = lmt;
cb69b4c7 245}
246
6d38ef86 247void
248httpRedirectReply(HttpReply * reply, http_status status, const char *loc)
249{
250 HttpHeader *hdr;
251 assert(reply);
252 httpStatusLineSet(&reply->sline, 1.0, status, httpStatusString(status));
253 hdr = &reply->header;
254 httpHeaderPutStr(hdr, HDR_SERVER, full_appname_string);
255 httpHeaderPutTime(hdr, HDR_DATE, squid_curtime);
256 httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, 0);
257 httpHeaderPutStr(hdr, HDR_LOCATION, loc);
258 reply->date = squid_curtime;
259 reply->content_length = 0;
260}
261
cb69b4c7 262void
2ac76861 263httpReplyUpdateOnNotModified(HttpReply * rep, HttpReply * freshRep)
cb69b4c7 264{
cb69b4c7 265 assert(rep && freshRep);
d8b249ef 266 /* clean cache */
267 httpReplyHdrCacheClean(rep);
268 /* update raw headers */
c68e9c6b 269 httpHeaderUpdate(&rep->header, &freshRep->header,
270 (const HttpHeaderMask *) &Denied304HeadersMask);
d8b249ef 271 /* init cache */
272 httpReplyHdrCacheInit(rep);
cb69b4c7 273}
274
cb69b4c7 275
d8b249ef 276/* internal routines */
cb69b4c7 277
d8b249ef 278/* internal function used by Destroy and Absorb */
279static void
280httpReplyDoDestroy(HttpReply * rep)
cb69b4c7 281{
db1cd23c 282 memFree(rep, MEM_HTTP_REPLY);
cb69b4c7 283}
284
d8b249ef 285/* sync this routine when you update HttpReply struct */
286static void
287httpReplyHdrCacheInit(HttpReply * rep)
cb69b4c7 288{
d8b249ef 289 const HttpHeader *hdr = &rep->header;
290 const char *str;
291 rep->content_length = httpHeaderGetInt(hdr, HDR_CONTENT_LENGTH);
292 rep->date = httpHeaderGetTime(hdr, HDR_DATE);
293 rep->last_modified = httpHeaderGetTime(hdr, HDR_LAST_MODIFIED);
294 rep->expires = httpHeaderGetTime(hdr, HDR_EXPIRES);
295 str = httpHeaderGetStr(hdr, HDR_CONTENT_TYPE);
296 if (str)
297 stringLimitInit(&rep->content_type, str, strcspn(str, ";\t "));
298 else
299 rep->content_type = StringNull;
300 rep->cache_control = httpHeaderGetCc(hdr);
301 rep->content_range = httpHeaderGetContRange(hdr);
99edd1c3 302 rep->keep_alive = httpMsgIsPersistent(rep->sline.version, &rep->header);
d8b249ef 303 /* final adjustments */
304 /* The max-age directive takes priority over Expires, check it first */
305 if (rep->cache_control && rep->cache_control->max_age >= 0)
306 rep->expires = squid_curtime + rep->cache_control->max_age;
307 else
b644367b 308 /*
309 * The HTTP/1.0 specs says that robust implementations should consider bad
310 * or malformed Expires header as equivalent to "expires immediately."
311 */
d8b249ef 312 if (rep->expires < 0 && httpHeaderHas(hdr, HDR_EXPIRES))
313 rep->expires = squid_curtime;
cb69b4c7 314}
315
d8b249ef 316/* sync this routine when you update HttpReply struct */
63259c34 317static void
d8b249ef 318httpReplyHdrCacheClean(HttpReply * rep)
2ac76861 319{
d8b249ef 320 stringClean(&rep->content_type);
321 if (rep->cache_control)
322 httpHdrCcDestroy(rep->cache_control);
323 if (rep->content_range)
324 httpHdrContRangeDestroy(rep->content_range);
63259c34 325}
cb69b4c7 326
327/*
328 * parses a 0-terminating buffer into HttpReply.
329 * Returns:
330 * +1 -- success
331 * 0 -- need more data (partial parse)
332 * -1 -- parse error
333 */
334static int
2ac76861 335httpReplyParseStep(HttpReply * rep, const char *buf, int atEnd)
cb69b4c7 336{
337 const char *parse_start = buf;
338 const char *blk_start, *blk_end;
339 const char **parse_end_ptr = &blk_end;
340 assert(rep);
341 assert(parse_start);
342 assert(rep->pstate < psParsed);
343
344 *parse_end_ptr = parse_start;
345 if (rep->pstate == psReadyToParseStartLine) {
346 if (!httpReplyIsolateStart(&parse_start, &blk_start, &blk_end))
347 return 0;
348 if (!httpStatusLineParse(&rep->sline, blk_start, blk_end))
349 return httpReplyParseError(rep);
350
351 *parse_end_ptr = parse_start;
352 rep->hdr_sz = *parse_end_ptr - buf;
2ac76861 353 rep->pstate++;
cb69b4c7 354 }
cb69b4c7 355 if (rep->pstate == psReadyToParseHeaders) {
99edd1c3 356 if (!httpMsgIsolateHeaders(&parse_start, &blk_start, &blk_end)) {
cb69b4c7 357 if (atEnd)
358 blk_start = parse_start, blk_end = blk_start + strlen(blk_start);
359 else
360 return 0;
5999b776 361 }
d8b249ef 362 if (!httpHeaderParse(&rep->header, blk_start, blk_end))
cb69b4c7 363 return httpReplyParseError(rep);
364
d8b249ef 365 httpReplyHdrCacheInit(rep);
366
cb69b4c7 367 *parse_end_ptr = parse_start;
368 rep->hdr_sz = *parse_end_ptr - buf;
369 rep->pstate++;
370 }
cb69b4c7 371 return 1;
372}
373
cb69b4c7 374/* handy: resets and returns -1 */
375static int
2ac76861 376httpReplyParseError(HttpReply * rep)
cb69b4c7 377{
378 assert(rep);
379 /* reset */
380 httpReplyReset(rep);
381 /* indicate an error */
382 rep->sline.status = HTTP_INVALID_HEADER;
383 return -1;
384}
385
386/* find first CRLF */
387static int
388httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end)
389{
390 int slen = strcspn(*parse_start, "\r\n");
2ac76861 391 if (!(*parse_start)[slen]) /* no CRLF found */
cb69b4c7 392 return 0;
393
394 *blk_start = *parse_start;
395 *blk_end = *blk_start + slen;
52639d06 396 while (**blk_end == '\r') /* CR */
cb69b4c7 397 (*blk_end)++;
2ac76861 398 if (**blk_end == '\n') /* LF */
cb69b4c7 399 (*blk_end)++;
400
401 *parse_start = *blk_end;
402 return 1;
403}