blob: 0cd22324f17b0cafa6aec57e674761d1135a123e [file] [log] [blame]
/**
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: jmarantz@google.com (Joshua Marantz)
#include "net/instaweb/util/public/http_cache.h"
#include "net/instaweb/util/public/cache_interface.h"
#include "net/instaweb/util/public/http_value.h"
#include "net/instaweb/util/public/message_handler.h"
#include "net/instaweb/util/public/meta_data.h"
#include "net/instaweb/util/public/shared_string.h"
#include "net/instaweb/util/public/simple_meta_data.h"
#include "net/instaweb/util/public/statistics.h"
#include "net/instaweb/util/public/timer.h"
namespace net_instaweb {
namespace {
// Remember that a Fetch failed for 5 minutes.
// TODO(jmarantz): consider allowing this to be configurable.
//
// TODO(jmarantz): We could handle cc-private a little differently:
// in this case we could arguably remember it using the original cc-private ttl.
const char kRememberNotFoundCacheControl[] = "max-age=300";
const char kCacheTimeUs[] = "cache_time_us";
const char kCacheHits[] = "cache_hits";
const char kCacheMisses[] = "cache_misses";
const char kCacheExpirations[] = "cache_expirations";
const char kCacheInserts[] = "cache_inserts";
} // namespace
HTTPCache::~HTTPCache() {}
bool HTTPCache::IsCurrentlyValid(const MetaData& headers, int64 now_ms) {
if (force_caching_) {
return true;
}
if (!headers.IsCacheable() || !headers.IsProxyCacheable()) {
// TODO(jmarantz): Should we have a separate 'force' bit that doesn't
// expired resources to be valid, but does ignore cache-control:private?
return false;
}
if (headers.CacheExpirationTimeMs() > now_ms) {
return true;
}
if (cache_expirations_ != NULL) {
cache_expirations_->Add(1);
}
return false;
}
HTTPCache::FindResult HTTPCache::Find(
const std::string& key, HTTPValue* value, MetaData* headers,
MessageHandler* handler) {
SharedString cache_buffer;
int64 start_us = timer_->NowUs();
int64 now_ms = start_us / 1000;
FindResult ret = kNotFound;
if ((cache_->Get(key, &cache_buffer) &&
value->Link(&cache_buffer, headers, handler))) {
if (IsCurrentlyValid(*headers, now_ms)) {
if (headers->status_code() == HttpStatus::kRememberNotFoundStatusCode) {
long remember_not_found_time_ms = headers->CacheExpirationTimeMs()
- now_ms;
handler->Info(key.c_str(), 0,
"HTTPCache: remembering not-found status for %ld seconds",
remember_not_found_time_ms / 1000);
ret = kRecentFetchFailedDoNotRefetch;
} else {
ret = kFound;
}
}
}
if (cache_time_us_ != NULL) {
long delta_us = timer_->NowUs() - start_us;
cache_time_us_->Add(delta_us);
if (ret == kFound) {
cache_hits_->Add(1);
} else {
cache_misses_->Add(1);
}
}
if (ret != kFound) {
headers->Clear();
value->Clear();
}
return ret;
}
void HTTPCache::RememberNotCacheable(const std::string& key,
MessageHandler* handler) {
SimpleMetaData headers;
headers.set_status_code(HttpStatus::kRememberNotFoundStatusCode);
headers.Add(HttpAttributes::kCacheControl, kRememberNotFoundCacheControl);
int64 now_ms = timer_->NowMs();
headers.UpdateDateHeader(HttpAttributes::kDate, now_ms);
headers.ComputeCaching();
headers.set_headers_complete(true);
Put(key, headers, "", handler);
}
void HTTPCache::Put(const std::string& key, HTTPValue* value,
MessageHandler* handler) {
SharedString* shared_string = value->share();
cache_->Put(key, shared_string);
}
void HTTPCache::Put(const std::string& key, const MetaData& headers,
const StringPiece& content,
MessageHandler* handler) {
int64 start_us = timer_->NowUs();
int64 now_ms = start_us / 1000;
if (!IsCurrentlyValid(headers, now_ms)) {
return;
}
HTTPValue value;
value.SetHeaders(headers);
value.Write(content, handler);
Put(key, &value, handler);
if (cache_time_us_ != NULL) {
int64 delta_us = timer_->NowUs() - start_us;
cache_time_us_->Add(delta_us);
cache_inserts_->Add(1);
}
}
CacheInterface::KeyState HTTPCache::Query(const std::string& key) {
return cache_->Query(key);
}
void HTTPCache::Delete(const std::string& key) {
return cache_->Delete(key);
}
void HTTPCache::Initialize(Statistics* statistics) {
statistics->AddVariable(kCacheTimeUs);
statistics->AddVariable(kCacheHits);
statistics->AddVariable(kCacheMisses);
statistics->AddVariable(kCacheExpirations);
statistics->AddVariable(kCacheInserts);
}
void HTTPCache::SetStatistics(Statistics* statistics) {
if (statistics != NULL) {
cache_time_us_ = statistics->GetVariable(kCacheTimeUs);
cache_hits_ = statistics->GetVariable(kCacheHits);
cache_misses_ = statistics->GetVariable(kCacheMisses);
cache_expirations_ = statistics->GetVariable(kCacheExpirations);
cache_inserts_ = statistics->GetVariable(kCacheInserts);
}
}
} // namespace net_instaweb