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