bd14840540
(which is more than one would think of at first due to it having somewhat peculiar semantics).
211 lines
7.9 KiB
C++
211 lines
7.9 KiB
C++
/*
|
|
* Copyright 2011 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: sligocki@google.com (Shawn Ligocki)
|
|
|
|
#include "net/instaweb/rewriter/public/resource_fetch.h"
|
|
|
|
#include "base/logging.h"
|
|
#include "net/instaweb/http/public/async_fetch.h"
|
|
#include "net/instaweb/http/public/request_context.h"
|
|
#include "net/instaweb/http/public/sync_fetcher_adapter_callback.h"
|
|
#include "net/instaweb/public/global_constants.h"
|
|
#include "net/instaweb/rewriter/public/resource_namer.h"
|
|
#include "net/instaweb/rewriter/public/rewrite_driver.h"
|
|
#include "net/instaweb/rewriter/public/rewrite_driver_pool.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/timer.h"
|
|
#include "pagespeed/kernel/http/http_names.h"
|
|
#include "pagespeed/kernel/http/response_headers.h"
|
|
|
|
namespace net_instaweb {
|
|
|
|
void ResourceFetch::ApplyExperimentOptions(const GoogleUrl& url,
|
|
const RequestContextPtr& request_ctx,
|
|
ServerContext* server_context,
|
|
RewriteOptions** custom_options) {
|
|
const RewriteOptions* active_options;
|
|
if (*custom_options == NULL) {
|
|
RewriteDriverPool* driver_pool =
|
|
server_context->standard_rewrite_driver_pool();
|
|
active_options = driver_pool->TargetOptions();
|
|
} else {
|
|
active_options = *custom_options;
|
|
}
|
|
if (active_options->running_experiment()) {
|
|
// If we're running an experiment and this resource url specifies a
|
|
// experiment_spec, make sure the custom options have that experiment
|
|
// selected.
|
|
ResourceNamer namer;
|
|
namer.DecodeIgnoreHashAndSignature(url.LeafSansQuery());
|
|
if (namer.has_experiment()) {
|
|
if (*custom_options == NULL) {
|
|
*custom_options = active_options->Clone();
|
|
}
|
|
(*custom_options)->SetExperimentStateStr(namer.experiment());
|
|
server_context->ComputeSignature(*custom_options);
|
|
}
|
|
}
|
|
}
|
|
|
|
RewriteDriver* ResourceFetch::GetDriver(
|
|
const GoogleUrl& url, RewriteOptions* custom_options,
|
|
ServerContext* server_context, const RequestContextPtr& request_ctx) {
|
|
ApplyExperimentOptions(url, request_ctx, server_context, &custom_options);
|
|
RewriteDriver* driver = (custom_options == NULL)
|
|
? server_context->NewRewriteDriver(request_ctx)
|
|
: server_context->NewCustomRewriteDriver(custom_options, request_ctx);
|
|
return driver;
|
|
}
|
|
|
|
void ResourceFetch::StartWithDriver(
|
|
const GoogleUrl& url, CleanupMode cleanup_mode,
|
|
ServerContext* server_context, RewriteDriver* driver,
|
|
AsyncFetch* async_fetch) {
|
|
|
|
ResourceFetch* resource_fetch = new ResourceFetch(
|
|
url, cleanup_mode, driver, server_context->timer(),
|
|
server_context->message_handler(), async_fetch);
|
|
|
|
if (!driver->FetchResource(url.Spec(), resource_fetch)) {
|
|
resource_fetch->Done(false);
|
|
}
|
|
}
|
|
|
|
void ResourceFetch::Start(const GoogleUrl& url,
|
|
RewriteOptions* custom_options,
|
|
ServerContext* server_context,
|
|
AsyncFetch* async_fetch) {
|
|
RewriteDriver* driver = GetDriver(
|
|
url, custom_options, server_context, async_fetch->request_context());
|
|
StartWithDriver(url, kAutoCleanupDriver,
|
|
server_context, driver, async_fetch);
|
|
}
|
|
|
|
bool ResourceFetch::BlockingFetch(const GoogleUrl& url,
|
|
ServerContext* server_context,
|
|
RewriteDriver* driver,
|
|
SyncFetcherAdapterCallback* callback) {
|
|
// Here, we do not want the driver to be cleaned up by the ResourceFetch
|
|
// since we will be calling BoundedWaitFor on it!
|
|
StartWithDriver(url, kDontAutoCleanupDriver, server_context, driver,
|
|
callback);
|
|
|
|
// Wait for resource fetch to complete.
|
|
if (!callback->IsDone()) {
|
|
int64 max_ms = driver->options()->blocking_fetch_timeout_ms();
|
|
for (int64 start_ms = server_context->timer()->NowMs(), now_ms = start_ms;
|
|
!callback->IsDone() && now_ms - start_ms < max_ms;
|
|
now_ms = server_context->timer()->NowMs()) {
|
|
int64 remaining_ms = max_ms - (now_ms - start_ms);
|
|
|
|
driver->BoundedWaitFor(RewriteDriver::kWaitForCompletion, remaining_ms);
|
|
}
|
|
}
|
|
|
|
MessageHandler* message_handler = server_context->message_handler();
|
|
bool ok = false;
|
|
if (callback->IsDone()) {
|
|
if (callback->success()) {
|
|
ok = true;
|
|
} else {
|
|
message_handler->Message(kWarning, "Fetch failed for %s, status=%d",
|
|
url.spec_c_str(),
|
|
callback->response_headers()->status_code());
|
|
}
|
|
} else {
|
|
message_handler->Message(kWarning, "Fetch timed out for %s",
|
|
url.spec_c_str());
|
|
}
|
|
|
|
driver->Cleanup();
|
|
|
|
return ok;
|
|
}
|
|
|
|
ResourceFetch::ResourceFetch(const GoogleUrl& url,
|
|
CleanupMode cleanup_mode,
|
|
RewriteDriver* driver,
|
|
Timer* timer,
|
|
MessageHandler* handler,
|
|
AsyncFetch* async_fetch)
|
|
: SharedAsyncFetch(async_fetch),
|
|
driver_(driver),
|
|
timer_(timer),
|
|
message_handler_(handler),
|
|
start_time_ms_(timer->NowMs()),
|
|
redirect_count_(0),
|
|
cleanup_mode_(cleanup_mode) {
|
|
resource_url_.Reset(url);
|
|
DCHECK(driver_->request_headers() == NULL);
|
|
}
|
|
|
|
ResourceFetch::~ResourceFetch() {
|
|
}
|
|
|
|
void ResourceFetch::HandleHeadersComplete() {
|
|
// We do not want any cookies (or other person information) in pagespeed
|
|
// resources. They shouldn't be here anyway, but we assure that.
|
|
ConstStringStarVector v;
|
|
DCHECK(!response_headers()->Lookup(HttpAttributes::kSetCookie, &v));
|
|
DCHECK(!response_headers()->Lookup(HttpAttributes::kSetCookie2, &v));
|
|
response_headers()->RemoveAll(HttpAttributes::kSetCookie);
|
|
response_headers()->RemoveAll(HttpAttributes::kSetCookie2);
|
|
|
|
for (int i = 0; i < driver_->options()->num_resource_headers(); ++i) {
|
|
const RewriteOptions::NameValue* nv =
|
|
driver_->options()->resource_header(i);
|
|
response_headers()->Add(nv->name, nv->value);
|
|
}
|
|
|
|
// "Vary: Accept-Encoding" for all resources that are transmitted compressed.
|
|
// Server ought to set these, I suppose.
|
|
// response_headers()->Add(HttpAttributes::kVary, "Accept-Encoding");
|
|
|
|
response_headers()->Add(kPageSpeedHeader,
|
|
driver_->options()->x_header_value());
|
|
SharedAsyncFetch::HandleHeadersComplete();
|
|
}
|
|
|
|
void ResourceFetch::HandleDone(bool success) {
|
|
if (success) {
|
|
LOG(INFO) << "Resource " << resource_url_.Spec()
|
|
<< " : " << response_headers()->status_code();
|
|
} else {
|
|
// This is a fetcher failure, like connection refused, not just an error
|
|
// status code.
|
|
LOG(WARNING) << "Fetch failed for resource url " << resource_url_.Spec();
|
|
if (!response_headers()->headers_complete()) {
|
|
response_headers()->SetStatusAndReason(HttpStatus::kNotFound);
|
|
}
|
|
}
|
|
RewriteStats* stats = driver_->server_context()->rewrite_stats();
|
|
stats->fetch_latency_histogram()->Add(timer_->NowMs() - start_time_ms_);
|
|
stats->total_fetch_count()->IncBy(1);
|
|
if (cleanup_mode_ == kAutoCleanupDriver) {
|
|
driver_->Cleanup();
|
|
}
|
|
SharedAsyncFetch::HandleDone(success);
|
|
delete this;
|
|
}
|
|
|
|
} // namespace net_instaweb
|