176 lines
6.6 KiB
C++
176 lines
6.6 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
// Author: anupama@google.com (Anupama Dutta)
|
|
|
|
#include "net/instaweb/rewriter/public/downstream_cache_purger.h"
|
|
|
|
#include "net/instaweb/http/public/async_fetch.h"
|
|
#include "net/instaweb/http/public/request_context.h"
|
|
#include "net/instaweb/http/public/url_async_fetcher.h"
|
|
#include "net/instaweb/public/global_constants.h"
|
|
#include "net/instaweb/rewriter/public/rewrite_driver.h"
|
|
#include "net/instaweb/rewriter/public/rewrite_options.h"
|
|
#include "net/instaweb/rewriter/public/rewrite_stats.h"
|
|
#include "net/instaweb/rewriter/public/server_context.h"
|
|
#include "pagespeed/kernel/base/message_handler.h"
|
|
#include "pagespeed/kernel/base/statistics.h"
|
|
#include "pagespeed/kernel/base/string_util.h"
|
|
#include "pagespeed/kernel/base/thread_annotations.h"
|
|
#include "pagespeed/kernel/http/google_url.h"
|
|
#include "pagespeed/kernel/http/http_names.h"
|
|
#include "pagespeed/kernel/http/request_headers.h"
|
|
#include "pagespeed/kernel/http/response_headers.h"
|
|
|
|
namespace net_instaweb {
|
|
|
|
namespace {
|
|
|
|
class StringAsyncFetchWithAsyncCountUpdates : public StringAsyncFetch {
|
|
public:
|
|
StringAsyncFetchWithAsyncCountUpdates(const RequestContextPtr& ctx,
|
|
RewriteDriver* driver)
|
|
: StringAsyncFetch(ctx),
|
|
driver_(driver) {
|
|
driver_->IncrementAsyncEventsCount();
|
|
}
|
|
|
|
virtual ~StringAsyncFetchWithAsyncCountUpdates() { }
|
|
|
|
virtual void HandleDone(bool success) {
|
|
if (response_headers()->status_code() == HttpStatus::kOK) {
|
|
driver_->server_context()->rewrite_stats()->
|
|
successful_downstream_cache_purges()->Add(1);
|
|
}
|
|
StringAsyncFetch::HandleDone(success);
|
|
driver_->DecrementAsyncEventsCount();
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
RewriteDriver* driver_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(StringAsyncFetchWithAsyncCountUpdates);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
DownstreamCachePurger::DownstreamCachePurger(RewriteDriver* driver)
|
|
: driver_(driver),
|
|
made_downstream_purge_attempt_(false) {
|
|
}
|
|
|
|
DownstreamCachePurger::~DownstreamCachePurger() {}
|
|
|
|
void DownstreamCachePurger::Clear() {
|
|
purge_url_.clear();
|
|
purge_method_.clear();
|
|
made_downstream_purge_attempt_ = false;
|
|
}
|
|
|
|
bool DownstreamCachePurger::GeneratePurgeRequestParameters(
|
|
const GoogleUrl& page_url) {
|
|
purge_url_ = StrCat(
|
|
driver_->options()->downstream_cache_purge_location_prefix(),
|
|
page_url.PathAndLeaf());
|
|
purge_method_ = driver_->options()->downstream_cache_purge_method();
|
|
return (!purge_url_.empty() && !purge_method_.empty());
|
|
}
|
|
|
|
// This function uses a few variables gaurded by rewrite_mutex() without locking
|
|
// it, but we should not have concurrent responses at this point so thread
|
|
// safety analysis is disabled.
|
|
bool DownstreamCachePurger::ShouldPurgeRewrittenResponse(
|
|
const GoogleUrl& google_url) NO_THREAD_SAFETY_ANALYSIS {
|
|
if (!driver_->options()->IsDownstreamCacheIntegrationEnabled()) {
|
|
// Downstream caching is not enabled.
|
|
return false;
|
|
}
|
|
if (driver_->num_initiated_rewrites() == 0) {
|
|
// No rewrites were initiated. Could happen if the rewriters
|
|
// enabled don't apply on the page, or apply instantly (e.g.
|
|
// collapse whitespace).
|
|
return false;
|
|
}
|
|
// Figure out what percentage of the rewriting was done before the
|
|
// response was served out, so that we can initiate a cache purge if there
|
|
// was significant amount of rewriting remaining to be done.
|
|
float served_rewritten_percentage =
|
|
((driver_->num_initiated_rewrites() - driver_->num_detached_rewrites()) *
|
|
100.0) /
|
|
driver_->num_initiated_rewrites();
|
|
if (served_rewritten_percentage <
|
|
driver_->options()->downstream_cache_rewritten_percentage_threshold()) {
|
|
driver_->message_handler()->Message(
|
|
kInfo,
|
|
"Should purge \"%s\" which was served with only %d%% rewriting done.",
|
|
google_url.spec_c_str(),
|
|
static_cast<int>(served_rewritten_percentage));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DownstreamCachePurger::PurgeDownstreamCache() {
|
|
// TODO(anupama): Use purge_method actually.
|
|
StringAsyncFetchWithAsyncCountUpdates* dummy_fetch =
|
|
new StringAsyncFetchWithAsyncCountUpdates(driver_->request_context(),
|
|
driver_);
|
|
// Add a purge-related header so that the purge request does not
|
|
// get us into a loop.
|
|
dummy_fetch->request_headers()->CopyFrom(*driver_->request_headers());
|
|
dummy_fetch->request_headers()->Add(kPsaPurgeRequest, "1");
|
|
if (purge_method_ == "PURGE") {
|
|
dummy_fetch->request_headers()->set_method(RequestHeaders::kPurge);
|
|
}
|
|
// Record the fact that a purge attempt has been made so that we do not
|
|
// issue multiple purges using the same RewriteDriver object.
|
|
made_downstream_purge_attempt_ = true;
|
|
|
|
driver_->message_handler()->Message(kInfo,
|
|
"Purge url is %s", purge_url_.c_str());
|
|
driver_->async_fetcher()->Fetch(purge_url_, driver_->message_handler(),
|
|
dummy_fetch);
|
|
}
|
|
|
|
bool DownstreamCachePurger::MaybeIssuePurge(const GoogleUrl& google_url) {
|
|
// If any of the following conditions are satisfied, we do not issue a purge:
|
|
// a) a purge attempt has already been made
|
|
// b) request headers have not been set
|
|
// c) this is a looped back purge request
|
|
// d) rewritten response is not optimized enough to warrant a purge
|
|
// e) valid purge URL or method are unavailable
|
|
if (!made_downstream_purge_attempt_ &&
|
|
driver_->request_headers() != NULL &&
|
|
driver_->request_headers()->Lookup1(kPsaPurgeRequest) == NULL &&
|
|
driver_->request_headers()->method() == RequestHeaders::kGet &&
|
|
google_url.IsWebValid() &&
|
|
ShouldPurgeRewrittenResponse(google_url) &&
|
|
GeneratePurgeRequestParameters(google_url)) {
|
|
driver_->server_context()->rewrite_stats()->
|
|
downstream_cache_purge_attempts()->Add(1);
|
|
// Purge old version from cache since we will have a better rewritten
|
|
// version available on the next request. The purge request will
|
|
// use the same request headers as the request (and hence the same
|
|
// UserAgent etc.).
|
|
PurgeDownstreamCache();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace net_instaweb
|