]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpReply.cc
fixed debug statement
[thirdparty/squid.git] / src / HttpReply.cc
CommitLineData
2ac76861 1
cb69b4c7 2/*
de336bbe 3 * $Id: HttpReply.cc,v 1.12 1998/03/11 22:18:46 rousskov 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/
9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
31
32/* tmp hack, delete it @?@ */
33#define Const
34
35#include "squid.h"
36
37
38/* local constants */
39
40/* local routines */
2ac76861 41static void httpReplyDoDestroy(HttpReply * rep);
42static int httpReplyParseStep(HttpReply * rep, const char *parse_start, int atEnd);
43static int httpReplyParseError(HttpReply * rep);
cb69b4c7 44static int httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end);
45static int httpReplyIsolateHeaders(const char **parse_start, const char **blk_start, const char **blk_end);
46
47
48HttpReply *
49httpReplyCreate()
50{
7021844c 51 HttpReply *rep = memAllocate(MEM_HTTPREPLY);
399e85ea 52 debug(58, 7) ("creating rep: %p\n", rep);
cb69b4c7 53 httpReplyInit(rep);
54 return rep;
55}
56
57void
2ac76861 58httpReplyInit(HttpReply * rep)
cb69b4c7 59{
60 assert(rep);
61 rep->hdr_sz = 0;
62 rep->pstate = psReadyToParseStartLine;
63 httpBodyInit(&rep->body);
64 httpHeaderInit(&rep->hdr);
65 httpStatusLineInit(&rep->sline);
66}
67
68void
2ac76861 69httpReplyClean(HttpReply * rep)
cb69b4c7 70{
71 assert(rep);
72 httpBodyClean(&rep->body);
73 httpHeaderClean(&rep->hdr);
74 httpStatusLineClean(&rep->sline);
75}
76
77void
2ac76861 78httpReplyDestroy(HttpReply * rep)
cb69b4c7 79{
80 assert(rep);
399e85ea 81 debug(58, 7) ("destroying rep: %p\n", rep);
cb69b4c7 82 httpReplyClean(rep);
63259c34 83 httpReplyDoDestroy(rep);
cb69b4c7 84}
85
86void
2ac76861 87httpReplyReset(HttpReply * rep)
cb69b4c7 88{
89 httpReplyClean(rep);
90 httpReplyInit(rep);
91}
92
63259c34 93/* absorb: copy the contents of a new reply to the old one, destroy new one */
94void
2ac76861 95httpReplyAbsorb(HttpReply * rep, HttpReply * new_rep)
63259c34 96{
97 assert(rep && new_rep);
98 httpReplyClean(rep);
99 *rep = *new_rep;
100 /* cannot use Clean() on new reply now! */
101 httpReplyDoDestroy(new_rep);
102}
103
cb69b4c7 104/* parses a buffer that may not be 0-terminated */
105int
2ac76861 106httpReplyParse(HttpReply * rep, const char *buf)
cb69b4c7 107{
108 /*
109 * this extra buffer/copy will be eliminated when headers become meta-data
110 * in store. Currently we have to xstrncpy the buffer becuase store.c may
111 * feed a non 0-terminated buffer to us @?@.
112 */
7021844c 113 char *headers = memAllocate(MEM_4K_BUF);
cb69b4c7 114 int success;
115 /* reset current state, because we are not used in incremental fashion */
116 httpReplyReset(rep);
117 /* put a 0-terminator */
118 xstrncpy(headers, buf, 4096);
119 success = httpReplyParseStep(rep, headers, 0);
120 memFree(MEM_4K_BUF, headers);
121 return success == 1;
122}
123
124void
2ac76861 125httpReplyPackInto(const HttpReply * rep, Packer * p)
cb69b4c7 126{
127 assert(rep);
128 httpStatusLinePackInto(&rep->sline, p);
129 httpHeaderPackInto(&rep->hdr, p);
130 packerAppend(p, "\r\n", 2);
131 httpBodyPackInto(&rep->body, p);
132}
133
134/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */
135MemBuf
2ac76861 136httpReplyPack(const HttpReply * rep)
cb69b4c7 137{
138 MemBuf mb;
139 Packer p;
140 assert(rep);
141
142 memBufDefInit(&mb);
143 packerToMemInit(&p, &mb);
144 httpReplyPackInto(rep, &p);
145 packerClean(&p);
146 return mb;
147}
148
149/* swap: create swap-based packer, pack, destroy packer */
150void
2ac76861 151httpReplySwapOut(const HttpReply * rep, StoreEntry * e)
cb69b4c7 152{
153 Packer p;
154 assert(rep && e);
155
156 packerToStoreInit(&p, e);
157 httpReplyPackInto(rep, &p);
158 packerClean(&p);
159}
160
161MemBuf
162httpPackedReply(double ver, http_status status, const char *ctype,
163 int clen, time_t lmt, time_t expires)
164{
165 HttpReply *rep = httpReplyCreate();
166 MemBuf mb;
167 httpReplySetHeaders(rep, ver, status, ctype, NULL, clen, lmt, expires);
168 mb = httpReplyPack(rep);
169 httpReplyDestroy(rep);
170 return mb;
171}
172
173MemBuf
2ac76861 174httpPacked304Reply(const HttpReply * rep)
cb69b4c7 175{
de336bbe 176 static const http_hdr_type ImsEntries[] = { HDR_DATE, HDR_CONTENT_LENGTH, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER };
177 http_hdr_type t;
cb69b4c7 178 MemBuf mb;
de336bbe 179 Packer p;
180 HttpHeaderEntry *e;
cb69b4c7 181 assert(rep);
182
cb69b4c7 183 memBufDefInit(&mb);
de336bbe 184 packerToMemInit(&p, &mb);
cb69b4c7 185 memBufPrintf(&mb, "%s", "HTTP/1.0 304 Not Modified\r\n");
de336bbe 186 for (t = 0; ImsEntries[t] != HDR_OTHER; ++t)
187 if ((e = httpHeaderFindEntry(&rep->hdr, ImsEntries[t], NULL)))
188 httpHeaderEntryPackInto(e, &p);
cb69b4c7 189 memBufAppend(&mb, "\r\n", 2);
de336bbe 190 packerClean(&p);
cb69b4c7 191 return mb;
192}
193
194void
2ac76861 195httpReplySetHeaders(HttpReply * reply, double ver, http_status status, const char *reason,
cb69b4c7 196 const char *ctype, int clen, time_t lmt, time_t expires)
197{
198 HttpHeader *hdr;
199 assert(reply);
200 httpStatusLineSet(&reply->sline, ver, status, reason);
201 hdr = &reply->hdr;
202 httpHeaderAddExt(hdr, "Server", full_appname_string);
2ac76861 203 httpHeaderAddExt(hdr, "MIME-Version", "1.0"); /* do we need this? @?@ */
cb69b4c7 204 httpHeaderSetTime(hdr, HDR_DATE, squid_curtime);
205 if (ctype)
206 httpHeaderSetStr(hdr, HDR_CONTENT_TYPE, ctype);
de336bbe 207 if (clen >= 0)
cb69b4c7 208 httpHeaderSetInt(hdr, HDR_CONTENT_LENGTH, clen);
209 if (expires >= 0)
210 httpHeaderSetTime(hdr, HDR_EXPIRES, expires);
2ac76861 211 if (lmt > 0) /* this used to be lmt != 0 @?@ */
212 httpHeaderSetTime(hdr, HDR_LAST_MODIFIED, lmt);
cb69b4c7 213}
214
adba4a64 215/*
216 * header manipulation
217 *
218 * never go to header directly if you can use these:
219 *
220 * our interpretation of headers often changes and you may get into trouble
221 * if you, for example, assume that HDR_EXPIRES contains expire info
222 *
223 * if you think about it, in most cases, you are not looking for the information
de336bbe 224 * in the header, but rather for current state of the reply, which may or may
225 * not depend on headers.
adba4a64 226 *
227 * For example, the _real_ question is
228 * "when does this object expire?"
229 * not
230 * "what is the value of the 'Expires:' header?"
231 */
232
cb69b4c7 233void
2ac76861 234httpReplyUpdateOnNotModified(HttpReply * rep, HttpReply * freshRep)
cb69b4c7 235{
de336bbe 236#if 0 /* this is what we want: */
cb69b4c7 237 rep->cache_control = freshRep->cache_control;
238 rep->misc_headers = freshRep->misc_headers;
239 if (freshRep->date > -1)
240 rep->date = freshRep->date;
241 if (freshRep->last_modified > -1)
242 rep->last_modified = freshRep->last_modified;
243 if (freshRep->expires > -1)
244 rep->expires = freshRep->expires;
245#endif
246 time_t date;
247 time_t expires;
248 time_t lmt;
249 assert(rep && freshRep);
250 /* save precious info */
251 date = httpHeaderGetTime(&rep->hdr, HDR_DATE);
2ac76861 252 expires = httpReplyExpires(rep);
cb69b4c7 253 lmt = httpHeaderGetTime(&rep->hdr, HDR_LAST_MODIFIED);
254 /* clean old headers */
255 httpHeaderClean(&rep->hdr);
de336bbe 256 httpHeaderInit(&rep->hdr);
257 /* copy */
258 httpHeaderCopy(&rep->hdr, &freshRep->hdr);
cb69b4c7 259 /* restore missing info if needed */
260 if (!httpHeaderHas(&rep->hdr, HDR_DATE))
261 httpHeaderSetTime(&rep->hdr, HDR_DATE, date);
262 if (!httpHeaderHas(&rep->hdr, HDR_EXPIRES))
263 httpHeaderSetTime(&rep->hdr, HDR_EXPIRES, expires);
264 if (!httpHeaderHas(&rep->hdr, HDR_LAST_MODIFIED))
265 httpHeaderSetTime(&rep->hdr, HDR_LAST_MODIFIED, lmt);
266}
267
268int
2ac76861 269httpReplyContentLen(const HttpReply * rep)
270{
cb69b4c7 271 assert(rep);
7c525cc2 272 return httpHeaderGetInt(&rep->hdr, HDR_CONTENT_LENGTH);
cb69b4c7 273}
274
275/* should we return "" or NULL if no content-type? Return NULL for now @?@ */
276const char *
2ac76861 277httpReplyContentType(const HttpReply * rep)
278{
cb69b4c7 279 assert(rep);
280 return httpHeaderGetStr(&rep->hdr, HDR_CONTENT_TYPE);
281}
282
cb69b4c7 283time_t
2ac76861 284httpReplyExpires(const HttpReply * rep)
cb69b4c7 285{
7faf2bdb 286 HttpHdrCc *cc;
cb69b4c7 287 time_t exp = -1;
288 assert(rep);
289 /* The max-age directive takes priority over Expires, check it first */
7faf2bdb 290 cc = httpHeaderGetCc(&rep->hdr);
291 if (cc)
292 exp = cc->max_age;
cb69b4c7 293 if (exp < 0)
294 exp = httpHeaderGetTime(&rep->hdr, HDR_EXPIRES);
295 return exp;
296}
297
298int
7faf2bdb 299httpReplyHasCc(const HttpReply * rep, http_hdr_cc_type type)
cb69b4c7 300{
7faf2bdb 301 HttpHdrCc *cc;
cb69b4c7 302 assert(rep);
7faf2bdb 303 assert(type >= 0 && type < CC_ENUM_END);
cb69b4c7 304
7faf2bdb 305 cc = httpHeaderGetCc(&rep->hdr);
306 return cc && /* scc header is present */
307 EBIT_TEST(cc->mask, type);
cb69b4c7 308}
309
310
311/* internal routines */
312
63259c34 313/* internal function used by Destroy and Absorb */
314static void
2ac76861 315httpReplyDoDestroy(HttpReply * rep)
316{
63259c34 317 memFree(MEM_HTTPREPLY, rep);
318}
cb69b4c7 319
320/*
321 * parses a 0-terminating buffer into HttpReply.
322 * Returns:
323 * +1 -- success
324 * 0 -- need more data (partial parse)
325 * -1 -- parse error
326 */
327static int
2ac76861 328httpReplyParseStep(HttpReply * rep, const char *buf, int atEnd)
cb69b4c7 329{
330 const char *parse_start = buf;
331 const char *blk_start, *blk_end;
332 const char **parse_end_ptr = &blk_end;
333 assert(rep);
334 assert(parse_start);
335 assert(rep->pstate < psParsed);
336
337 *parse_end_ptr = parse_start;
338 if (rep->pstate == psReadyToParseStartLine) {
339 if (!httpReplyIsolateStart(&parse_start, &blk_start, &blk_end))
340 return 0;
341 if (!httpStatusLineParse(&rep->sline, blk_start, blk_end))
342 return httpReplyParseError(rep);
343
344 *parse_end_ptr = parse_start;
345 rep->hdr_sz = *parse_end_ptr - buf;
2ac76861 346 rep->pstate++;
cb69b4c7 347 }
cb69b4c7 348 if (rep->pstate == psReadyToParseHeaders) {
349 if (!httpReplyIsolateHeaders(&parse_start, &blk_start, &blk_end))
350 if (atEnd)
351 blk_start = parse_start, blk_end = blk_start + strlen(blk_start);
352 else
353 return 0;
354 if (!httpHeaderParse(&rep->hdr, blk_start, blk_end))
355 return httpReplyParseError(rep);
356
357 *parse_end_ptr = parse_start;
358 rep->hdr_sz = *parse_end_ptr - buf;
359 rep->pstate++;
360 }
cb69b4c7 361 return 1;
362}
363
364
365/* handy: resets and returns -1 */
366static int
2ac76861 367httpReplyParseError(HttpReply * rep)
cb69b4c7 368{
369 assert(rep);
370 /* reset */
371 httpReplyReset(rep);
372 /* indicate an error */
373 rep->sline.status = HTTP_INVALID_HEADER;
374 return -1;
375}
376
377/* find first CRLF */
378static int
379httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end)
380{
381 int slen = strcspn(*parse_start, "\r\n");
2ac76861 382 if (!(*parse_start)[slen]) /* no CRLF found */
cb69b4c7 383 return 0;
384
385 *blk_start = *parse_start;
386 *blk_end = *blk_start + slen;
2ac76861 387 if (**blk_end == '\r') /* CR */
cb69b4c7 388 (*blk_end)++;
2ac76861 389 if (**blk_end == '\n') /* LF */
cb69b4c7 390 (*blk_end)++;
391
392 *parse_start = *blk_end;
393 return 1;
394}
395
396/* find end of headers */
397static int
398httpReplyIsolateHeaders(const char **parse_start, const char **blk_start, const char **blk_end)
399{
400 /* adopted with mods from mime_headers_end() */
401 const char *p1 = strstr(*parse_start, "\n\r\n");
402 const char *p2 = strstr(*parse_start, "\n\n");
403 const char *end = NULL;
404
405 if (p1 && p2)
2ac76861 406 end = p1 < p2 ? p1 : p2;
cb69b4c7 407 else
2ac76861 408 end = p1 ? p1 : p2;
cb69b4c7 409
410 if (end) {
2ac76861 411 *blk_start = *parse_start;
cb69b4c7 412 *blk_end = end + 1;
413 *parse_start = end + (end == p1 ? 3 : 2);
414 }
415 return end != NULL;
416}