blob: ee96764ef16d549ae5808b263072d76611f76dd2 [file] [log] [blame]
/*
* Copyright 2013 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.
*/
// Authors: mmohabey@google.com (Megha Mohabey)
// pulkitg@google.com (Pulkit Goyal)
#include "net/instaweb/automatic/public/cache_html_flow.h"
#include "base/logging.h"
#include "net/instaweb/automatic/public/html_detector.h"
#include "net/instaweb/automatic/public/proxy_fetch.h"
#include "net/instaweb/http/http.pb.h"
#include "net/instaweb/http/public/async_fetch.h"
#include "net/instaweb/http/public/http_value.h"
#include "net/instaweb/http/public/log_record.h"
#include "net/instaweb/http/public/meta_data.h"
#include "net/instaweb/http/public/request_headers.h"
#include "net/instaweb/http/public/response_headers.h"
#include "net/instaweb/public/global_constants.h"
#include "net/instaweb/rewriter/cache_html_info.pb.h"
#include "net/instaweb/rewriter/public/blink_util.h"
#include "net/instaweb/rewriter/public/furious_matcher.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/server_context.h"
#include "net/instaweb/rewriter/public/static_asset_manager.h"
#include "net/instaweb/util/public/abstract_mutex.h"
#include "net/instaweb/util/public/basictypes.h"
#include "net/instaweb/util/public/function.h"
#include "net/instaweb/util/public/hasher.h"
#include "net/instaweb/util/public/property_cache.h"
#include "net/instaweb/util/public/proto_util.h"
#include "net/instaweb/util/public/statistics.h"
#include "net/instaweb/util/public/string_util.h"
#include "net/instaweb/util/public/thread_synchronizer.h"
#include "net/instaweb/util/public/thread_system.h"
#include "net/instaweb/util/public/timer.h"
#include "net/instaweb/util/public/scoped_ptr.h"
namespace net_instaweb {
const char kBlinkJsString[] =
"<script type=\"text/javascript\" src=\"%s\"></script>";
const char kCacheHtmlSuffixJsString[] =
"<script type=\"text/javascript\">"
"pagespeed.panelLoaderInit();"
"pagespeed.panelLoader.loadCriticalData({});"
"pagespeed.panelLoader.loadImagesData({});"
"</script>\n";
const char CacheHtmlFlow::kBackgroundComputationDone[] =
"BackgroundComputation:Done";
const char CacheHtmlFlow::kNumCacheHtmlHits[] =
"num_cache_html_hits";
const char CacheHtmlFlow::kNumCacheHtmlMisses[] =
"num_cache_html_misses";
const char CacheHtmlFlow::kNumCacheHtmlMatches[] =
"num_cache_html_matches";
const char CacheHtmlFlow::kNumCacheHtmlMismatches[] =
"num_cache_html_mismatches";
const char CacheHtmlFlow::kNumCacheHtmlMismatchesCacheDeletes[] =
"num_cache_html_mismatch_cache_deletes";
const char CacheHtmlFlow::kNumCacheHtmlSmartdiffMatches[] =
"num_cache_html_smart_diff_matches";
const char CacheHtmlFlow::kNumCacheHtmlSmartdiffMismatches[] =
"num_cache_html_smart_diff_mismatches";
namespace {
// Reads requisite info from Property Page. After reading, property page in
// driver is set to NULL, so that no one writes to property cache while
// rewriting cached html.
// TODO(mmohabey): Move the logic of copying properties in rewrite_driver when
// it is cloned.
void InitDriverWithPropertyCacheValues(
RewriteDriver* cache_html_driver, PropertyPage* page) {
// TODO(mmohabey): Critical line info should be populated here.
}
class CacheHtmlComputationFetch : public AsyncFetch {
public:
CacheHtmlComputationFetch(const GoogleString& url,
RewriteDriver* rewrite_driver,
CacheHtmlInfo* cache_html_info)
: AsyncFetch(rewrite_driver->request_context()),
url_(url),
server_context_(rewrite_driver->server_context()),
options_(rewrite_driver->options()),
rewrite_driver_(rewrite_driver),
cache_html_info_(cache_html_info),
claims_html_(false),
probable_html_(false),
content_length_over_threshold_(false),
non_ok_status_code_(false),
cache_html_change_mutex_(
server_context_->thread_system()->NewMutex()),
finish_(false) {
// Makes rewrite_driver live longer as ProxyFetch may called Cleanup()
// on the rewrite_driver even if ComputeCacheHtmlInfo() has not yet
// been triggered.
rewrite_driver_->increment_async_events_count();
Statistics* stats = server_context_->statistics();
num_cache_html_misses_ = stats->GetTimedVariable(
CacheHtmlFlow::kNumCacheHtmlMisses);
num_cache_html_matches_ = stats->GetTimedVariable(
CacheHtmlFlow::kNumCacheHtmlMatches);
num_cache_html_mismatches_ = stats->GetTimedVariable(
CacheHtmlFlow::kNumCacheHtmlMismatches);
num_cache_html_mismatches_cache_deletes_ = stats->GetTimedVariable(
CacheHtmlFlow::kNumCacheHtmlMismatchesCacheDeletes);
num_cache_html_smart_diff_matches_ = stats->GetTimedVariable(
CacheHtmlFlow::kNumCacheHtmlSmartdiffMatches);
num_cache_html_smart_diff_mismatches_ = stats->GetTimedVariable(
CacheHtmlFlow::kNumCacheHtmlSmartdiffMismatches);
}
virtual ~CacheHtmlComputationFetch() {
rewrite_driver_->decrement_async_events_count();
ThreadSynchronizer* sync = server_context_->thread_synchronizer();
sync->Signal(CacheHtmlFlow::kBackgroundComputationDone);
}
virtual void HandleHeadersComplete() {
if (response_headers()->status_code() == HttpStatus::kOK) {
claims_html_ = response_headers()->IsHtmlLike();
int64 content_length;
bool content_length_found = response_headers()->FindContentLength(
&content_length);
if (content_length_found && content_length >
options_->blink_max_html_size_rewritable()) {
content_length_over_threshold_ = true;
}
} else {
non_ok_status_code_ = true;
VLOG(1) << "Non 200 response code for: " << url_;
}
}
virtual bool HandleWrite(const StringPiece& content,
MessageHandler* handler) {
if (!claims_html_ || content_length_over_threshold_) {
return true;
}
if (!html_detector_.already_decided() &&
html_detector_.ConsiderInput(content)) {
if (html_detector_.probable_html()) {
probable_html_ = true;
html_detector_.ReleaseBuffered(&buffer_);
}
}
// TODO(poojatandon): share this logic of finding the length and setting a
// limit with http_cache code.
if (probable_html_) {
if (unsigned(buffer_.size() + content.size()) >
options_->blink_max_html_size_rewritable()) {
content_length_over_threshold_ = true;
buffer_.clear();
} else {
content.AppendToString(&buffer_);
}
}
return true;
}
virtual bool HandleFlush(MessageHandler* handler) {
return true;
// No operation.
}
virtual void HandleDone(bool success) {
if (non_ok_status_code_ || !success || !claims_html_ || !probable_html_ ||
content_length_over_threshold_) {
if (cache_html_info_->has_cached_html()) {
// This means it is a cache hit case. Currently it also means diff is
// enabled (possibly in logging mode), since CacheHtmlComputationFetch
// is attached in cache hit case only when diff is enabled.
// Calling Finish since the deletion of this object needs to be
// synchronized with HandleDone call in AsyncFetchWithHeadersInhibited,
// since that class refers to this object.
Finish();
} else {
delete this;
}
return;
}
if (rewrite_driver_->options()->enable_blink_html_change_detection() ||
rewrite_driver_->options()->
enable_blink_html_change_detection_logging()) {
// We do diff mismatch detection in cache miss case also so that we can
// update the content hash and smart text hash in CacheHtmlInfo in pcache.
CreateHtmlChangeDetectionDriverAndRewrite();
} else {
CreateCacheHtmlComputationDriverAndRewrite();
}
}
void CreateHtmlChangeDetectionDriverAndRewrite() {
RewriteOptions* options = rewrite_driver_->options()->Clone();
options->ClearFilters();
options->ForceEnableFilter(RewriteOptions::kRemoveComments);
options->ForceEnableFilter(RewriteOptions::kStripNonCacheable);
options->ForceEnableFilter(RewriteOptions::kComputeVisibleText);
server_context_->ComputeSignature(options);
html_change_detection_driver_ =
server_context_->NewCustomRewriteDriver(options, request_context());
value_.Clear();
html_change_detection_driver_->SetWriter(&value_);
html_change_detection_driver_->set_response_headers_ptr(response_headers());
complete_finish_parse_html_change_driver_fn_ = MakeFunction(
this,
&CacheHtmlComputationFetch::CompleteFinishParseForHtmlChangeDriver);
html_change_detection_driver_->AddLowPriorityRewriteTask(
MakeFunction(
this, &CacheHtmlComputationFetch::Parse,
&CacheHtmlComputationFetch::CancelParseForHtmlChangeDriver,
html_change_detection_driver_,
complete_finish_parse_html_change_driver_fn_));
}
void CreateCacheHtmlComputationDriverAndRewrite() {
RewriteOptions* options = rewrite_driver_->options()->Clone();
options->ClearFilters();
options->ForceEnableFilter(RewriteOptions::kStripNonCacheable);
cache_html_computation_driver_ =
server_context_->NewCustomRewriteDriver(options, request_context());
value_.Clear();
cache_html_computation_driver_->SetWriter(&value_);
cache_html_computation_driver_->set_response_headers_ptr(
response_headers());
complete_finish_parse_cache_html_driver_fn_ = MakeFunction(
this, &CacheHtmlComputationFetch::
CompleteFinishParseForCacheHtmlComputationDriver);
cache_html_computation_driver_->AddLowPriorityRewriteTask(
MakeFunction(
this, &CacheHtmlComputationFetch::Parse,
&CacheHtmlComputationFetch::
CancelParseForCacheHtmlComputationDriver,
cache_html_computation_driver_,
complete_finish_parse_cache_html_driver_fn_));
}
void Parse(RewriteDriver* driver, Function* task) {
driver->StartParse(url_);
driver->ParseText(buffer_);
driver->FinishParseAsync(task);
}
void CancelParseForCacheHtmlComputationDriver(RewriteDriver* driver,
Function* task) {
LOG(WARNING) << "Cache Html computation dropped due to load"
<< " for url: " << url_;
complete_finish_parse_cache_html_driver_fn_->CallCancel();
cache_html_computation_driver_->Cleanup();
delete this;
}
void CancelParseForHtmlChangeDriver(RewriteDriver* driver, Function* task) {
LOG(WARNING) << "Html change diff dropped due to load"
<< " for url: " << url_;
complete_finish_parse_html_change_driver_fn_->CallCancel();
html_change_detection_driver_->Cleanup();
Finish();
}
void CompleteFinishParseForCacheHtmlComputationDriver() {
StringPiece rewritten_content;
value_.ExtractContents(&rewritten_content);
cache_html_info_->set_cached_html(rewritten_content.data(),
rewritten_content.size());
cache_html_info_->set_last_cached_html_computation_timestamp_ms(
server_context_->timer()->NowMs());
if (!cache_html_info_->cached_html().empty() &&
!content_length_over_threshold_) {
UpdatePropertyCacheWithCacheHtmlInfo();
}
delete this;
}
void CompleteFinishParseForHtmlChangeDriver() {
StringPiece output;
value_.ExtractContents(&output);
StringPieceVector result;
net_instaweb::SplitStringUsingSubstr(
output, BlinkUtil::kComputeVisibleTextFilterOutputEndMarker,
&result);
if (result.size() == 2) {
computed_hash_smart_diff_ = server_context_->hasher()->Hash(result[0]);
computed_hash_ = server_context_->hasher()->Hash(result[1]);
}
if (!cache_html_info_->has_cached_html()) {
CreateCacheHtmlComputationDriverAndRewrite();
return;
}
if (computed_hash_ != cache_html_info_->hash()) {
num_cache_html_mismatches_->IncBy(1);
} else {
num_cache_html_matches_->IncBy(1);
}
if (computed_hash_smart_diff_ !=
cache_html_info_->hash_smart_diff()) {
num_cache_html_smart_diff_mismatches_->IncBy(1);
} else {
num_cache_html_smart_diff_matches_->IncBy(1);
}
Finish();
}
// This function should only be called if change detection is enabled and
// this is a cache hit case. In such cases, the content may need to be deleted
// from the property cache if a change was detected. This deletion should wait
// for AsyncFetchWithHeadersInhibited to complete (HandleDone called) to
// ensure that we do not delete entry from cache while it is still being used
// to process the request.
//
// This method achieves this goal using a mutex protected
// variable finish_. Both CacheHtmlComputationFetch and
// AsyncFetchWithHeadersInhibited call this method once their processing is
// done. The first call sets the value of finish_ to true and returns.
// The second call to this method actually calls ProcessDiffResult.
void Finish() {
{
ScopedMutex lock(cache_html_change_mutex_.get());
if (!finish_) {
finish_ = true;
return;
}
}
ProcessDiffResult();
}
// This method processes the result of html change detection. If a mismatch
// is found, we delete the entry from the cache and trigger a cache html info
// computation.
void ProcessDiffResult() {
if (computed_hash_.empty()) {
LOG(WARNING) << "Computed hash is empty for url " << url_;
delete this;
return;
}
bool compute_cache_html_info = false;
if (options_->use_smart_diff_in_blink()) {
compute_cache_html_info =
(computed_hash_smart_diff_ !=
cache_html_info_->hash_smart_diff());
} else {
compute_cache_html_info =
(computed_hash_ !=
cache_html_info_->hash());
}
// TODO(mmohabey): Incorporate DiffInfo.
if (options_->enable_blink_html_change_detection() &&
compute_cache_html_info) {
// TODO(mmohabey): Do not call delete here as we will be subsequently
// updating the new value in property cache using
// CreateCacheHtmlComputationDriverAndRewrite.
DeleteCacheHtmlInfoFromPropertyCache();
CreateCacheHtmlComputationDriverAndRewrite();
} else if (options_->enable_blink_html_change_detection() ||
computed_hash_ != cache_html_info_->hash() ||
computed_hash_smart_diff_ !=
cache_html_info_->hash_smart_diff()) {
UpdatePropertyCacheWithCacheHtmlInfo();
delete this;
} else {
delete this;
}
}
void UpdatePropertyCacheWithCacheHtmlInfo() {
cache_html_info_->set_charset(response_headers()->DetermineCharset());
cache_html_info_->set_hash(computed_hash_);
cache_html_info_->set_hash_smart_diff(computed_hash_smart_diff_);
PropertyCache* property_cache =
rewrite_driver_->server_context()->page_property_cache();
PropertyPage* page = rewrite_driver_->property_page();
const PropertyCache::Cohort* cohort = property_cache->GetCohort(
BlinkUtil::kBlinkCohort);
GoogleString buf;
cache_html_info_->SerializeToString(&buf);
PropertyValue* property_value = page->GetProperty(
cohort, BlinkUtil::kCacheHtmlRewriterInfo);
property_cache->UpdateValue(buf, property_value);
property_cache->WriteCohort(cohort, page);
}
void DeleteCacheHtmlInfoFromPropertyCache() {
num_cache_html_mismatches_cache_deletes_->IncBy(1);
const PropertyCache::Cohort* cohort =
rewrite_driver_->server_context()->page_property_cache()->
GetCohort(BlinkUtil::kBlinkCohort);
PropertyPage* page = rewrite_driver_->property_page();
page->DeleteProperty(cohort, BlinkUtil::kCacheHtmlRewriterInfo);
// TODO(mmohabey): Call WriteCohort only once in
// UpdatePropertyCacheWithCacheHtmlInfo and not here. This is to avoid
// property cache write race.
rewrite_driver_->server_context()->
page_property_cache()->WriteCohort(cohort, page);
// TODO(mmohabey): Add logic to propogate the deletes and deleting the
// critical line info.
}
private:
GoogleString url_;
ServerContext* server_context_;
const RewriteOptions* options_;
GoogleString buffer_;
HTTPValue value_;
HtmlDetector html_detector_;
GoogleString computed_hash_;
GoogleString computed_hash_smart_diff_;
HttpResponseHeaders http_response_headers_;
// RewriteDriver passed to ProxyFetch to serve user-facing request.
RewriteDriver* rewrite_driver_;
// RewriteDriver used to parse the buffered html content.
RewriteDriver* cache_html_computation_driver_;
RewriteDriver* html_change_detection_driver_;
scoped_ptr<LogRecord> log_record_;
scoped_ptr<CacheHtmlInfo> cache_html_info_;
Function* complete_finish_parse_cache_html_driver_fn_;
Function* complete_finish_parse_html_change_driver_fn_;
bool claims_html_;
bool probable_html_;
bool content_length_over_threshold_;
bool non_ok_status_code_;
// Variables to manage change detection processing.
// Mutex
scoped_ptr<AbstractMutex> cache_html_change_mutex_;
bool finish_; // protected by cache_html_change_mutex_
TimedVariable* num_cache_html_misses_;
TimedVariable* num_cache_html_matches_;
TimedVariable* num_cache_html_mismatches_;
TimedVariable* num_cache_html_mismatches_cache_deletes_;
TimedVariable* num_cache_html_smart_diff_matches_;
TimedVariable* num_cache_html_smart_diff_mismatches_;
DISALLOW_COPY_AND_ASSIGN(CacheHtmlComputationFetch);
};
// AsyncFetch that doesn't call HeadersComplete() on the base fetch. Note that
// this class only links the request headers from the base fetch and does not
// link the response headers.
// This is used as a wrapper around the base fetch when CacheHtmlInfo is
// found in cache. This is done because the response headers and the
// cached html have been already been flushed out in the base fetch
// and we don't want to call HeadersComplete() twice on the base fetch.
// This class deletes itself when HandleDone() is called.
class AsyncFetchWithHeadersInhibited : public AsyncFetchUsingWriter {
public:
AsyncFetchWithHeadersInhibited(
AsyncFetch* fetch,
CacheHtmlComputationFetch* cache_html_computation_fetch)
: AsyncFetchUsingWriter(fetch->request_context(), fetch),
base_fetch_(fetch),
cache_html_computation_fetch_(cache_html_computation_fetch) {
set_request_headers(fetch->request_headers());
}
private:
virtual ~AsyncFetchWithHeadersInhibited() {
}
virtual void HandleHeadersComplete() {}
virtual void HandleDone(bool success) {
base_fetch_->Done(success);
if (cache_html_computation_fetch_ != NULL) {
cache_html_computation_fetch_->Finish();
}
delete this;
}
AsyncFetch* base_fetch_;
CacheHtmlComputationFetch* cache_html_computation_fetch_;
DISALLOW_COPY_AND_ASSIGN(AsyncFetchWithHeadersInhibited);
};
} // namespace
void CacheHtmlFlow::Start(
const GoogleString& url,
AsyncFetch* base_fetch,
RewriteDriver* driver,
ProxyFetchFactory* factory,
ProxyFetchPropertyCallbackCollector* property_cache_callback) {
CacheHtmlFlow* flow = new CacheHtmlFlow(
url, base_fetch, driver, factory, property_cache_callback);
Function* func = MakeFunction(flow, &CacheHtmlFlow::CacheHtmlLookupDone,
&CacheHtmlFlow::Cancel);
property_cache_callback->AddPostLookupTask(func);
}
void CacheHtmlFlow::InitStats(Statistics* stats) {
stats->AddTimedVariable(kNumCacheHtmlHits,
ServerContext::kStatisticsGroup);
stats->AddTimedVariable(kNumCacheHtmlMisses,
ServerContext::kStatisticsGroup);
stats->AddTimedVariable(kNumCacheHtmlMatches,
ServerContext::kStatisticsGroup);
stats->AddTimedVariable(kNumCacheHtmlMismatches,
ServerContext::kStatisticsGroup);
stats->AddTimedVariable(kNumCacheHtmlMismatchesCacheDeletes,
ServerContext::kStatisticsGroup);
stats->AddTimedVariable(kNumCacheHtmlSmartdiffMatches,
ServerContext::kStatisticsGroup);
stats->AddTimedVariable(kNumCacheHtmlSmartdiffMismatches,
ServerContext::kStatisticsGroup);
}
CacheHtmlFlow::CacheHtmlFlow(
const GoogleString& url,
AsyncFetch* base_fetch,
RewriteDriver* driver,
ProxyFetchFactory* factory,
ProxyFetchPropertyCallbackCollector* property_cache_callback)
: url_(url),
google_url_(url),
base_fetch_(base_fetch),
rewrite_driver_(driver),
options_(driver->options()),
factory_(factory),
server_context_(driver->server_context()),
property_cache_callback_(property_cache_callback),
handler_(rewrite_driver_->server_context()->message_handler()) {
Statistics* stats = server_context_->statistics();
num_cache_html_misses_ = stats->GetTimedVariable(
kNumCacheHtmlMisses);
num_cache_html_hits_ = stats->GetTimedVariable(
kNumCacheHtmlHits);
}
CacheHtmlFlow::~CacheHtmlFlow() {
}
void CacheHtmlFlow::CacheHtmlLookupDone() {
PropertyPage* page = property_cache_callback_->
GetPropertyPageWithoutOwnership(
ProxyFetchPropertyCallback::kPagePropertyCache);
PopulateCacheHtmlInfo(page);
// TODO(mmohabey): Add timings and dashboard.
if (cache_html_info_.has_cached_html()) {
CacheHtmlHit(page);
} else {
CacheHtmlMiss();
}
}
void CacheHtmlFlow::CacheHtmlMiss() {
num_cache_html_misses_->IncBy(1);
TriggerProxyFetch();
}
void CacheHtmlFlow::CacheHtmlHit(PropertyPage* page) {
num_cache_html_hits_->IncBy(1);
StringPiece cached_html = cache_html_info_.cached_html();
// TODO(mmohabey): Handle malformed html case.
ResponseHeaders* response_headers = base_fetch_->response_headers();
response_headers->SetStatusAndReason(HttpStatus::kOK);
// TODO(pulkitg): Store content type in pcache.
// TODO(mmohabey): Handle Meta tags.
GoogleString content_type = StrCat(
"text/html", cache_html_info_.has_charset() ?
StrCat("; charset=", cache_html_info_.charset()) : "");
response_headers->Add(HttpAttributes::kContentType, content_type);
response_headers->Add(
kPsaRewriterHeader,
RewriteOptions::FilterId(RewriteOptions::kCacheHtml));
response_headers->ComputeCaching();
response_headers->SetDateAndCaching(server_context_->timer()->NowMs(), 0,
", private, no-cache");
// If relevant, add the Set-Cookie header for furious experiments.
if (options_->need_to_store_experiment_data() &&
options_->running_furious()) {
int furious_value = options_->furious_id();
server_context_->furious_matcher()->StoreExperimentData(
furious_value, url_,
server_context_->timer()->NowMs() +
options_->furious_cookie_duration_ms(),
response_headers);
}
base_fetch_->HeadersComplete();
// Clone the RewriteDriver which is used to rewrite the HTML that we are
// trying to flush early.
RewriteDriver* new_driver = rewrite_driver_->Clone();
new_driver->set_response_headers_ptr(base_fetch_->response_headers());
new_driver->set_flushing_cached_html(true);
new_driver->SetWriter(base_fetch_);
new_driver->StartParse(url_);
InitDriverWithPropertyCacheValues(new_driver, page);
new_driver->ParseText(cached_html);
new_driver->FinishParseAsync(
MakeFunction(this, &CacheHtmlFlow::CacheHtmlRewriteDone));
}
void CacheHtmlFlow::CacheHtmlRewriteDone() {
rewrite_driver_->set_flushed_cached_html(true);
StaticAssetManager* static_asset_manager =
server_context_->static_asset_manager();
base_fetch_->Write(StringPrintf(kBlinkJsString,
static_asset_manager->GetAssetUrl(
StaticAssetManager::kBlinkJs, options_).c_str()), handler_);
base_fetch_->Write(kCacheHtmlSuffixJsString, handler_);
base_fetch_->Flush(handler_);
TriggerProxyFetch();
}
void CacheHtmlFlow::TriggerProxyFetch() {
bool flushed_cached_html = rewrite_driver_->flushed_cached_html();
AsyncFetch* fetch = NULL;
CacheHtmlComputationFetch* cache_html_computation_fetch = NULL;
// Remove any headers that can lead to a 304, since CacheHtmlFlow can't
// handle 304s.
base_fetch_->request_headers()->RemoveAll(HttpAttributes::kIfNoneMatch);
base_fetch_->request_headers()->RemoveAll(HttpAttributes::kIfModifiedSince);
if (!flushed_cached_html || options_->enable_blink_html_change_detection() ||
options_->enable_blink_html_change_detection_logging()) {
CacheHtmlInfo* cache_html_info = new CacheHtmlInfo();
cache_html_info->CopyFrom(cache_html_info_);
cache_html_computation_fetch = new CacheHtmlComputationFetch(
url_, rewrite_driver_, cache_html_info);
// TODO(mmohabey) : Set a fixed user agent for fetching content from the
// origin server if options->use_fixed_user_agent_for_blink_cache_misses()
// is enabled.
}
if (flushed_cached_html) {
// TODO(mmohabey): Disable LazyloadImages filter for the driver sending non
// cacheables.
fetch = new AsyncFetchWithHeadersInhibited(base_fetch_,
cache_html_computation_fetch);
} else {
// PassThrough case.
// This flow has side effect that DeferJs is applied in passthrough case
// even when it is not explicitly enabled since it is added in
// RewriteDriver::AddPostRenderFilters() if RewriteOptions::kCacheHtml is
// enabled.
fetch = base_fetch_;
}
factory_->StartNewProxyFetch(
url_, fetch, rewrite_driver_, property_cache_callback_,
cache_html_computation_fetch);
delete this;
}
// TODO(mmohabey): Disable conflicting filters for cache html flow.
void CacheHtmlFlow::Cancel() {
delete this;
}
void CacheHtmlFlow::PopulateCacheHtmlInfo(PropertyPage* page) {
const PropertyCache::Cohort* cohort = server_context_->page_property_cache()->
GetCohort(BlinkUtil::kBlinkCohort);
if (page == NULL || cohort == NULL) {
return;
}
PropertyValue* property_value = page->GetProperty(
cohort, BlinkUtil::kCacheHtmlRewriterInfo);
if (!property_value->has_value()) {
return;
}
ArrayInputStream value(property_value->value().data(),
property_value->value().size());
if (!cache_html_info_.ParseFromZeroCopyStream(&value)) {
LOG(DFATAL) << "Parsing value from cache into CacheHtmlInfo failed.";
cache_html_info_.Clear();
return;
}
int64 expiration_time_ms =
cache_html_info_.last_cached_html_computation_timestamp_ms() +
options_->GetBlinkCacheTimeFor(google_url_);
if (!options_->enable_blink_html_change_detection() &&
server_context_->timer()->NowMs() > expiration_time_ms) {
cache_html_info_.Clear();
return;
}
}
} // namespace net_instaweb