"Fossies" - the Fresh Open Source Software Archive

Member "incubator-pagespeed-mod-1.14.36.1/net/instaweb/rewriter/add_instrumentation_filter.cc" (28 Feb 2020, 9247 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.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 "base/logging.h"
   24 #include "net/instaweb/http/public/request_context.h"
   25 #include "net/instaweb/http/public/request_timing_info.h"
   26 #include "net/instaweb/rewriter/public/experiment_util.h"
   27 #include "net/instaweb/rewriter/public/request_properties.h"
   28 #include "net/instaweb/rewriter/public/rewrite_driver.h"
   29 #include "net/instaweb/rewriter/public/rewrite_options.h"
   30 #include "net/instaweb/rewriter/public/server_context.h"
   31 #include "net/instaweb/rewriter/public/static_asset_manager.h"
   32 #include "pagespeed/kernel/base/escaping.h"
   33 #include "pagespeed/kernel/base/ref_counted_ptr.h"
   34 #include "pagespeed/kernel/base/statistics.h"
   35 #include "pagespeed/kernel/base/string.h"
   36 #include "pagespeed/kernel/base/string_util.h"
   37 #include "pagespeed/kernel/html/html_element.h"
   38 #include "pagespeed/kernel/html/html_name.h"
   39 #include "pagespeed/kernel/html/html_node.h"
   40 #include "pagespeed/kernel/http/google_url.h"
   41 #include "pagespeed/kernel/http/http_names.h"
   42 #include "pagespeed/kernel/http/response_headers.h"
   43 
   44 namespace net_instaweb {
   45 
   46 namespace {
   47 
   48 // The javascript tag to insert in the top of the <head> element.  We want this
   49 // as early as possible in the html.  It must be short and fast.
   50 const char kHeadScriptPedantic[] =
   51     "<script type='text/javascript'>"
   52     "window.mod_pagespeed_start = Number(new Date());"
   53     "</script>";
   54 
   55 // script tag without type attribute
   56 const char kHeadScriptNonPedantic[] =
   57     "<script>"
   58     "window.mod_pagespeed_start = Number(new Date());"
   59     "</script>";
   60 
   61 }  // namespace
   62 
   63 // Timing tag for total page load time.  Also embedded in kTailScriptFormat
   64 // above via the second %s.
   65 // TODO(jud): These values would be better set to "load" and "beforeunload".
   66 const char AddInstrumentationFilter::kLoadTag[] = "load:";
   67 const char AddInstrumentationFilter::kUnloadTag[] = "unload:";
   68 
   69 // Counters.
   70 const char AddInstrumentationFilter::kInstrumentationScriptAddedCount[] =
   71     "instrumentation_filter_script_added_count";
   72 AddInstrumentationFilter::AddInstrumentationFilter(RewriteDriver* driver)
   73     : CommonFilter(driver),
   74       found_head_(false),
   75       added_head_script_(false),
   76       added_unload_script_(false) {
   77   Statistics* stats = driver->server_context()->statistics();
   78   instrumentation_script_added_count_ = stats->GetVariable(
   79       kInstrumentationScriptAddedCount);
   80 }
   81 
   82 AddInstrumentationFilter::~AddInstrumentationFilter() {}
   83 
   84 void AddInstrumentationFilter::InitStats(Statistics* statistics) {
   85   statistics->AddVariable(kInstrumentationScriptAddedCount);
   86 }
   87 
   88 void AddInstrumentationFilter::StartDocumentImpl() {
   89   found_head_ = false;
   90   added_head_script_ = false;
   91   added_unload_script_ = false;
   92 }
   93 
   94 void AddInstrumentationFilter::AddHeadScript(HtmlElement* element) {
   95   // IE doesn't like tags other than title or meta at the start of the
   96   // head. The MSDN page says:
   97   //   The X-UA-Compatible header isn't case sensitive; however, it must appear
   98   //   in the header of the webpage (the HEAD section) before all other elements
   99   //   except for the title element and other meta elements.
  100   // Reference: http://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx
  101   if (element->keyword() != HtmlName::kTitle &&
  102       element->keyword() != HtmlName::kMeta) {
  103     added_head_script_ = true;
  104     // TODO(abliss): add an actual element instead, so other filters can
  105     // rewrite this JS
  106     HtmlCharactersNode* script = nullptr;
  107     if (driver()->options()->Enabled(RewriteOptions::kPedantic)) {
  108         script = driver()->NewCharactersNode(nullptr, kHeadScriptPedantic);
  109     } else {
  110         script = driver()->NewCharactersNode(nullptr, kHeadScriptNonPedantic);
  111     }
  112     driver()->InsertNodeBeforeCurrent(script);
  113     instrumentation_script_added_count_->Add(1);
  114   }
  115 }
  116 
  117 void AddInstrumentationFilter::StartElementImpl(HtmlElement* element) {
  118   if (found_head_ && !added_head_script_) {
  119     AddHeadScript(element);
  120   }
  121   if (!found_head_ && element->keyword() == HtmlName::kHead) {
  122     found_head_ = true;
  123   }
  124 }
  125 
  126 void AddInstrumentationFilter::EndElementImpl(HtmlElement* element) {
  127   if (found_head_ && element->keyword() == HtmlName::kHead) {
  128     if (!added_head_script_) {
  129       AddHeadScript(element);
  130     }
  131     if (driver()->options()->report_unload_time() &&
  132         !added_unload_script_) {
  133       GoogleString js = GetScriptJs(kUnloadTag);
  134       HtmlElement* script = driver()->NewElement(element, HtmlName::kScript);
  135       if (!driver()->defer_instrumentation_script()) {
  136         driver()->AddAttribute(script, HtmlName::kDataPagespeedNoDefer,
  137                                StringPiece());
  138       }
  139       driver()->InsertNodeBeforeCurrent(script);
  140       AddJsToElement(js, script);
  141       added_unload_script_ = true;
  142     }
  143   }
  144 }
  145 
  146 void AddInstrumentationFilter::EndDocument() {
  147   // We relied on the existence of a <head> element.  This should have been
  148   // assured by add_head_filter.
  149   if (!found_head_) {
  150     LOG(WARNING) << "No <head> found for URL " << driver()->url();
  151     return;
  152   }
  153   GoogleString js = GetScriptJs(kLoadTag);
  154   HtmlElement* script = driver()->NewElement(nullptr, HtmlName::kScript);
  155   if (!driver()->defer_instrumentation_script()) {
  156     driver()->AddAttribute(script, HtmlName::kDataPagespeedNoDefer,
  157                            StringPiece());
  158   }
  159   InsertNodeAtBodyEnd(script);
  160   AddJsToElement(js, script);
  161 }
  162 
  163 GoogleString AddInstrumentationFilter::GetScriptJs(StringPiece event) {
  164   GoogleString js;
  165   StaticAssetManager* static_asset_manager =
  166       driver()->server_context()->static_asset_manager();
  167   // Only add the static JS once.
  168   if (!added_unload_script_) {
  169     if (driver()->options()->enable_extended_instrumentation()) {
  170       js = static_asset_manager->GetAsset(
  171           StaticAssetEnum::EXTENDED_INSTRUMENTATION_JS, driver()->options());
  172     }
  173     StrAppend(&js, static_asset_manager->GetAsset(
  174         StaticAssetEnum::ADD_INSTRUMENTATION_JS, driver()->options()));
  175   }
  176 
  177   GoogleString js_event = (event == kLoadTag) ? "load" : "beforeunload";
  178 
  179   const RewriteOptions::BeaconUrl& beacons = driver()->options()->beacon_url();
  180   const GoogleString* beacon_url =
  181       driver()->IsHttps() ? &beacons.https : &beacons.http;
  182   GoogleString extra_params;
  183   if (driver()->options()->running_experiment()) {
  184     int experiment_state = driver()->options()->experiment_id();
  185     if (experiment_state != experiment::kExperimentNotSet &&
  186         experiment_state != experiment::kNoExperiment) {
  187       StrAppend(&extra_params, "&exptid=",
  188                 IntegerToString(driver()->options()->experiment_id()));
  189     }
  190   }
  191 
  192   const RequestTimingInfo& timing_info =
  193       driver()->request_context()->timing_info();
  194   int64 header_fetch_ms;
  195   if (timing_info.GetFetchHeaderLatencyMs(&header_fetch_ms)) {
  196     // If time taken to fetch the http header is not set, then the response
  197     // came from cache.
  198     StrAppend(&extra_params, "&hft=", Integer64ToString(header_fetch_ms));
  199   }
  200   int64 fetch_ms;
  201   if (timing_info.GetFetchLatencyMs(&fetch_ms)) {
  202     // If time taken to fetch the resource is not set, then the response
  203     // came from cache.
  204     StrAppend(&extra_params, "&ft=", Integer64ToString(fetch_ms));
  205   }
  206   int64 ttfb_ms;
  207   if (timing_info.GetTimeToFirstByte(&ttfb_ms)) {
  208     StrAppend(&extra_params, "&s_ttfb=", Integer64ToString(ttfb_ms));
  209   }
  210 
  211   // Append the http response code.
  212   if (driver()->response_headers() != nullptr &&
  213       driver()->response_headers()->status_code() > 0 &&
  214       driver()->response_headers()->status_code() != HttpStatus::kOK) {
  215     StrAppend(&extra_params, "&rc=", IntegerToString(
  216         driver()->response_headers()->status_code()));
  217   }
  218   // Append the request id.
  219   if (driver()->request_context()->request_id() > 0) {
  220     StrAppend(&extra_params, "&id=", Integer64ToString(
  221         driver()->request_context()->request_id()));
  222   }
  223 
  224   GoogleString html_url;
  225   EscapeToJsStringLiteral(driver()->google_url().Spec(),
  226                           false, /* no quotes */
  227                           &html_url);
  228 
  229   StrAppend(&js, "\npagespeed.addInstrumentationInit(");
  230   StrAppend(&js, "'", *beacon_url, "', ");
  231   StrAppend(&js, "'", js_event, "', ");
  232   StrAppend(&js, "'", extra_params, "', ");
  233   StrAppend(&js, "'", html_url, "');");
  234 
  235   return js;
  236 }
  237 
  238 void AddInstrumentationFilter::DetermineEnabled(GoogleString* disabled_reason) {
  239   set_is_enabled(!driver()->request_properties()->IsBot());
  240 }
  241 
  242 }  // namespace net_instaweb