"Fossies" - the Fresh Open Source Software Archive

Member "incubator-pagespeed-mod-1.14.36.1/net/instaweb/rewriter/add_instrumentation_filter_test.cc" (28 Feb 2020, 13246 Bytes) of package /linux/www/apache_httpd_modules/incubator-pagespeed-mod-1.14.36.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "add_instrumentation_filter_test.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.13.35.2_vs_1.14.36.1.

    1 /*
    2  * Licensed to the Apache Software Foundation (ASF) under one
    3  * or more contributor license agreements.  See the NOTICE file
    4  * distributed with this work for additional information
    5  * regarding copyright ownership.  The ASF licenses this file
    6  * to you under the Apache License, Version 2.0 (the
    7  * "License"); you may not use this file except in compliance
    8  * with the License.  You may obtain a copy of the License at
    9  * 
   10  *   http://www.apache.org/licenses/LICENSE-2.0
   11  * 
   12  * Unless required by applicable law or agreed to in writing,
   13  * software distributed under the License is distributed on an
   14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15  * KIND, either express or implied.  See the License for the
   16  * specific language governing permissions and limitations
   17  * under the License.
   18  */
   19 
   20 
   21 #include "net/instaweb/rewriter/public/add_instrumentation_filter.h"
   22 
   23 #include "net/instaweb/http/public/request_context.h"
   24 #include "net/instaweb/http/public/request_timing_info.h"
   25 #include "net/instaweb/rewriter/public/rewrite_driver.h"
   26 #include "net/instaweb/rewriter/public/rewrite_options.h"
   27 #include "net/instaweb/rewriter/public/rewrite_test_base.h"
   28 #include "net/instaweb/rewriter/public/server_context.h"
   29 #include "pagespeed/kernel/base/escaping.h"
   30 #include "pagespeed/kernel/base/gtest.h"
   31 #include "pagespeed/kernel/base/null_message_handler.h"
   32 #include "pagespeed/kernel/base/ref_counted_ptr.h"
   33 #include "pagespeed/kernel/base/statistics.h"
   34 #include "pagespeed/kernel/html/amp_document_filter.h"
   35 #include "pagespeed/kernel/html/html_keywords.h"
   36 #include "pagespeed/kernel/html/html_name.h"
   37 #include "pagespeed/kernel/html/html_parse_test_base.h"
   38 #include "pagespeed/kernel/http/google_url.h"
   39 #include "pagespeed/kernel/http/http_names.h"
   40 #include "pagespeed/kernel/http/response_headers.h"
   41 #include "pagespeed/kernel/http/user_agent_matcher_test_base.h"
   42 
   43 namespace net_instaweb {
   44 
   45 class AddInstrumentationFilterTest : public RewriteTestBase {
   46  protected:
   47   AddInstrumentationFilterTest() {}
   48 
   49   virtual void SetUp() {
   50     options()->set_beacon_url("http://example.com/beacon?org=xxx");
   51     AddInstrumentationFilter::InitStats(statistics());
   52     options()->EnableFilter(RewriteOptions::kAddInstrumentation);
   53     RewriteTestBase::SetUp();
   54     report_unload_time_ = false;
   55     xhtml_mode_ = false;
   56     cdata_mode_ = false;
   57     https_mode_ = false;
   58   }
   59 
   60   virtual bool AddBody() const { return false; }
   61 
   62   void AddFilters() {
   63     AddFiltersWithUserAgent(UserAgentMatcherTestBase::kChrome18UserAgent);
   64   }
   65 
   66   void AddFiltersWithUserAgent(StringPiece user_agent) {
   67     SetCurrentUserAgent(user_agent);
   68     SetDriverRequestHeaders();
   69     rewrite_driver()->AddFilters();
   70   }
   71 
   72   void RunInjection() {
   73     options()->set_report_unload_time(report_unload_time_);
   74     AddFilters();
   75     ParseUrl(GetTestUrl(),
   76              "<head></head><head></head><body></body><body></body>");
   77     EXPECT_EQ(1, statistics()->GetVariable(
   78         AddInstrumentationFilter::kInstrumentationScriptAddedCount)->Get());
   79   }
   80 
   81   void SetMimetypeToXhtml() {
   82     SetXhtmlMimetype();
   83     xhtml_mode_ = !cdata_mode_;
   84   }
   85 
   86   void DoNotRelyOnContentType() {
   87     cdata_mode_ = true;
   88     server_context()->set_response_headers_finalized(false);
   89   }
   90 
   91   void AssumeHttps() {
   92     https_mode_ = true;
   93   }
   94 
   95   GoogleString GetTestUrl() {
   96     return StrCat((https_mode_ ? "https://example.com/" : kTestDomain),
   97                   "index.html?a&b");
   98   }
   99 
  100   GoogleString CreateInitString(StringPiece beacon_url,
  101                                 StringPiece event,
  102                                 StringPiece extra_params) {
  103     GoogleString url;
  104     EscapeToJsStringLiteral(rewrite_driver()->google_url().Spec(), false, &url);
  105     GoogleString str = "pagespeed.addInstrumentationInit(";
  106     StrAppend(&str, "'", beacon_url, "', ");
  107     StrAppend(&str, "'", event, "', ");
  108     StrAppend(&str, "'", extra_params, "', ");
  109     StrAppend(&str, "'", url, "');");
  110     return str;
  111   }
  112 
  113   bool report_unload_time_;
  114   bool xhtml_mode_;
  115   bool cdata_mode_;
  116   bool https_mode_;
  117   ResponseHeaders response_headers_;
  118 };
  119 
  120 TEST_F(AddInstrumentationFilterTest, ScriptInjection) {
  121   RunInjection();
  122   EXPECT_TRUE(output_buffer_.find(
  123       CreateInitString(
  124           options()->beacon_url().http, "load", "")) !=
  125               GoogleString::npos);
  126 }
  127 
  128 TEST_F(AddInstrumentationFilterTest, ScriptInjectionWithNavigation) {
  129   report_unload_time_ = true;
  130   RunInjection();
  131   EXPECT_TRUE(output_buffer_.find(
  132       CreateInitString(
  133           options()->beacon_url().http, "beforeunload", "")) !=
  134               GoogleString::npos);
  135 }
  136 
  137 // Test an https fetch.
  138 TEST_F(AddInstrumentationFilterTest, TestScriptInjectionWithHttps) {
  139   AssumeHttps();
  140   RunInjection();
  141   EXPECT_TRUE(output_buffer_.find(
  142       CreateInitString(
  143           options()->beacon_url().https, "load", "")) !=
  144               GoogleString::npos);
  145 }
  146 
  147 // Test an https fetch, reporting unload and using Xhtml
  148 TEST_F(AddInstrumentationFilterTest,
  149        TestScriptInjectionWithHttpsUnloadAndXhtml) {
  150   SetMimetypeToXhtml();
  151   AssumeHttps();
  152   report_unload_time_ = true;
  153   RunInjection();
  154   EXPECT_TRUE(output_buffer_.find(
  155       CreateInitString(
  156           options()->beacon_url().https, "beforeunload", "")) !=
  157               GoogleString::npos);
  158 }
  159 
  160 // Test that experiment id reporting is done correctly.
  161 TEST_F(AddInstrumentationFilterTest, TestExperimentIdReporting) {
  162   NullMessageHandler handler;
  163   options()->set_running_experiment(true);
  164   options()->AddExperimentSpec("id=2;percent=10;slot=4;", &handler);
  165   options()->AddExperimentSpec("id=7;percent=10;level=CoreFilters;slot=4;",
  166                                &handler);
  167   options()->SetExperimentState(2);
  168   RunInjection();
  169   EXPECT_TRUE(output_buffer_.find(
  170       CreateInitString(
  171           options()->beacon_url().http, "load", "&exptid=2")) !=
  172               GoogleString::npos);
  173 }
  174 
  175 // Test that extended instrumentation is injected properly.
  176 TEST_F(AddInstrumentationFilterTest, TestExtendedInstrumentation) {
  177   options()->set_enable_extended_instrumentation(true);
  178   RunInjection();
  179   EXPECT_TRUE(output_buffer_.find(
  180       CreateInitString(
  181           options()->beacon_url().http, "load", "")) !=
  182               GoogleString::npos);
  183   EXPECT_TRUE(output_buffer_.find("getResourceTimingData=function()") !=
  184               GoogleString::npos);
  185 }
  186 
  187 // Test that headers fetch timing reporting is done correctly.
  188 TEST_F(AddInstrumentationFilterTest, TestHeadersFetchTimingReporting) {
  189   RequestTimingInfo* timing_info = mutable_timing_info();
  190   timing_info->FetchStarted();
  191   AdvanceTimeMs(200);
  192   timing_info->FetchHeaderReceived();
  193   AdvanceTimeMs(100);
  194   timing_info->FirstByteReturned();
  195   AdvanceTimeMs(200);
  196   timing_info->FetchFinished();
  197   RunInjection();
  198   EXPECT_TRUE(output_buffer_.find(
  199       CreateInitString(
  200           options()->beacon_url().http, "load", "&hft=200&ft=500&s_ttfb=300"))
  201               != GoogleString::npos) << output_buffer_;
  202 }
  203 
  204 TEST_F(AddInstrumentationFilterTest, Quoting) {
  205   AddFilters();
  206   GoogleString url = "http://example.com/?');alert('foo)";
  207   ParseUrl(url, "<head></head><body></body>");
  208   EXPECT_EQ(GoogleString::npos,
  209             output_buffer_.find("?');alert('foo)"));
  210 }
  211 
  212 // Test that head script is inserted after title and meta tags.
  213 TEST_F(AddInstrumentationFilterTest, TestScriptAfterTitleAndMeta) {
  214   AddFilters();
  215   ParseUrl(GetTestUrl(),
  216            "<head><meta name='abc' /><title></title></head><body></body>");
  217   EXPECT_TRUE(output_buffer_.find(
  218       "<head><meta name='abc' /><title></title><script"));
  219 }
  220 
  221 TEST_F(AddInstrumentationFilterTest, TestNon200Response) {
  222   AddFilters();
  223   response_headers_.set_status_code(HttpStatus::kForbidden);
  224   rewrite_driver()->set_response_headers_ptr(&response_headers_);
  225   ParseUrl(GetTestUrl(),
  226            "<head></head><head></head><body></body><body></body>");
  227   EXPECT_EQ(1, statistics()->GetVariable(
  228       AddInstrumentationFilter::kInstrumentationScriptAddedCount)->Get());
  229   EXPECT_TRUE(output_buffer_.find(
  230       CreateInitString(
  231           options()->beacon_url().http, "load", "&rc=403")) !=
  232               GoogleString::npos);
  233 }
  234 
  235 TEST_F(AddInstrumentationFilterTest, TestRequestId) {
  236   rewrite_driver()->request_context()->set_request_id(1234567890L);
  237   RunInjection();
  238   EXPECT_TRUE(output_buffer_.find(
  239       CreateInitString(options()->beacon_url().http, "load",
  240                        "&id=1234567890")) != GoogleString::npos);
  241 }
  242 
  243 TEST_F(AddInstrumentationFilterTest, TestNoDeferInstrumentationScript) {
  244   RunInjection();
  245   EXPECT_TRUE(output_buffer_.find(
  246       CreateInitString(
  247           options()->beacon_url().http, "load", "")) !=
  248               GoogleString::npos);
  249   const StringPiece* nodefer =
  250       HtmlKeywords::KeywordToString(HtmlName::kDataPagespeedNoDefer);
  251   EXPECT_TRUE(output_buffer_.find(nodefer->as_string()) != GoogleString::npos);
  252 }
  253 
  254 TEST_F(AddInstrumentationFilterTest, TestDeferInstrumentationScript) {
  255   rewrite_driver()->set_defer_instrumentation_script(true);
  256   RunInjection();
  257   EXPECT_TRUE(output_buffer_.find(
  258       CreateInitString(
  259           options()->beacon_url().http, "load", "")) !=
  260               GoogleString::npos);
  261   const StringPiece* nodefer =
  262       HtmlKeywords::KeywordToString(HtmlName::kDataPagespeedNoDefer);
  263   EXPECT_TRUE(output_buffer_.find(nodefer->as_string()) == GoogleString::npos);
  264 }
  265 
  266 TEST_F(AddInstrumentationFilterTest, TestDisableForBots) {
  267   AddFiltersWithUserAgent(UserAgentMatcherTestBase::kGooglebotUserAgent);
  268   ValidateNoChanges(GetTestUrl(),
  269                     "<head></head><head></head><body></body><body></body>");
  270 }
  271 
  272 // Test script tag and type attribute without pedantic filter
  273 TEST_F(AddInstrumentationFilterTest, TestScriptTagTypeAttribute) {
  274   options()->EnableFilter(RewriteOptions::kAddInstrumentation);
  275   AddFilters();
  276 
  277   SetupWriter();
  278   rewrite_driver()->StartParse(kTestDomain);
  279   rewrite_driver()->ParseText("<!DOCTYPE html><html><head></head><body>"
  280                               "<img src='Puzzle.jpg'/></body></html>");
  281   rewrite_driver()->FinishParse();
  282 
  283   // check html without type attribute in head
  284   EXPECT_TRUE(output_buffer_.find(
  285       "<script>window.mod_pagespeed_start") !=
  286           StringPiece::npos);
  287 
  288   // check html without type attribute in data-pagespeed-no-defer tag
  289   EXPECT_TRUE(output_buffer_.find(
  290       "<script data-pagespeed-no-defer>") !=
  291           StringPiece::npos);
  292 }
  293 
  294 // Test script tag and type attribute with pedantic filter
  295 TEST_F(AddInstrumentationFilterTest, TestScriptTagTypeAttributePedantic) {
  296   options()->EnableFilter(RewriteOptions::kAddInstrumentation);
  297   options()->EnableFilter(RewriteOptions::kPedantic);
  298   AddFilters();
  299 
  300   SetupWriter();
  301   rewrite_driver()->StartParse(kTestDomain);
  302   rewrite_driver()->ParseText("<!DOCTYPE html><html><head></head><body>"
  303                               "<img src='Puzzle.jpg'/></body></html>");
  304   rewrite_driver()->FinishParse();
  305 
  306   // check html with type attribute in head
  307   EXPECT_TRUE(output_buffer_.find(
  308       "<script type='text/javascript'>window.mod_pagespeed_start") !=
  309           StringPiece::npos);
  310 
  311   // check html with type attribute in data-pagespeed-no-defer tag
  312   EXPECT_TRUE(output_buffer_.find(
  313       "<script data-pagespeed-no-defer type=\"text/javascript\">")!=
  314           StringPiece::npos);
  315 }
  316 
  317 const char kBeaconUrl[] = "http://example.com/beacon?org=xxx";
  318 
  319 class AddInstrumentationAmpTest : public AddInstrumentationFilterTest {
  320  protected:
  321   void SetUp() override {
  322     AddInstrumentationFilterTest::SetUp();
  323     SetCurrentUserAgent(UserAgentMatcherTestBase::kIPhone4Safari);
  324     options()->EnableFilter(RewriteOptions::kAddInstrumentation);
  325     options()->set_beacon_url(kBeaconUrl);
  326     options()->set_report_unload_time(true);
  327     AddFilters();
  328   }
  329 
  330   void CheckInstrumentation(StringPiece html, bool expect_has_beacon) {
  331     for (int i = 0, n = html.size(); i < n; ++i) {
  332       if (rewrite_driver_->request_headers() == nullptr) {
  333         SetDriverRequestHeaders();
  334       }
  335       SetupWriter();
  336       rewrite_driver_->StartParse(
  337           StringPrintf("http://example.com/amp_doc_%d.html", i));
  338       rewrite_driver_->ParseText(html.substr(0, i));
  339       rewrite_driver_->Flush();
  340       rewrite_driver_->ParseText(html.substr(i));
  341       rewrite_driver_->FinishParse();
  342       EXPECT_EQ(expect_has_beacon,
  343                 output_buffer_.find(kBeaconUrl) != GoogleString::npos);
  344     }
  345   }
  346 
  347   bool AddBody() const override { return false; }
  348   bool AddHtmlTags() const override { return false; }
  349 };
  350 
  351 TEST_F(AddInstrumentationAmpTest, IsAmpHtml) {
  352   CheckInstrumentation("<!doctype foo>  <html amp><head/><body></body></html>",
  353                        false);
  354   EXPECT_TRUE(rewrite_driver_->is_amp_document());
  355 }
  356 
  357 TEST_F(AddInstrumentationAmpTest, IsAmpLightningBolt) {
  358   CheckInstrumentation(StrCat("<!doctype foo>  <html ",
  359                               AmpDocumentFilter::kUtf8LightningBolt,
  360                               "><head/><body></body></html>"),
  361                        false);
  362   EXPECT_TRUE(rewrite_driver_->is_amp_document());
  363 }
  364 
  365 TEST_F(AddInstrumentationAmpTest, IsNotAmp) {
  366   CheckInstrumentation("<!doctype foo>  <html><head/><body></body></html>",
  367                        true);
  368   EXPECT_FALSE(rewrite_driver_->is_amp_document());
  369 }
  370 
  371 }  // namespace net_instaweb