]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpReply.cc
Convert http_ver from a float to http_version_t which is a struct containing
[thirdparty/squid.git] / src / HttpReply.cc
1
2 /*
3 * $Id: HttpReply.cc,v 1.43 2000/11/13 12:25:11 adrian 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 * 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.
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 static time_t httpReplyHdrExpirationTime(const HttpReply * rep);
60
61
62 /* module initialization */
63 void
64 httpReplyInitModule(void)
65 {
66 httpHeaderMaskInit(&Denied304HeadersMask, 0);
67 httpHeaderCalcMask(&Denied304HeadersMask, (const int *) Denied304HeadersArr, countof(Denied304HeadersArr));
68 }
69
70
71 HttpReply *
72 httpReplyCreate(void)
73 {
74 HttpReply *rep = memAllocate(MEM_HTTP_REPLY);
75 debug(58, 7) ("creating rep: %p\n", rep);
76 httpReplyInit(rep);
77 return rep;
78 }
79
80 static void
81 httpReplyInit(HttpReply * rep)
82 {
83 assert(rep);
84 rep->hdr_sz = 0;
85 rep->pstate = psReadyToParseStartLine;
86 httpBodyInit(&rep->body);
87 httpHeaderInit(&rep->header, hoReply);
88 httpReplyHdrCacheInit(rep);
89 httpStatusLineInit(&rep->sline);
90 }
91
92 static void
93 httpReplyClean(HttpReply * rep)
94 {
95 assert(rep);
96 httpBodyClean(&rep->body);
97 httpReplyHdrCacheClean(rep);
98 httpHeaderClean(&rep->header);
99 httpStatusLineClean(&rep->sline);
100 }
101
102 void
103 httpReplyDestroy(HttpReply * rep)
104 {
105 assert(rep);
106 debug(58, 7) ("destroying rep: %p\n", rep);
107 httpReplyClean(rep);
108 httpReplyDoDestroy(rep);
109 }
110
111 void
112 httpReplyReset(HttpReply * rep)
113 {
114 httpReplyClean(rep);
115 httpReplyInit(rep);
116 }
117
118 /* absorb: copy the contents of a new reply to the old one, destroy new one */
119 void
120 httpReplyAbsorb(HttpReply * rep, HttpReply * new_rep)
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
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 */
137 int
138 httpReplyParse(HttpReply * rep, const char *buf, ssize_t end)
139 {
140 /*
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.
145 */
146 char *headers = memAllocate(MEM_4K_BUF);
147 int success;
148 size_t s = XMIN(end + 1, 4096);
149 /* reset current state, because we are not used in incremental fashion */
150 httpReplyReset(rep);
151 /* put a string terminator. s is how many bytes to touch in
152 * 'buf' including the terminating NULL. */
153 xstrncpy(headers, buf, s);
154 success = httpReplyParseStep(rep, headers, 0);
155 memFree(headers, MEM_4K_BUF);
156 return success == 1;
157 }
158
159 void
160 httpReplyPackInto(const HttpReply * rep, Packer * p)
161 {
162 assert(rep);
163 httpStatusLinePackInto(&rep->sline, p);
164 httpHeaderPackInto(&rep->header, p);
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 */
170 MemBuf
171 httpReplyPack(const HttpReply * rep)
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 */
185 void
186 httpReplySwapOut(const HttpReply * rep, StoreEntry * e)
187 {
188 Packer p;
189 assert(rep && e);
190
191 packerToStoreInit(&p, e);
192 httpReplyPackInto(rep, &p);
193 packerClean(&p);
194 }
195
196 MemBuf
197 httpPackedReply(http_version_t ver, http_status status, const char *ctype,
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
208 MemBuf
209 httpPacked304Reply(const HttpReply * rep)
210 {
211 static const http_hdr_type ImsEntries[] =
212 {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER};
213 int t;
214 MemBuf mb;
215 Packer p;
216 HttpHeaderEntry *e;
217 assert(rep);
218
219 memBufDefInit(&mb);
220 packerToMemInit(&p, &mb);
221 memBufPrintf(&mb, "%s", "HTTP/1.0 304 Not Modified\r\n");
222 for (t = 0; ImsEntries[t] != HDR_OTHER; ++t)
223 if ((e = httpHeaderFindEntry(&rep->header, ImsEntries[t])))
224 httpHeaderEntryPackInto(e, &p);
225 memBufAppend(&mb, "\r\n", 2);
226 packerClean(&p);
227 return mb;
228 }
229
230 void
231 httpReplySetHeaders(HttpReply * reply, http_version_t ver, http_status status, const char *reason,
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);
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;
246 if (clen >= 0)
247 httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, clen);
248 if (expires >= 0)
249 httpHeaderPutTime(hdr, HDR_EXPIRES, expires);
250 if (lmt > 0) /* this used to be lmt != 0 @?@ */
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;
256 }
257
258 void
259 httpRedirectReply(HttpReply * reply, http_status status, const char *loc)
260 {
261 HttpHeader *hdr;
262 http_version_t ver;
263 assert(reply);
264 httpBuildVersion(&ver,1,0);
265 httpStatusLineSet(&reply->sline, ver, status, httpStatusString(status));
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
275 void
276 httpReplyUpdateOnNotModified(HttpReply * rep, HttpReply * freshRep)
277 {
278 assert(rep && freshRep);
279 /* clean cache */
280 httpReplyHdrCacheClean(rep);
281 /* update raw headers */
282 httpHeaderUpdate(&rep->header, &freshRep->header,
283 (const HttpHeaderMask *) &Denied304HeadersMask);
284 /* init cache */
285 httpReplyHdrCacheInit(rep);
286 }
287
288
289 /* internal routines */
290
291 /* internal function used by Destroy and Absorb */
292 static void
293 httpReplyDoDestroy(HttpReply * rep)
294 {
295 memFree(rep, MEM_HTTP_REPLY);
296 }
297
298 static time_t
299 httpReplyHdrExpirationTime(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
331 /* sync this routine when you update HttpReply struct */
332 static void
333 httpReplyHdrCacheInit(HttpReply * rep)
334 {
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);
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);
347 rep->keep_alive = httpMsgIsPersistent(rep->sline.version, &rep->header);
348 /* be sure to set expires after date and cache-control */
349 rep->expires = httpReplyHdrExpirationTime(rep);
350 }
351
352 /* sync this routine when you update HttpReply struct */
353 static void
354 httpReplyHdrCacheClean(HttpReply * rep)
355 {
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);
361 }
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 */
370 static int
371 httpReplyParseStep(HttpReply * rep, const char *buf, int atEnd)
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;
389 rep->pstate++;
390 }
391 if (rep->pstate == psReadyToParseHeaders) {
392 if (!httpMsgIsolateHeaders(&parse_start, &blk_start, &blk_end)) {
393 if (atEnd)
394 blk_start = parse_start, blk_end = blk_start + strlen(blk_start);
395 else
396 return 0;
397 }
398 if (!httpHeaderParse(&rep->header, blk_start, blk_end))
399 return httpReplyParseError(rep);
400
401 httpReplyHdrCacheInit(rep);
402
403 *parse_end_ptr = parse_start;
404 rep->hdr_sz = *parse_end_ptr - buf;
405 rep->pstate++;
406 }
407 return 1;
408 }
409
410 /* handy: resets and returns -1 */
411 static int
412 httpReplyParseError(HttpReply * rep)
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 */
423 static int
424 httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end)
425 {
426 int slen = strcspn(*parse_start, "\r\n");
427 if (!(*parse_start)[slen]) /* no CRLF found */
428 return 0;
429
430 *blk_start = *parse_start;
431 *blk_end = *blk_start + slen;
432 while (**blk_end == '\r') /* CR */
433 (*blk_end)++;
434 if (**blk_end == '\n') /* LF */
435 (*blk_end)++;
436
437 *parse_start = *blk_end;
438 return 1;
439 }
440
441 /*
442 * Returns the body size of a HTTP response
443 */
444 int
445 httpReplyBodySize(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 }