/*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "squid.h"
#include "acl/AclSizeLimit.h"
#include "acl/FilledChecklist.h"
+#include "base/EnumIterator.h"
#include "globals.h"
#include "HttpBody.h"
#include "HttpHdrCc.h"
#include "Store.h"
#include "StrList.h"
-/* local constants */
-
-/* If we receive a 304 from the origin during a cache revalidation, we must
- * update the headers of the existing entry. Specifically, we need to update all
- * end-to-end headers and not any hop-by-hop headers (rfc2616 13.5.3).
- *
- * This is not the whole story though: since it is possible for a faulty/malicious
- * origin server to set headers it should not in a 304, we must explicitly ignore
- * these too. Specifically all entity-headers except those permitted in a 304
- * (rfc2616 10.3.5) must be ignored.
- *
- * The list of headers we don't update is made up of:
- * all hop-by-hop headers
- * all entity-headers except Expires and Content-Location
- */
-static HttpHeaderMask Denied304HeadersMask;
-static http_hdr_type Denied304HeadersArr[] = {
- // hop-by-hop headers
- HDR_CONNECTION, HDR_KEEP_ALIVE, HDR_PROXY_AUTHENTICATE, HDR_PROXY_AUTHORIZATION,
- HDR_TE, HDR_TRAILER, HDR_TRANSFER_ENCODING, HDR_UPGRADE,
- // entity headers
- HDR_ALLOW, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, HDR_CONTENT_LENGTH,
- HDR_CONTENT_MD5, HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_LAST_MODIFIED
-};
-
-/* module initialization */
-void
-httpReplyInitModule(void)
-{
- assert(Http::scNone == 0); // HttpReply::parse() interface assumes that
- httpHeaderMaskInit(&Denied304HeadersMask, 0);
- httpHeaderCalcMask(&Denied304HeadersMask, Denied304HeadersArr, countof(Denied304HeadersArr));
-}
-
-HttpReply::HttpReply() : HttpMsg(hoReply), date (0), last_modified (0),
- expires (0), surrogate_control (NULL), content_range (NULL), keep_alive (0),
- protoPrefix("HTTP/"), bodySizeMax(-2)
+HttpReply::HttpReply():
+ Http::Message(hoReply),
+ date(0),
+ last_modified(0),
+ expires(0),
+ surrogate_control(nullptr),
+ keep_alive(0),
+ protoPrefix("HTTP/"),
+ bodySizeMax(-2),
+ content_range(nullptr)
{
init();
}
{
hdrCacheInit();
sline.init();
- pstate = psReadyToParseStartLine;
+ pstate = Http::Message::psReadyToParseStartLine;
do_clean = true;
}
}
void
-HttpReply::packHeadersInto(Packer * p) const
+HttpReply::packHeadersInto(Packable * p) const
{
sline.packInto(p);
header.packInto(p);
}
void
-HttpReply::packInto(Packer * p)
+HttpReply::packInto(Packable * p) const
{
packHeadersInto(p);
body.packInto(p);
/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */
MemBuf *
-HttpReply::pack()
+HttpReply::pack() const
{
MemBuf *mb = new MemBuf;
- Packer p;
-
mb->init();
- packerToMemInit(&p, mb);
- packInto(&p);
+ packInto(mb);
return mb;
}
HttpReply *
HttpReply::make304() const
{
- static const http_hdr_type ImsEntries[] = {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER};
+ static const Http::HdrType ImsEntries[] = {Http::HdrType::DATE, Http::HdrType::CONTENT_TYPE, Http::HdrType::EXPIRES, Http::HdrType::LAST_MODIFIED, /* eof */ Http::HdrType::OTHER};
HttpReply *rv = new HttpReply;
int t;
rv->last_modified = last_modified;
rv->expires = expires;
rv->content_type = content_type;
- /* rv->cache_control */
/* rv->content_range */
/* rv->keep_alive */
rv->sline.set(Http::ProtocolVersion(), Http::scNotModified, NULL);
- for (t = 0; ImsEntries[t] != HDR_OTHER; ++t)
+ for (t = 0; ImsEntries[t] != Http::HdrType::OTHER; ++t) {
if ((e = header.findEntry(ImsEntries[t])))
rv->header.addEntry(e->clone());
+ }
+
+ rv->putCc(cache_control);
/* rv->body */
return rv;
}
MemBuf *
-HttpReply::packed304Reply()
+HttpReply::packed304Reply() const
{
/* Not as efficient as skipping the header duplication,
* but easier to maintain
HttpHeader *hdr;
sline.set(Http::ProtocolVersion(), status, reason);
hdr = &header;
- hdr->putStr(HDR_SERVER, visible_appname_string);
- hdr->putStr(HDR_MIME_VERSION, "1.0");
- hdr->putTime(HDR_DATE, squid_curtime);
+ hdr->putStr(Http::HdrType::SERVER, visible_appname_string);
+ hdr->putStr(Http::HdrType::MIME_VERSION, "1.0");
+ hdr->putTime(Http::HdrType::DATE, squid_curtime);
if (ctype) {
- hdr->putStr(HDR_CONTENT_TYPE, ctype);
+ hdr->putStr(Http::HdrType::CONTENT_TYPE, ctype);
content_type = ctype;
} else
content_type = String();
if (clen >= 0)
- hdr->putInt64(HDR_CONTENT_LENGTH, clen);
+ hdr->putInt64(Http::HdrType::CONTENT_LENGTH, clen);
if (expiresTime >= 0)
- hdr->putTime(HDR_EXPIRES, expiresTime);
+ hdr->putTime(Http::HdrType::EXPIRES, expiresTime);
if (lmt > 0) /* this used to be lmt != 0 @?@ */
- hdr->putTime(HDR_LAST_MODIFIED, lmt);
+ hdr->putTime(Http::HdrType::LAST_MODIFIED, lmt);
date = squid_curtime;
HttpHeader *hdr;
sline.set(Http::ProtocolVersion(), status, NULL);
hdr = &header;
- hdr->putStr(HDR_SERVER, APP_FULLNAME);
- hdr->putTime(HDR_DATE, squid_curtime);
- hdr->putInt64(HDR_CONTENT_LENGTH, 0);
- hdr->putStr(HDR_LOCATION, loc);
+ hdr->putStr(Http::HdrType::SERVER, APP_FULLNAME);
+ hdr->putTime(Http::HdrType::DATE, squid_curtime);
+ hdr->putInt64(Http::HdrType::CONTENT_LENGTH, 0);
+ hdr->putStr(Http::HdrType::LOCATION, loc);
date = squid_curtime;
content_length = 0;
}
return 0;
/* ETag */
- one = header.getStrOrList(HDR_ETAG);
+ one = header.getStrOrList(Http::HdrType::ETAG);
- two = otherRep->header.getStrOrList(HDR_ETAG);
+ two = otherRep->header.getStrOrList(Http::HdrType::ETAG);
if (one.size()==0 || two.size()==0 || one.caseCmp(two)!=0 ) {
one.clean();
return 0;
/* MD5 */
- one = header.getStrOrList(HDR_CONTENT_MD5);
+ one = header.getStrOrList(Http::HdrType::CONTENT_MD5);
- two = otherRep->header.getStrOrList(HDR_CONTENT_MD5);
+ two = otherRep->header.getStrOrList(Http::HdrType::CONTENT_MD5);
if (one.size()==0 || two.size()==0 || one.caseCmp(two)!=0 ) {
one.clean();
return 1;
}
-void
+bool
HttpReply::updateOnNotModified(HttpReply const * freshRep)
{
assert(freshRep);
+ /* update raw headers */
+ if (!header.update(&freshRep->header))
+ return false;
+
/* clean cache */
hdrCacheClean();
- /* update raw headers */
- header.update(&freshRep->header,
- (const HttpHeaderMask *) &Denied304HeadersMask);
header.compact();
/* init cache */
hdrCacheInit();
+
+ return true;
}
/* internal routines */
/* The s-maxage and max-age directive takes priority over Expires */
if (cache_control) {
- if (date >= 0) {
- if (cache_control->hasSMaxAge())
- return date + cache_control->sMaxAge();
-
- if (cache_control->hasMaxAge())
- return date + cache_control->maxAge();
- } else {
- /*
- * Conservatively handle the case when we have a max-age
- * header, but no Date for reference?
- */
-
- if (cache_control->hasSMaxAge())
- return squid_curtime;
-
- if (cache_control->hasMaxAge())
- return squid_curtime;
- }
+ int maxAge = -1;
+ /*
+ * Conservatively handle the case when we have a max-age
+ * header, but no Date for reference?
+ */
+ if (cache_control->hasSMaxAge(&maxAge) || cache_control->hasMaxAge(&maxAge))
+ return (date >= 0) ? date + maxAge : squid_curtime;
}
if (Config.onoff.vary_ignore_expire &&
- header.has(HDR_VARY)) {
- const time_t d = header.getTime(HDR_DATE);
- const time_t e = header.getTime(HDR_EXPIRES);
+ header.has(Http::HdrType::VARY)) {
+ const time_t d = header.getTime(Http::HdrType::DATE);
+ const time_t e = header.getTime(Http::HdrType::EXPIRES);
if (d == e)
return -1;
}
- if (header.has(HDR_EXPIRES)) {
- const time_t e = header.getTime(HDR_EXPIRES);
+ if (header.has(Http::HdrType::EXPIRES)) {
+ const time_t e = header.getTime(Http::HdrType::EXPIRES);
/*
* HTTP/1.0 says that robust implementations should consider
* bad or malformed Expires header as equivalent to "expires
void
HttpReply::hdrCacheInit()
{
- HttpMsg::hdrCacheInit();
+ Http::Message::hdrCacheInit();
http_ver = sline.version;
- content_length = header.getInt64(HDR_CONTENT_LENGTH);
- date = header.getTime(HDR_DATE);
- last_modified = header.getTime(HDR_LAST_MODIFIED);
+ content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH);
+ date = header.getTime(Http::HdrType::DATE);
+ last_modified = header.getTime(Http::HdrType::LAST_MODIFIED);
surrogate_control = header.getSc();
- content_range = header.getContRange();
+ content_range = (sline.status() == Http::scPartialContent) ?
+ header.getContRange() : nullptr;
keep_alive = persistent() ? 1 : 0;
- const char *str = header.getStr(HDR_CONTENT_TYPE);
+ const char *str = header.getStr(Http::HdrType::CONTENT_TYPE);
if (str)
content_type.limitInit(str, strcspn(str, ";\t "));
expires = hdrExpirationTime();
}
+const HttpHdrContRange *
+HttpReply::contentRange() const
+{
+ assert(!content_range || sline.status() == Http::scPartialContent);
+ return content_range;
+}
+
/* sync this routine when you update HttpReply struct */
void
HttpReply::hdrCacheClean()
}
if (content_range) {
- httpHdrContRangeDestroy(content_range);
+ delete content_range;
content_range = NULL;
}
}
* NP: not all error cases are detected yet. Some are left for detection later in parse.
*/
bool
-HttpReply::sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, Http::StatusCode *error)
+HttpReply::sanityCheckStartLine(const char *buf, const size_t hdr_len, Http::StatusCode *error)
{
// hack warning: using psize instead of size here due to type mismatches with MemBuf.
// content is long enough to possibly hold a reply
// 4 being magic size of a 3-digit number plus space delimiter
- if ( buf->contentSize() < (protoPrefix.psize() + 4) ) {
+ if (hdr_len < (size_t)(protoPrefix.psize() + 4)) {
if (hdr_len > 0) {
- debugs(58, 3, HERE << "Too small reply header (" << hdr_len << " bytes)");
+ debugs(58, 3, "Too small reply header (" << hdr_len << " bytes)");
*error = Http::scInvalidHeader;
}
return false;
int pos;
// catch missing or mismatched protocol identifier
// allow special-case for ICY protocol (non-HTTP identifier) in response to faked HTTP request.
- if (strncmp(buf->content(), "ICY", 3) == 0) {
+ if (strncmp(buf, "ICY", 3) == 0) {
protoPrefix = "ICY";
pos = protoPrefix.psize();
} else {
- if (protoPrefix.cmp(buf->content(), protoPrefix.size()) != 0) {
- debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol prefix (" << protoPrefix << ") in '" << buf->content() << "'");
+ if (protoPrefix.cmp(buf, protoPrefix.size()) != 0) {
+ debugs(58, 3, "missing protocol prefix (" << protoPrefix << ") in '" << buf << "'");
*error = Http::scInvalidHeader;
return false;
}
pos = protoPrefix.psize();
// skip arbitrary number of digits and a dot in the verion portion
- while ( pos <= buf->contentSize() && (*(buf->content()+pos) == '.' || xisdigit(*(buf->content()+pos)) ) ) ++pos;
+ while ((size_t)pos <= hdr_len && (*(buf+pos) == '.' || xisdigit(*(buf+pos)) ) ) ++pos;
// catch missing version info
if (pos == protoPrefix.psize()) {
- debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol version numbers (ie. " << protoPrefix << "/1.0) in '" << buf->content() << "'");
+ debugs(58, 3, "missing protocol version numbers (ie. " << protoPrefix << "/1.0) in '" << buf << "'");
*error = Http::scInvalidHeader;
return false;
}
}
// skip arbitrary number of spaces...
- while (pos <= buf->contentSize() && (char)*(buf->content()+pos) == ' ') ++pos;
+ while ((size_t)pos <= hdr_len && (char)*(buf+pos) == ' ') ++pos;
- if (pos < buf->contentSize() && !xisdigit(*(buf->content()+pos))) {
- debugs(58, 3, "HttpReply::sanityCheckStartLine: missing or invalid status number in '" << buf->content() << "'");
+ if ((size_t)pos < hdr_len && !xisdigit(*(buf+pos))) {
+ debugs(58, 3, "missing or invalid status number in '" << buf << "'");
*error = Http::scInvalidHeader;
return false;
}
int
HttpReply::httpMsgParseError()
{
- int result(HttpMsg::httpMsgParseError());
+ int result(Http::Message::httpMsgParseError());
/* indicate an error in the status line */
sline.set(Http::ProtocolVersion(), Http::scInvalidHeader);
return result;
HTTPMSGLOCK(ch.reply);
for (AclSizeLimit *l = Config.ReplyBodySize; l; l = l -> next) {
/* if there is no ACL list or if the ACLs listed match use this size value */
- if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
+ if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
debugs(58, 4, HERE << "bodySizeMax=" << bodySizeMax);
bodySizeMax = l->size; // may be -1
break;
return rep;
}
-bool HttpReply::inheritProperties(const HttpMsg *aMsg)
+bool
+HttpReply::inheritProperties(const Http::Message *aMsg)
{
const HttpReply *aRep = dynamic_cast<const HttpReply*>(aMsg);
if (!aRep)
return false;
keep_alive = aRep->keep_alive;
+ sources = aRep->sources;
return true;
}
void HttpReply::removeStaleWarnings()
{
String warning;
- if (header.getList(HDR_WARNING, &warning)) {
+ if (header.getList(Http::HdrType::WARNING, &warning)) {
const String newWarning = removeStaleWarningValues(warning);
if (warning.size() && warning.size() == newWarning.size())
return; // some warnings are there and none changed
- header.delById(HDR_WARNING);
+ header.delById(Http::HdrType::WARNING);
if (newWarning.size()) { // some warnings left
HttpHeaderEntry *const e =
- new HttpHeaderEntry(HDR_WARNING, NULL, newWarning.termedBuf());
+ new HttpHeaderEntry(Http::HdrType::WARNING, SBuf(), newWarning.termedBuf());
header.addEntry(e);
}
}
return newValue;
}
+bool
+HttpReply::olderThan(const HttpReply *them) const
+{
+ if (!them || !them->date || !date)
+ return false;
+ return date < them->date;
+}
+