]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpReply.cc
Fix diskd debugging to print the correct unlink function name, either
[thirdparty/squid.git] / src / HttpReply.cc
CommitLineData
2ac76861 1
cb69b4c7 2/*
eede25e7 3 * $Id: HttpReply.cc,v 1.87 2006/05/08 20:59:28 wessels Exp $
cb69b4c7 4 *
123abbe1 5 * DEBUG: section 58 HTTP Reply (Response)
cb69b4c7 6 * AUTHOR: Alex Rousskov
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
cb69b4c7 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see 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"
528b2c61 37#include "Store.h"
8596962e 38#include "HttpReply.h"
528b2c61 39#include "HttpHdrContRange.h"
25b6a907 40#include "HttpHdrSc.h"
4fb35c3c 41#include "ACLChecklist.h"
0eb49b6d 42#include "MemBuf.h"
cb69b4c7 43
44/* local constants */
45
2246b732 46/* these entity-headers must be ignored if a bogus server sends them in 304 */
47static HttpHeaderMask Denied304HeadersMask;
48static http_hdr_type Denied304HeadersArr[] =
62e76326 49 {
50 HDR_ALLOW, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, HDR_CONTENT_LENGTH,
51 HDR_CONTENT_LOCATION, HDR_CONTENT_RANGE, HDR_LAST_MODIFIED, HDR_LINK,
52 HDR_OTHER
53 };
2246b732 54
2246b732 55/* module initialization */
56void
9bc73deb 57httpReplyInitModule(void)
2246b732 58{
8596962e 59 assert(HTTP_STATUS_NONE == 0); // HttpReply::parse() interface assumes that
97474590 60 httpHeaderMaskInit(&Denied304HeadersMask, 0);
8abf232c 61 httpHeaderCalcMask(&Denied304HeadersMask, Denied304HeadersArr, countof(Denied304HeadersArr));
2246b732 62}
63
8596962e 64HttpReply::HttpReply() : HttpMsg(hoReply), date (0), last_modified (0), expires (0), surrogate_control (NULL), content_range (NULL), keep_alive (0), protoPrefix("HTTP/")
cb69b4c7 65{
06a5ae20 66 init();
cb69b4c7 67}
68
06a5ae20 69HttpReply::~HttpReply()
cb69b4c7 70{
06a5ae20 71 if (do_clean)
72 clean();
cb69b4c7 73}
74
75void
06a5ae20 76HttpReply::init()
cb69b4c7 77{
06a5ae20 78 httpBodyInit(&body);
79 hdrCacheInit();
80 httpStatusLineInit(&sline);
81 do_clean = true;
cb69b4c7 82}
83
06a5ae20 84void HttpReply::reset()
cb69b4c7 85{
06a5ae20 86
8596962e 87 // reset should not reset the protocol; could have made protoPrefix a
88 // virtual function instead, but it is not clear whether virtual methods
89 // are allowed with MEMPROXY_CLASS() and whether some cbdata void*
90 // conversions are not going to kill virtual tables
06a5ae20 91 const String pfx = protoPrefix;
92 clean();
93 init();
94 protoPrefix = pfx;
95}
96
97void
98HttpReply::clean()
99{
100 httpBodyClean(&body);
101 hdrCacheClean();
519e0948 102 header.clean();
06a5ae20 103 httpStatusLineClean(&sline);
cb69b4c7 104}
105
4a56ee8d 106#if OLD
63259c34 107/* absorb: copy the contents of a new reply to the old one, destroy new one */
108void
06a5ae20 109HttpReply::absorb(HttpReply * new_rep)
63259c34 110{
06a5ae20 111 assert(new_rep);
112 clean();
113 *this = *new_rep;
528b2c61 114 new_rep->header.entries.clean();
63259c34 115 /* cannot use Clean() on new reply now! */
06a5ae20 116 new_rep->do_clean = false;
07947ad8 117 new_rep->cache_control = NULL; // helps with debugging
06a5ae20 118 delete new_rep;
63259c34 119}
120
4a56ee8d 121#endif
122
cb69b4c7 123void
06a5ae20 124HttpReply::packHeadersInto(Packer * p) const
cb69b4c7 125{
06a5ae20 126 httpStatusLinePackInto(&sline, p);
a9925b40 127 header.packInto(p);
cb69b4c7 128 packerAppend(p, "\r\n", 2);
528b2c61 129}
130
131void
06a5ae20 132HttpReply::packInto(Packer * p)
528b2c61 133{
06a5ae20 134 packHeadersInto(p);
135 httpBodyPackInto(&body, p);
cb69b4c7 136}
137
06a5ae20 138/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */
032785bf 139MemBuf *
06a5ae20 140HttpReply::pack()
cb69b4c7 141{
032785bf 142 MemBuf *mb = new MemBuf;
cb69b4c7 143 Packer p;
cb69b4c7 144
2fe7eff9 145 mb->init();
032785bf 146 packerToMemInit(&p, mb);
06a5ae20 147 packInto(&p);
cb69b4c7 148 packerClean(&p);
149 return mb;
150}
151
032785bf 152MemBuf *
450e0c10 153httpPackedReply(HttpVersion ver, http_status status, const char *ctype,
62e76326 154 int clen, time_t lmt, time_t expires)
cb69b4c7 155{
06a5ae20 156 HttpReply *rep = new HttpReply;
157 rep->setHeaders(ver, status, ctype, NULL, clen, lmt, expires);
158 MemBuf *mb = rep->pack();
159 delete rep;
cb69b4c7 160 return mb;
161}
162
528b2c61 163HttpReply *
06a5ae20 164HttpReply::make304 () const
cb69b4c7 165{
62e76326 166 static const http_hdr_type ImsEntries[] = {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER};
167
06a5ae20 168 HttpReply *rv = new HttpReply;
728da2ee 169 int t;
de336bbe 170 HttpHeaderEntry *e;
cb69b4c7 171
528b2c61 172 /* rv->content_length; */
06a5ae20 173 rv->date = date;
174 rv->last_modified = last_modified;
175 rv->expires = expires;
176 rv->content_type = content_type;
528b2c61 177 /* rv->cache_control */
178 /* rv->content_range */
179 /* rv->keep_alive */
450e0c10 180 HttpVersion ver(1,0);
528b2c61 181 httpStatusLineSet(&rv->sline, ver,
62e76326 182 HTTP_NOT_MODIFIED, "");
183
de336bbe 184 for (t = 0; ImsEntries[t] != HDR_OTHER; ++t)
a9925b40 185 if ((e = header.findEntry(ImsEntries[t])))
eede25e7 186 rv->header.addEntry(e->clone());
62e76326 187
528b2c61 188 /* rv->body */
189 return rv;
190}
191
032785bf 192MemBuf *
06a5ae20 193HttpReply::packed304Reply()
528b2c61 194{
195 /* Not as efficient as skipping the header duplication,
196 * but easier to maintain
197 */
06a5ae20 198 HttpReply *temp = make304 ();
199 MemBuf *rv = temp->pack();
200 delete temp;
528b2c61 201 return rv;
cb69b4c7 202}
203
204void
06a5ae20 205HttpReply::setHeaders(HttpVersion ver, http_status status, const char *reason,
206 const char *ctype, int clen, time_t lmt, time_t expires)
cb69b4c7 207{
208 HttpHeader *hdr;
06a5ae20 209 httpStatusLineSet(&sline, ver, status, reason);
210 hdr = &header;
a9925b40 211 hdr->putStr(HDR_SERVER, visible_appname_string);
212 hdr->putStr(HDR_MIME_VERSION, "1.0");
213 hdr->putTime(HDR_DATE, squid_curtime);
62e76326 214
d8b249ef 215 if (ctype) {
a9925b40 216 hdr->putStr(HDR_CONTENT_TYPE, ctype);
06a5ae20 217 content_type = ctype;
d8b249ef 218 } else
06a5ae20 219 content_type = String();
62e76326 220
de336bbe 221 if (clen >= 0)
a9925b40 222 hdr->putInt(HDR_CONTENT_LENGTH, clen);
62e76326 223
cb69b4c7 224 if (expires >= 0)
a9925b40 225 hdr->putTime(HDR_EXPIRES, expires);
62e76326 226
2ac76861 227 if (lmt > 0) /* this used to be lmt != 0 @?@ */
a9925b40 228 hdr->putTime(HDR_LAST_MODIFIED, lmt);
62e76326 229
06a5ae20 230 date = squid_curtime;
62e76326 231
06a5ae20 232 content_length = clen;
62e76326 233
06a5ae20 234 expires = expires;
62e76326 235
06a5ae20 236 last_modified = lmt;
cb69b4c7 237}
238
6d38ef86 239void
06a5ae20 240HttpReply::redirect(http_status status, const char *loc)
6d38ef86 241{
242 HttpHeader *hdr;
450e0c10 243 HttpVersion ver(1,0);
06a5ae20 244 httpStatusLineSet(&sline, ver, status, httpStatusString(status));
245 hdr = &header;
a9925b40 246 hdr->putStr(HDR_SERVER, full_appname_string);
247 hdr->putTime(HDR_DATE, squid_curtime);
248 hdr->putInt(HDR_CONTENT_LENGTH, 0);
249 hdr->putStr(HDR_LOCATION, loc);
06a5ae20 250 date = squid_curtime;
251 content_length = 0;
6d38ef86 252}
253
528b2c61 254/* compare the validators of two replies.
255 * 1 = they match
256 * 0 = they do not match
257 */
258int
06a5ae20 259HttpReply::validatorsMatch(HttpReply const * otherRep) const
62e76326 260{
528b2c61 261 String one,two;
06a5ae20 262 assert (otherRep);
528b2c61 263 /* Numbers first - easiest to check */
264 /* Content-Length */
265 /* TODO: remove -1 bypass */
62e76326 266
06a5ae20 267 if (content_length != otherRep->content_length
268 && content_length > -1 &&
62e76326 269 otherRep->content_length > -1)
270 return 0;
271
528b2c61 272 /* ETag */
a9925b40 273 one = header.getStrOrList(HDR_ETAG);
62e76326 274
a9925b40 275 two = otherRep->header.getStrOrList(HDR_ETAG);
62e76326 276
528b2c61 277 if (!one.buf() || !two.buf() || strcasecmp (one.buf(), two.buf())) {
62e76326 278 one.clean();
279 two.clean();
280 return 0;
528b2c61 281 }
62e76326 282
06a5ae20 283 if (last_modified != otherRep->last_modified)
62e76326 284 return 0;
285
528b2c61 286 /* MD5 */
a9925b40 287 one = header.getStrOrList(HDR_CONTENT_MD5);
62e76326 288
a9925b40 289 two = otherRep->header.getStrOrList(HDR_CONTENT_MD5);
62e76326 290
6bd76974 291 if (!one.buf() || !two.buf() || strcasecmp (one.buf(), two.buf())) {
62e76326 292 one.clean();
293 two.clean();
294 return 0;
528b2c61 295 }
62e76326 296
528b2c61 297 return 1;
298}
299
cb69b4c7 300void
06a5ae20 301HttpReply::updateOnNotModified(HttpReply const * freshRep)
cb69b4c7 302{
07947ad8 303 assert(freshRep);
528b2c61 304 /* Can not update modified headers that don't match! */
06a5ae20 305 assert (validatorsMatch(freshRep));
d8b249ef 306 /* clean cache */
06a5ae20 307 hdrCacheClean();
d8b249ef 308 /* update raw headers */
a9925b40 309 header.update(&freshRep->header,
310 (const HttpHeaderMask *) &Denied304HeadersMask);
d8b249ef 311 /* init cache */
07947ad8 312 hdrCacheInit();
cb69b4c7 313}
314
d8b249ef 315/* internal routines */
cb69b4c7 316
06a5ae20 317time_t
318HttpReply::hdrExpirationTime()
d20b1cd0 319{
320 /* The s-maxage and max-age directive takes priority over Expires */
62e76326 321
06a5ae20 322 if (cache_control) {
323 if (date >= 0) {
324 if (cache_control->s_maxage >= 0)
325 return date + cache_control->s_maxage;
62e76326 326
06a5ae20 327 if (cache_control->max_age >= 0)
328 return date + cache_control->max_age;
62e76326 329 } else {
330 /*
331 * Conservatively handle the case when we have a max-age
332 * header, but no Date for reference?
333 */
334
06a5ae20 335 if (cache_control->s_maxage >= 0)
62e76326 336 return squid_curtime;
337
06a5ae20 338 if (cache_control->max_age >= 0)
62e76326 339 return squid_curtime;
340 }
d20b1cd0 341 }
62e76326 342
f66a9ef4 343 if (Config.onoff.vary_ignore_expire &&
a9925b40 344 header.has(HDR_VARY)) {
345 const time_t d = header.getTime(HDR_DATE);
346 const time_t e = header.getTime(HDR_EXPIRES);
62e76326 347
348 if (d == e)
349 return -1;
f66a9ef4 350 }
62e76326 351
a9925b40 352 if (header.has(HDR_EXPIRES)) {
353 const time_t e = header.getTime(HDR_EXPIRES);
62e76326 354 /*
355 * HTTP/1.0 says that robust implementations should consider
356 * bad or malformed Expires header as equivalent to "expires
357 * immediately."
358 */
359 return e < 0 ? squid_curtime : e;
d20b1cd0 360 }
62e76326 361
d20b1cd0 362 return -1;
363}
364
d8b249ef 365/* sync this routine when you update HttpReply struct */
8596962e 366void
07947ad8 367HttpReply::hdrCacheInit()
cb69b4c7 368{
07947ad8 369 HttpMsg::hdrCacheInit();
370
a9925b40 371 content_length = header.getInt(HDR_CONTENT_LENGTH);
372 date = header.getTime(HDR_DATE);
373 last_modified = header.getTime(HDR_LAST_MODIFIED);
374 surrogate_control = header.getSc();
375 content_range = header.getContRange();
07947ad8 376 keep_alive = httpMsgIsPersistent(sline.version, &header);
a9925b40 377 const char *str = header.getStr(HDR_CONTENT_TYPE);
62e76326 378
d8b249ef 379 if (str)
07947ad8 380 content_type.limitInit(str, strcspn(str, ";\t "));
d8b249ef 381 else
07947ad8 382 content_type = String();
62e76326 383
d20b1cd0 384 /* be sure to set expires after date and cache-control */
06a5ae20 385 expires = hdrExpirationTime();
cb69b4c7 386}
387
d8b249ef 388/* sync this routine when you update HttpReply struct */
06a5ae20 389void
390HttpReply::hdrCacheClean()
2ac76861 391{
06a5ae20 392 content_type.clean();
62e76326 393
06a5ae20 394 if (cache_control) {
395 httpHdrCcDestroy(cache_control);
396 cache_control = NULL;
07947ad8 397 }
62e76326 398
06a5ae20 399 if (surrogate_control) {
400 httpHdrScDestroy(surrogate_control);
401 surrogate_control = NULL;
07947ad8 402 }
43ae1d95 403
06a5ae20 404 if (content_range) {
405 httpHdrContRangeDestroy(content_range);
406 content_range = NULL;
07947ad8 407 }
63259c34 408}
cb69b4c7 409
35282fbf 410/*
411 * Returns the body size of a HTTP response
412 */
413int
06a5ae20 414HttpReply::bodySize(method_t method) const
35282fbf 415{
06a5ae20 416 if (sline.version.major < 1)
1bda350e 417 return -1;
418 else if (METHOD_HEAD == method)
62e76326 419 return 0;
06a5ae20 420 else if (sline.status == HTTP_OK)
62e76326 421 (void) 0; /* common case, continue */
06a5ae20 422 else if (sline.status == HTTP_NO_CONTENT)
62e76326 423 return 0;
06a5ae20 424 else if (sline.status == HTTP_NOT_MODIFIED)
62e76326 425 return 0;
06a5ae20 426 else if (sline.status < HTTP_OK)
62e76326 427 return 0;
428
06a5ae20 429 return content_length;
35282fbf 430}
8596962e 431
432bool HttpReply::sanityCheckStartLine(MemBuf *buf, http_status *error)
433{
434 if (buf->contentSize() >= protoPrefix.size() && protoPrefix.cmp(buf->content(), protoPrefix.size()) != 0) {
435 debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol prefix (" << protoPrefix.buf() << ") in '" << buf->content() << "'");
436 *error = HTTP_INVALID_HEADER;
437 return false;
438 }
439
440 return true;
441}
442
443void HttpReply::packFirstLineInto(Packer *p, bool unused) const
444{
445 httpStatusLinePackInto(&sline, p);
446}
429f7150 447
448bool HttpReply::parseFirstLine(const char *blk_start, const char *blk_end)
449{
450 return httpStatusLineParse(&sline, protoPrefix, blk_start, blk_end);
451}
5c09dcb8 452
fb525683 453/* handy: resets and returns -1 */
454int
455HttpReply::httpMsgParseError()
456{
457 int result(HttpMsg::httpMsgParseError());
458 /* indicate an error in the status line */
459 sline.status = HTTP_INVALID_HEADER;
460 return result;
461}
462
5c09dcb8 463/*
464 * Indicate whether or not we would usually expect an entity-body
465 * along with this response
466 */
467bool
468HttpReply::expectingBody(method_t req_method, ssize_t& theSize) const
469{
470 bool expectBody = true;
471
472 if (req_method == METHOD_HEAD)
473 expectBody = false;
474 else if (sline.status == HTTP_NO_CONTENT)
475 expectBody = false;
476 else if (sline.status == HTTP_NOT_MODIFIED)
477 expectBody = false;
478 else if (sline.status < HTTP_OK)
479 expectBody = false;
480 else
481 expectBody = true;
482
483 if (expectBody) {
a9925b40 484 if (header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ','))
5c09dcb8 485 theSize = -1;
486 else if (content_length >= 0)
487 theSize = content_length;
488 else
489 theSize = -1;
490 }
491
492 return expectBody;
493}