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