blob: 56e8bfdf4188ee58d793a8ae07a70cd38ee48793 [file] [log] [blame]
/*
* Copyright 2014 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: morlovich@google.com (Maksim Orlovich)
#include "net/instaweb/http/public/simulated_delay_fetcher.h"
#include <utility>
#include "net/instaweb/http/public/async_fetch.h"
#include "pagespeed/kernel/base/abstract_mutex.h"
#include "pagespeed/kernel/base/file_system.h"
#include "pagespeed/kernel/base/function.h"
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/base/thread_system.h"
#include "pagespeed/kernel/base/time_util.h"
#include "pagespeed/kernel/base/timer.h"
#include "pagespeed/kernel/http/google_url.h"
#include "pagespeed/kernel/http/http_names.h"
#include "pagespeed/kernel/http/response_headers.h"
#include "pagespeed/kernel/thread/scheduler.h"
namespace net_instaweb {
const char SimulatedDelayFetcher::kPayload[] = "A very complex webpage";
// SimulatedDelayFetcher lets one configure various per-host delays, and will
// make hardcoded replies according to those replies. This exists to help run
// simulations of server behavior with sites of widely various speeds.
SimulatedDelayFetcher::SimulatedDelayFetcher(
ThreadSystem* thread_system,
Timer* timer,
Scheduler* scheduler,
MessageHandler* handler,
FileSystem* file_system,
StringPiece delay_map_path,
StringPiece request_log_path,
int request_log_flush_frequency)
: timer_(timer),
scheduler_(scheduler),
message_handler_(handler),
file_system_(file_system),
request_log_flush_frequency_(request_log_flush_frequency),
mutex_(thread_system->NewMutex()),
request_log_outstanding_(0),
request_log_(
file_system_->OpenOutputFile(request_log_path.as_string().c_str(),
message_handler_)) {
ParseDelayMap(delay_map_path);
}
SimulatedDelayFetcher::~SimulatedDelayFetcher() {
file_system_->Close(request_log_, message_handler_);
}
void SimulatedDelayFetcher::Fetch(const GoogleString& url,
MessageHandler* message_handler,
AsyncFetch* fetch) {
fetch = EnableInflation(fetch);
GoogleUrl gurl(url);
if (!gurl.IsWebValid()) {
fetch->Done(false);
return;
}
GoogleString host = gurl.Host().as_string();
DelayMap::iterator delay = delays_ms_.find(host);
if (delay == delays_ms_.end()) {
message_handler_->Message(
kWarning, "Host not in delay map:%s", host.c_str());
fetch->Done(false);
} else {
int64 now_ms = timer_->NowMs();
// Log the request.
GoogleString timestamp;
ConvertTimeToString(now_ms, &timestamp);
GoogleString log_msg = StrCat(timestamp, " ", url, "\n");
{
ScopedMutex lock(mutex_.get());
request_log_->Write(log_msg, message_handler_);
++request_log_outstanding_;
if (request_log_outstanding_ >= request_log_flush_frequency_) {
request_log_->Flush(message_handler_);
request_log_outstanding_ = 0;
}
}
// delay->second is in milliseconds. It's 'second' as in
// the thing after first, not the unit of time.
int64 delay_ms = delay->second;
scheduler_->AddAlarmAtUs(
(now_ms + delay_ms) * Timer::kMsUs,
MakeFunction(this, &SimulatedDelayFetcher::ProduceReply, fetch));
}
}
void SimulatedDelayFetcher::ProduceReply(AsyncFetch* fetch) {
fetch->response_headers()->SetStatusAndReason(HttpStatus::kOK);
fetch->response_headers()->SetDateAndCaching(timer_->NowMs(),
0 /* uncacheable */);
fetch->response_headers()->Add(HttpAttributes::kContentType, "text/html");
fetch->Write(kPayload, message_handler_);
fetch->Done(true);
}
void SimulatedDelayFetcher::ParseDelayMap(StringPiece delay_map_path) {
GoogleString contents;
if (!file_system_->ReadFile(delay_map_path.as_string().c_str(),
&contents,
message_handler_)) {
return;
}
// We expect something like host1=123;host2=345; with whitespace between
// tokens ignored.
StringPieceVector pairs;
SplitStringUsingSubstr(contents, ";", &pairs);
for (int i = 0, n = pairs.size(); i < n; ++i) {
TrimWhitespace(&pairs[i]);
if (pairs[i].empty()) {
continue;
}
StringPieceVector host_delay;
SplitStringUsingSubstr(pairs[i], "=", &host_delay);
if (host_delay.size() != 2) {
message_handler_->Message(
kWarning, "Unable to parse host=delay spec:%s",
pairs[i].as_string().c_str());
continue;
}
TrimWhitespace(&host_delay[0]);
TrimWhitespace(&host_delay[1]);
int delay_ms = 0;
if (!StringToInt(host_delay[1], &delay_ms)) {
message_handler_->Message(
kWarning, "Unable to parse delay spec:%s",
host_delay[1].as_string().c_str());
continue;
}
delays_ms_[host_delay[0].as_string()] = delay_ms;
}
}
} // namespace net_instaweb