3 * $Id: HttpReply.cc,v 1.93 2007/05/18 06:41:22 amosjeffries Exp $
5 * DEBUG: section 58 HTTP Reply (Response)
6 * AUTHOR: Alex Rousskov
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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.
37 #include "SquidTime.h"
39 #include "HttpReply.h"
40 #include "HttpHdrContRange.h"
41 #include "HttpHdrSc.h"
42 #include "ACLChecklist.h"
47 /* If we receive a 304 from the origin during a cache revalidation, we must
48 * update the headers of the existing entry. Specifically, we need to update all
49 * end-to-end headers and not any hop-by-hop headers (rfc2616 13.5.3).
51 * This is not the whole story though: since it is possible for a faulty/malicious
52 * origin server to set headers it should not in a 304, we must explicitly ignore
53 * these too. Specifically all entity-headers except those permitted in a 304
54 * (rfc2616 10.3.5) must be ignored.
56 * The list of headers we don't update is made up of:
57 * all hop-by-hop headers
58 * all entity-headers except Expires and Content-Location
60 static HttpHeaderMask Denied304HeadersMask
;
61 static http_hdr_type Denied304HeadersArr
[] =
64 HDR_CONNECTION
, HDR_KEEP_ALIVE
, HDR_PROXY_AUTHENTICATE
, HDR_PROXY_AUTHORIZATION
,
65 HDR_TE
, HDR_TRAILERS
, HDR_TRANSFER_ENCODING
, HDR_UPGRADE
,
67 HDR_ALLOW
, HDR_CONTENT_ENCODING
, HDR_CONTENT_LANGUAGE
, HDR_CONTENT_LENGTH
,
68 HDR_CONTENT_MD5
, HDR_CONTENT_RANGE
, HDR_CONTENT_TYPE
, HDR_LAST_MODIFIED
71 /* module initialization */
73 httpReplyInitModule(void)
75 assert(HTTP_STATUS_NONE
== 0); // HttpReply::parse() interface assumes that
76 httpHeaderMaskInit(&Denied304HeadersMask
, 0);
77 httpHeaderCalcMask(&Denied304HeadersMask
, Denied304HeadersArr
, countof(Denied304HeadersArr
));
80 HttpReply::HttpReply() : HttpMsg(hoReply
), date (0), last_modified (0), expires (0), surrogate_control (NULL
), content_range (NULL
), keep_alive (0), protoPrefix("HTTP/")
85 HttpReply::~HttpReply()
96 httpStatusLineInit(&sline
);
97 pstate
= psReadyToParseStartLine
;
101 void HttpReply::reset()
104 // reset should not reset the protocol; could have made protoPrefix a
105 // virtual function instead, but it is not clear whether virtual methods
106 // are allowed with MEMPROXY_CLASS() and whether some cbdata void*
107 // conversions are not going to kill virtual tables
108 const string pfx
= protoPrefix
;
117 // we used to assert that the pipe is NULL, but now the message only
118 // points to a pipe that is owned and initiated by another object.
121 httpBodyClean(&body
);
124 httpStatusLineClean(&sline
);
128 HttpReply::packHeadersInto(Packer
* p
) const
130 httpStatusLinePackInto(&sline
, p
);
132 packerAppend(p
, "\r\n", 2);
136 HttpReply::packInto(Packer
* p
)
139 httpBodyPackInto(&body
, p
);
142 /* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */
146 MemBuf
*mb
= new MemBuf
;
150 packerToMemInit(&p
, mb
);
157 httpPackedReply(HttpVersion ver
, http_status status
, const char *ctype
,
158 int clen
, time_t lmt
, time_t expires
)
160 HttpReply
*rep
= new HttpReply
;
161 rep
->setHeaders(ver
, status
, ctype
, NULL
, clen
, lmt
, expires
);
162 MemBuf
*mb
= rep
->pack();
168 HttpReply::make304 () const
170 static const http_hdr_type ImsEntries
[] = {HDR_DATE
, HDR_CONTENT_TYPE
, HDR_EXPIRES
, HDR_LAST_MODIFIED
, /* eof */ HDR_OTHER
};
172 HttpReply
*rv
= new HttpReply
;
176 /* rv->content_length; */
178 rv
->last_modified
= last_modified
;
179 rv
->expires
= expires
;
180 rv
->content_type
= content_type
;
181 /* rv->cache_control */
182 /* rv->content_range */
184 HttpVersion
ver(1,0);
185 httpStatusLineSet(&rv
->sline
, ver
,
186 HTTP_NOT_MODIFIED
, "");
188 for (t
= 0; ImsEntries
[t
] != HDR_OTHER
; ++t
)
189 if ((e
= header
.findEntry(ImsEntries
[t
])))
190 rv
->header
.addEntry(e
->clone());
197 HttpReply::packed304Reply()
199 /* Not as efficient as skipping the header duplication,
200 * but easier to maintain
202 HttpReply
*temp
= make304 ();
203 MemBuf
*rv
= temp
->pack();
209 HttpReply::setHeaders(HttpVersion ver
, http_status status
, const char *reason
,
210 const char *ctype
, int clen
, time_t lmt
, time_t expires
)
213 httpStatusLineSet(&sline
, ver
, status
, reason
);
215 hdr
->putStr(HDR_SERVER
, visible_appname_string
);
216 hdr
->putStr(HDR_MIME_VERSION
, "1.0");
217 hdr
->putTime(HDR_DATE
, squid_curtime
);
220 hdr
->putStr(HDR_CONTENT_TYPE
, ctype
);
221 content_type
= ctype
;
223 content_type
.clear();
226 hdr
->putInt(HDR_CONTENT_LENGTH
, clen
);
229 hdr
->putTime(HDR_EXPIRES
, expires
);
231 if (lmt
> 0) /* this used to be lmt != 0 @?@ */
232 hdr
->putTime(HDR_LAST_MODIFIED
, lmt
);
234 date
= squid_curtime
;
236 content_length
= clen
;
244 HttpReply::redirect(http_status status
, const char *loc
)
247 HttpVersion
ver(1,0);
248 httpStatusLineSet(&sline
, ver
, status
, httpStatusString(status
));
250 hdr
->putStr(HDR_SERVER
, full_appname_string
);
251 hdr
->putTime(HDR_DATE
, squid_curtime
);
252 hdr
->putInt(HDR_CONTENT_LENGTH
, 0);
253 hdr
->putStr(HDR_LOCATION
, loc
);
254 date
= squid_curtime
;
258 /* compare the validators of two replies.
260 * 0 = they do not match
263 HttpReply::validatorsMatch(HttpReply
const * otherRep
) const
267 /* Numbers first - easiest to check */
269 /* TODO: remove -1 bypass */
271 if (content_length
!= otherRep
->content_length
272 && content_length
> -1 &&
273 otherRep
->content_length
> -1)
277 one
= header
.getStrOrList(HDR_ETAG
);
279 two
= otherRep
->header
.getStrOrList(HDR_ETAG
);
281 if (one
.empty() || two
.empty() || strcasecmp (one
, two
)) {
285 if (last_modified
!= otherRep
->last_modified
)
289 one
= header
.getStrOrList(HDR_CONTENT_MD5
);
291 two
= otherRep
->header
.getStrOrList(HDR_CONTENT_MD5
);
293 if (one
.empty() || two
.empty() || strcasecmp (one
, two
)) {
303 HttpReply::updateOnNotModified(HttpReply
const * freshRep
)
309 /* update raw headers */
310 header
.update(&freshRep
->header
,
311 (const HttpHeaderMask
*) &Denied304HeadersMask
);
317 /* internal routines */
320 HttpReply::hdrExpirationTime()
322 /* The s-maxage and max-age directive takes priority over Expires */
326 if (cache_control
->s_maxage
>= 0)
327 return date
+ cache_control
->s_maxage
;
329 if (cache_control
->max_age
>= 0)
330 return date
+ cache_control
->max_age
;
333 * Conservatively handle the case when we have a max-age
334 * header, but no Date for reference?
337 if (cache_control
->s_maxage
>= 0)
338 return squid_curtime
;
340 if (cache_control
->max_age
>= 0)
341 return squid_curtime
;
345 if (Config
.onoff
.vary_ignore_expire
&&
346 header
.has(HDR_VARY
)) {
347 const time_t d
= header
.getTime(HDR_DATE
);
348 const time_t e
= header
.getTime(HDR_EXPIRES
);
354 if (header
.has(HDR_EXPIRES
)) {
355 const time_t e
= header
.getTime(HDR_EXPIRES
);
357 * HTTP/1.0 says that robust implementations should consider
358 * bad or malformed Expires header as equivalent to "expires
361 return e
< 0 ? squid_curtime
: e
;
367 /* sync this routine when you update HttpReply struct */
369 HttpReply::hdrCacheInit()
371 HttpMsg::hdrCacheInit();
373 content_length
= header
.getInt(HDR_CONTENT_LENGTH
);
374 date
= header
.getTime(HDR_DATE
);
375 last_modified
= header
.getTime(HDR_LAST_MODIFIED
);
376 surrogate_control
= header
.getSc();
377 content_range
= header
.getContRange();
378 keep_alive
= httpMsgIsPersistent(sline
.version
, &header
);
379 const char *str
= header
.getStr(HDR_CONTENT_TYPE
);
382 content_type
.limitInit(str
, strcspn(str
, ";\t "));
386 /* be sure to set expires after date and cache-control */
387 expires
= hdrExpirationTime();
390 /* sync this routine when you update HttpReply struct */
392 HttpReply::hdrCacheClean()
394 content_type
.clear();
397 httpHdrCcDestroy(cache_control
);
398 cache_control
= NULL
;
401 if (surrogate_control
) {
402 httpHdrScDestroy(surrogate_control
);
403 surrogate_control
= NULL
;
407 httpHdrContRangeDestroy(content_range
);
408 content_range
= NULL
;
413 * Returns the body size of a HTTP response
416 HttpReply::bodySize(method_t method
) const
418 if (sline
.version
.major
< 1)
420 else if (METHOD_HEAD
== method
)
422 else if (sline
.status
== HTTP_OK
)
423 (void) 0; /* common case, continue */
424 else if (sline
.status
== HTTP_NO_CONTENT
)
426 else if (sline
.status
== HTTP_NOT_MODIFIED
)
428 else if (sline
.status
< HTTP_OK
)
431 return content_length
;
434 bool HttpReply::sanityCheckStartLine(MemBuf
*buf
, http_status
*error
)
436 if (buf
->contentSize() >= protoPrefix
.size() && strncmp(protoPrefix
, buf
->content(), protoPrefix
.size()) != 0) {
437 debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol prefix (" << protoPrefix
<< ") in '" << buf
->content() << "'");
438 *error
= HTTP_INVALID_HEADER
;
445 void HttpReply::packFirstLineInto(Packer
*p
, bool unused
) const
447 httpStatusLinePackInto(&sline
, p
);
450 bool HttpReply::parseFirstLine(const char *blk_start
, const char *blk_end
)
452 return httpStatusLineParse(&sline
, protoPrefix
, blk_start
, blk_end
);
455 /* handy: resets and returns -1 */
457 HttpReply::httpMsgParseError()
459 int result(HttpMsg::httpMsgParseError());
460 /* indicate an error in the status line */
461 sline
.status
= HTTP_INVALID_HEADER
;
466 * Indicate whether or not we would usually expect an entity-body
467 * along with this response
470 HttpReply::expectingBody(method_t req_method
, ssize_t
& theSize
) const
472 bool expectBody
= true;
474 if (req_method
== METHOD_HEAD
)
476 else if (sline
.status
== HTTP_NO_CONTENT
)
478 else if (sline
.status
== HTTP_NOT_MODIFIED
)
480 else if (sline
.status
< HTTP_OK
)
486 if (header
.hasListMember(HDR_TRANSFER_ENCODING
, "chunked", ','))
488 else if (content_length
>= 0)
489 theSize
= content_length
;