| /* |
| * 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 |