blob: 18890542f36972e153ad7355eac28089983ecce8 [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.
*/
// Author: jud@google.com (Jud Porter)
#include "net/instaweb/rewriter/public/split_html_beacon_filter.h"
#include <algorithm>
#include "net/instaweb/http/public/request_context.h"
#include "net/instaweb/rewriter/public/beacon_critical_line_info_finder.h"
#include "net/instaweb/rewriter/public/critical_line_info_finder.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/rewrite_test_base.h"
#include "net/instaweb/rewriter/public/server_context.h"
#include "net/instaweb/rewriter/public/static_asset_manager.h"
#include "net/instaweb/rewriter/public/test_rewrite_driver_factory.h"
#include "net/instaweb/util/public/mock_property_page.h"
#include "net/instaweb/util/public/property_cache.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/mock_timer.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/timer.h"
#include "pagespeed/kernel/html/html_parse_test_base.h"
#include "pagespeed/kernel/http/user_agent_matcher_test_base.h"
namespace net_instaweb {
class SplitHtmlBeaconFilterTest : public RewriteTestBase {
protected:
SplitHtmlBeaconFilterTest() {}
virtual ~SplitHtmlBeaconFilterTest() {}
virtual void SetUp() {
RewriteTestBase::SetUp();
SplitHtmlBeaconFilter::InitStats(statistics());
// Enabling the filter and options that will turn on beacon injection.
factory()->set_use_beacon_results_in_filters(true);
// Set up pcache for page.
const PropertyCache::Cohort* cohort =
SetupCohort(page_property_cache(), RewriteDriver::kBeaconCohort);
server_context()->set_beacon_cohort(cohort);
// Set up and register a beacon finder now that the pcache is configured.
CriticalLineInfoFinder* finder = new BeaconCriticalLineInfoFinder(
server_context()->beacon_cohort(), factory()->nonce_generator());
server_context()->set_critical_line_info_finder(finder);
options()->EnableFilter(RewriteOptions::kSplitHtml);
rewrite_driver()->AddFilters();
ResetDriver();
}
void ResetDriver() {
rewrite_driver()->Clear();
SetCurrentUserAgent(
UserAgentMatcherTestBase::kChrome18UserAgent);
SetHtmlMimetype(); // Don't wrap scripts in <![CDATA[ ]]>
rewrite_driver()->set_request_context(
RequestContext::NewTestRequestContext(factory()->thread_system()));
MockPropertyPage* page = NewMockPage(kTestDomain);
rewrite_driver()->set_property_page(page);
PropertyCache* pcache = server_context_->page_property_cache();
pcache->Read(page);
}
void ValidateBeacon() {
ParseUrl(kTestDomain, "<head></head><body></body>");
EXPECT_NE(GoogleString::npos, output_buffer_.find(BeaconScript()));
// split_html will be disabled when we beacon, so the noscript redirect that
// it enables won't be inserted.
EXPECT_EQ(GoogleString::npos, output_buffer_.find("noscript"));
}
void ValidateNoBeacon() {
ParseUrl(kTestDomain, "<head></head><body></body>");
EXPECT_EQ(GoogleString::npos,
output_buffer_.find("pagespeed.splitHtmlBeaconInit"));
// If we don't beacon, then a noscript tag will be inserted because
// the split_html filter should be enabled.
EXPECT_NE(GoogleString::npos, output_buffer_.find("noscript"));
}
void WriteToPropertyCache() {
rewrite_driver()->property_page()->WriteCohort(
rewrite_driver()
->server_context()
->critical_line_info_finder()
->cohort());
}
// Don't call this if a beacon isn't also being generated by rewriting (for
// example, if you wanted to check that a beacon isn't being generated)
// because then ExpectedNonce() will get out of sync.
GoogleString BeaconScript() {
GoogleString script =
StrCat("<script type=\"text/javascript\" data-pagespeed-no-defer>",
server_context()->static_asset_manager()->GetAsset(
StaticAssetEnum::SPLIT_HTML_BEACON_JS, options()));
StrAppend(&script, "\npagespeed.splitHtmlBeaconInit('",
options()->beacon_url().http, "', '", kTestDomain, "', '0', '",
ExpectedNonce(), "');");
StrAppend(&script, "</script>");
return script;
}
};
TEST_F(SplitHtmlBeaconFilterTest, ScriptInjection) {
ValidateBeacon();
}
TEST_F(SplitHtmlBeaconFilterTest, DontRebeaconBeforeTimeout) {
ValidateBeacon();
WriteToPropertyCache();
ResetDriver();
ValidateNoBeacon();
int64 expiration_time_ms =
std::min(options()->finder_properties_cache_expiration_time_ms(),
options()->beacon_reinstrument_time_sec() * Timer::kSecondMs);
factory()->mock_timer()->AdvanceMs(expiration_time_ms / 2);
ResetDriver();
ValidateNoBeacon();
// Beacon injection happens when the pcache value expires.
factory()->mock_timer()->AdvanceMs(expiration_time_ms / 2 + 1);
ResetDriver();
ValidateBeacon();
}
TEST_F(SplitHtmlBeaconFilterTest, DisabledForBots) {
SetCurrentUserAgent(UserAgentMatcherTestBase::kGooglebotUserAgent);
ValidateNoBeacon();
}
} // namespace net_instaweb