blob: 4488a3f77e79869ef2faf09ae568a81e44285b63 [file] [log] [blame]
/*
* Copyright 2010 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: jmarantz@google.com (Joshua Marantz)
#ifndef NET_INSTAWEB_REWRITER_PUBLIC_REWRITE_DRIVER_H_
#define NET_INSTAWEB_REWRITER_PUBLIC_REWRITE_DRIVER_H_
#include <map>
#include <set>
#include <vector>
#include "base/logging.h"
#include "net/instaweb/http/public/cache_url_async_fetcher.h"
#include "net/instaweb/http/public/http_cache.h"
#include "net/instaweb/http/public/request_context.h"
#include "net/instaweb/http/public/url_async_fetcher.h"
#include "net/instaweb/rewriter/cached_result.pb.h"
#include "net/instaweb/rewriter/critical_keys.pb.h"
#include "net/instaweb/rewriter/public/critical_images_finder.h"
#include "net/instaweb/rewriter/public/critical_selector_finder.h"
#include "net/instaweb/rewriter/public/downstream_cache_purger.h"
#include "net/instaweb/rewriter/public/inline_attribute_slot.h"
#include "net/instaweb/rewriter/public/inline_resource_slot.h"
#include "net/instaweb/rewriter/public/output_resource.h"
#include "net/instaweb/rewriter/public/output_resource_kind.h"
#include "net/instaweb/rewriter/public/resource.h"
#include "net/instaweb/rewriter/public/resource_namer.h"
#include "net/instaweb/rewriter/public/resource_slot.h"
#include "net/instaweb/rewriter/public/rewrite_context.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/scan_filter.h"
#include "net/instaweb/rewriter/public/server_context.h"
#include "pagespeed/kernel/base/abstract_mutex.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/function.h"
#include "pagespeed/kernel/base/printf_format.h"
#include "pagespeed/kernel/base/proto_util.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/thread_annotations.h"
#include "pagespeed/kernel/base/thread_system.h"
#include "pagespeed/kernel/base/writer.h"
#include "pagespeed/kernel/html/html_element.h"
#include "pagespeed/kernel/html/html_filter.h"
#include "pagespeed/kernel/html/html_node.h"
#include "pagespeed/kernel/html/html_parse.h"
#include "pagespeed/kernel/http/content_type.h"
#include "pagespeed/kernel/http/google_url.h"
#include "pagespeed/kernel/http/request_headers.h"
#include "pagespeed/kernel/http/response_headers.h"
#include "pagespeed/kernel/http/user_agent_matcher.h"
#include "pagespeed/kernel/thread/queued_worker_pool.h"
#include "pagespeed/kernel/thread/scheduler.h"
#include "pagespeed/kernel/util/categorized_refcount.h"
#include "pagespeed/kernel/util/url_segment_encoder.h"
#include "pagespeed/opt/http/property_cache.h"
namespace net_instaweb {
class AbstractLogRecord;
class AsyncFetch;
class CriticalCssResult;
class CriticalLineInfo;
class DebugFilter;
class DomStatsFilter;
class DomainRewriteFilter;
class FallbackPropertyPage;
class FileSystem;
class FlushEarlyInfo;
class FlushEarlyRenderInfo;
class HtmlWriterFilter;
class MessageHandler;
class RequestProperties;
class RequestTrace;
class RewriteDriverPool;
class RewriteFilter;
class SplitHtmlConfig;
class Statistics;
class UrlLeftTrimFilter;
class UrlNamer;
// This extends class HtmlParse (which should renamed HtmlContext) by providing
// context for rewriting resources (css, js, images).
class RewriteDriver : public HtmlParse {
public:
// Status return-code for ResolveCssUrls.
enum CssResolutionStatus {
kWriteFailed,
kNoResolutionNeeded,
kSuccess
};
// Mode for BoundedWaitForCompletion
enum WaitMode {
kNoWait, // Used internally. Do not pass in.
kWaitForCompletion, // wait for everything to complete (up to deadline)
kWaitForCachedRender, // wait for at least cached rewrites to complete,
// and anything else that finishes within deadline.
kWaitForShutDown // Makes sure that all work, including any that's
// being done in background, finishes.
};
// Indicates document's mimetype as XHTML, HTML, or is not
// known/something else. Note that in Apache we might not know the
// correct mimetype because a downstream module might change it.
// It's not clear how likely this is, since mod_rewrite and mod_mime
// run upstream of mod_pagespeed. However if anyone sets mimetype
// via "Header Add", it would affect the Browser's view of the
// document's mimetype (which is what determines the parsing) but
// mod_pagespeed would not know.
//
// Note that we also have doctype().IsXhtml() but that indicates quirks-mode
// for CSS, and does not control how the parser parses the document.
enum XhtmlStatus {
kXhtmlUnknown,
kIsXhtml,
kIsNotXhtml
};
// See CreateInputResource.
enum InlineAuthorizationPolicy {
kInlineUnauthorizedResources,
kInlineOnlyAuthorizedResources
};
// See CreateInputResource.
enum IntendedFor {
kIntendedForInlining,
kIntendedForGeneral
};
// This string identifies, for the PropertyCache, a group of properties
// that are computed from the DOM, and thus can, if desired, be rewritten
// on every HTML request.
static const char kDomCohort[];
// The cohort for properties that are written by the beacon handler.
static const char kBeaconCohort[];
// Property Names in DomCohort.
// Tracks the timestamp when we last received a request for this url.
static const char kLastRequestTimestamp[];
// Tracks if we exceeded the maximum size limit of html which we should parse.
static const char kParseSizeLimitExceeded[];
// Flush Subresources Info associted with the HTML page.
static const char kSubresourcesPropertyName[];
// Status codes of previous responses.
static const char kStatusCodePropertyName[];
RewriteDriver(MessageHandler* message_handler,
FileSystem* file_system,
UrlAsyncFetcher* url_async_fetcher);
// Need explicit destructors to allow destruction of scoped_ptr-controlled
// instances without propagating the include files.
virtual ~RewriteDriver();
// Returns a fresh instance using the same options we do, using the same log
// record. Drivers should only be cloned within the same request.
//
// Clones share the same request_context, which contains bits derived from the
// request headers, so request_headers_ is also cloned (or shared if we make
// them shareable).
//
// You must call SetRequestHeaders before calling Clone.
RewriteDriver* Clone();
// Clears the current request cache of resources and base URL. The
// filter-chain is left intact so that a new request can be issued.
// Deletes all RewriteContexts.
//
// WaitForCompletion must be called prior to Clear().
void Clear();
// Initialize statistics for all filters that need it.
static void InitStats(Statistics* statistics);
// Initialize statics. Initialize/Terminate calls must be paired.
static void Initialize();
static void Terminate();
// Formats a "deadline exceeded" message for a given filter.
static GoogleString DeadlineExceededMessage(StringPiece filter_name);
// Sets a server context enabling the rewriting of
// resources. This will replace any previous server context.
void SetServerContext(ServerContext* server_context);
// Returns true if we may cache extend Css, Images, PDFs, or Scripts
// respectively.
bool MayCacheExtendCss() const;
bool MayCacheExtendImages() const;
bool MayCacheExtendPdfs() const;
bool MayCacheExtendScripts() const;
const GoogleString& user_agent() const { return user_agent_; }
const RequestProperties* request_properties() const {
return request_properties_.get();
}
// Reinitializes request_properties_, clearing any cached values.
void ClearRequestProperties();
// Returns true if the request we're rewriting was made using SPDY.
bool using_spdy() const { return request_context_->using_spdy(); }
bool write_property_cache_dom_cohort() const {
return write_property_cache_dom_cohort_;
}
void set_write_property_cache_dom_cohort(bool x) {
write_property_cache_dom_cohort_ = x;
}
RequestContextPtr request_context() { return request_context_; }
void set_request_context(const RequestContextPtr& x);
// Convenience method to return the trace context from the request_context()
// if both are configured and NULL otherwise.
RequestTrace* trace_context();
// Convenience methods to issue a trace annotation if tracing is enabled.
// If tracing is disabled, these methods are no-ops.
void TracePrintf(const char* fmt, ...);
void TraceLiteral(const char* literal);
void TraceString(const GoogleString& s);
// Return a mutable pointer to the response headers that filters can update
// before the first flush. Returns NULL after Flush has occurred.
ResponseHeaders* mutable_response_headers() {
return flush_occurred_ ? NULL : response_headers_;
}
// Returns a const version of the ResponseHeaders*, indepdendent of whether
// Flush has occurred. Note that ResponseHeaders* may still be NULL if
// no one has called set_response_headers_ptr.
//
// TODO(jmarantz): Change API to require response_headers in StartParse so
// we can guarantee this is non-null.
const ResponseHeaders* response_headers() {
return response_headers_;
}
// Set the pointer to the response headers that filters can update
// before the first flush. RewriteDriver does NOT take ownership
// of this memory.
void set_response_headers_ptr(ResponseHeaders* headers) {
response_headers_ = headers;
}
// Reinitializes request_headers_ (a scoped ptr) with a copy of the original
// request headers. Note that the fetches associated with the driver could
// be using a modified version of the original request headers.
// There MUST be exactly 1 call to this method after a rewrite driver object
// has been constructed or recycled, before the RewriteDriver is used for
// request processing.
//
// This method also sets up the user-agent and device properties.
void SetRequestHeaders(const RequestHeaders& headers);
const RequestHeaders* request_headers() const {
return request_headers_.get();
}
UserAgentMatcher* user_agent_matcher() const {
DCHECK(server_context() != NULL);
return server_context()->user_agent_matcher();
}
// Adds the filters from the options, specified by name in enabled_filters.
// This must be called explicitly after object construction to provide an
// opportunity to programatically add custom filters beyond those defined
// in RewriteOptions, via AddFilter(HtmlFilter* filter) (below).
void AddFilters();
// Adds a filter to the very beginning of the pre-render chain, taking
// ownership. This should only be used for filters that must run before any
// filter added via PrependOwnedPreRenderFilter.
void AddOwnedEarlyPreRenderFilter(HtmlFilter* filter);
// Adds a filter to the beginning of the pre-render chain, taking ownership.
void PrependOwnedPreRenderFilter(HtmlFilter* filter);
// Adds a filter to the end of the pre-render chain, taking ownership.
void AppendOwnedPreRenderFilter(HtmlFilter* filter);
// Same, without taking ownership.
void AppendUnownedPreRenderFilter(HtmlFilter* filter);
// Adds a filter to the end of the post-render chain, taking ownership.
void AddOwnedPostRenderFilter(HtmlFilter* filter);
// Same, without taking ownership.
void AddUnownedPostRenderFilter(HtmlFilter* filter);
// Add a RewriteFilter to the end of the pre-render chain and take ownership
// of the filter. This differs from AppendOwnedPreRenderFilter in that
// it adds the filter's ID into a dispatch table for serving
// rewritten resources. E.g. if your filter->id == "xy" and
// FetchResource("NAME.pagespeed.xy.HASH.EXT"...) is called, then
// RewriteDriver will dispatch to filter->Fetch().
//
// This is used when the filter being added is not part of the
// core set built into RewriteDriver and RewriteOptions, such
// as platform-specific or server-specific filters, or filters
// invented for unit-testing the framework.
void AppendRewriteFilter(RewriteFilter* filter);
// Like AppendRewriteFilter, but adds the filter to the beginning of the
// pre-render chain.
void PrependRewriteFilter(RewriteFilter* filter);
// Tells RewriteDriver that a certain portion of URL namespace should not
// be handled via usual (HTTP proxy semantics) means. It's up to
// the filters to actually arrange for that to do something.
// Takes ownership of the claimant object. Note that it's important for the
// claims to be disjoint, since the RewriteContext framework needs to
// be able to assign compatible Resource objects for same URLs/slots among
// all filters that deal with them.
void AddResourceUrlClaimant(ResourceUrlClaimant* claimant);
// Controls how HTML output is written. Be sure to call this last, after
// all other filters have been established.
//
// TODO(jmarantz): fix this in the implementation so that the caller can
// install filters in any order and the writer will always be last.
void SetWriter(Writer* writer);
Writer* writer() const { return writer_; }
// Initiates an async fetch for a rewritten resource with the specified name.
// If url matches the pattern of what the driver is authorized to serve,
// then true is returned and the caller must listen on the callback for
// the completion of the request.
//
// If the driver is not authorized to serve the resource for any of the
// following reasons, false is returned and the callback will -not- be
// called - the request should be passed to another handler.
// * The URL is invalid or it does not match the general pagespeed pattern.
// * The filter id in the URL does not map to a known filter.
// * The filter for the id in the URL doesn't recognize the format of the URL.
// * The filter for the id in the URL is forbidden.
//
// In other words there are three outcomes for this routine:
// 1. the request was handled immediately and the callback called
// before the method returns. true is returned.
// 2. the request looks good but was queued because some other resource
// fetch is needed to satisfy it. true is returned.
// 3. the request does not look like it belongs to Instaweb. The callback
// will not be called, and false will be returned.
//
// In even other words, if this routine returns 'false' then the callback
// will not be called. If the callback -is- called, then this should be the
// 'final word' on this request, whether it was called with success=true or
// success=false.
//
// Note that if the request headers have not yet been set on the driver then
// they'll be taken from the fetch.
bool FetchResource(const StringPiece& url, AsyncFetch* fetch);
// Initiates an In-Place Resource Optimization (IPRO) fetch (A resource which
// is served under the original URL, but is still able to be rewritten).
//
// proxy_mode indicates whether we are running as a proxy where users
// depend on us to send contents. When set true, we will perform HTTP fetches
// to get contents if not in cache and will ignore kRecentFetchNotCacheable
// and kRecentFetchFailed since we'll have to fetch the resource for users
// anyway. Origin implementations (like mod_pagespeed) should set this to
// false and let the serve serve the resource if it's not in cache.
//
// If proxy_mode is false and the resource could not be found in HTTP cache,
// async_fetch->Done(false) will be called and async_fetch->status_code()
// will be CacheUrlAsyncFetcher::kNotInCacheStatus (to distinguish this
// from a different reason for failure, like kRecentFetchNotCacheable).
//
// Note that if the request headers have not yet been set on the driver then
// they'll be taken from the fetch.
void FetchInPlaceResource(const GoogleUrl& gurl, bool proxy_mode,
AsyncFetch* async_fetch);
// See FetchResource. There are two differences:
// 1. It takes an OutputResource instead of a URL.
// 2. It returns whether a fetch was queued or not. This is safe
// to ignore because in either case the callback will be called.
// 3. If 'filter' is NULL then the request only checks cache and
// (if enabled) the file system.
bool FetchOutputResource(const OutputResourcePtr& output_resource,
RewriteFilter* filter,
AsyncFetch* async_fetch);
// Attempts to decode an output resource based on the URL pattern
// without actually rewriting it. No permission checks are performed on the
// url, though it is parsed to see if it looks like the url of a generated
// resource (which should mean checking the hash to ensure we generated it
// ourselves).
// TODO(jmaessen): add url hash & check thereof.
OutputResourcePtr DecodeOutputResource(const GoogleUrl& url,
RewriteFilter** filter) const;
// As above, but does not actually create a resource object,
// and instead outputs the decoded information into the various out
// parameters. Returns whether decoding successful or not.
// Uses options_to_use rather than this->options() to determine which
// drivers are forbidden from applying, etc.
bool DecodeOutputResourceName(const GoogleUrl& url,
const RewriteOptions* options_to_use,
const UrlNamer* url_namer,
ResourceNamer* name_out,
OutputResourceKind* kind_out,
RewriteFilter** filter_out) const;
// Attempts to lookup the metadata cache info that would be used for the
// output resource at url with the RewriteOptions set on this driver.
//
// If there is a problem with the URL, returns false, and *error_out
// will contain an error message.
//
// If it can determine the metadata cache key successfully, returns true,
// and eventually callback will be invoked with the metadata cache key
// and the decoding results.
//
// After calling this method, the driver should not be used for anything else.
bool LookupMetadataForOutputResource(
StringPiece url,
GoogleString* error_out,
RewriteContext::CacheLookupResultCallback* callback);
// Decodes the incoming pagespeed url to original url(s).
bool DecodeUrl(const GoogleUrl& url,
StringVector* decoded_urls) const;
// As above, but lets one specify the options and URL namer to use.
// Meant for use with the decoding_driver.
bool DecodeUrlGivenOptions(const GoogleUrl& url,
const RewriteOptions* options,
const UrlNamer* url_namer,
StringVector* decoded_urls) const;
FileSystem* file_system() { return file_system_; }
UrlAsyncFetcher* async_fetcher() { return url_async_fetcher_; }
// Set a fetcher that will be used by RewriteDriver for current request
// only (that is, until Clear()). RewriteDriver will take ownership of this
// fetcher, and will keep it around until Clear(), even if further calls
// to this method are made.
void SetSessionFetcher(UrlAsyncFetcher* f);
UrlAsyncFetcher* distributed_fetcher() { return distributed_async_fetcher_; }
// Does not take ownership.
void set_distributed_fetcher(UrlAsyncFetcher* fetcher) {
distributed_async_fetcher_ = fetcher;
}
// Creates a cache fetcher that uses the driver's fetcher and its options.
// Note: this means the driver's fetcher must survive as long as this does.
CacheUrlAsyncFetcher* CreateCacheFetcher();
// Returns a cache fetcher that does not fall back to an actual fetcher.
CacheUrlAsyncFetcher* CreateCacheOnlyFetcher();
ServerContext* server_context() const { return server_context_; }
Statistics* statistics() const;
// Takes ownership of 'options'.
void set_custom_options(RewriteOptions* options) {
set_options_for_pool(NULL, options);
}
// Takes ownership of 'options'. pool denotes the pool of rewrite drivers that
// use these options. May be NULL if using custom options.
void set_options_for_pool(RewriteDriverPool* pool, RewriteOptions* options) {
controlling_pool_ = pool;
options_.reset(options);
}
// Pool in which this driver can be recycled. May be NULL.
RewriteDriverPool* controlling_pool() { return controlling_pool_; }
// Return the options used for this RewriteDriver.
const RewriteOptions* options() const { return options_.get(); }
// Override HtmlParse's StartParseId to propagate any required options.
// Note that if this (or other variants) returns true you should use
// FinishParse(), otherwise Cleanup().
virtual bool StartParseId(const StringPiece& url, const StringPiece& id,
const ContentType& content_type);
// Override HtmlParse's FinishParse to ensure that the
// request-scoped cache is cleared immediately.
//
// Note that the RewriteDriver can delete itself in this method, if
// it's not externally managed, and if all RewriteContexts have been
// completed.
virtual void FinishParse();
// As above, but asynchronous. Note that the RewriteDriver may already be
// deleted at the point the callback is invoked. The scheduler lock will
// not be held when the callback is run.
void FinishParseAsync(Function* callback);
// Report error message with description of context's location
// (such as filenames and line numbers). context may be NULL, in which case
// the current parse position will be used.
void InfoAt(const RewriteContext* context,
const char* msg, ...) INSTAWEB_PRINTF_FORMAT(3, 4);
// Constructs name and URL for the specified input resource and encoder.
bool GenerateOutputResourceNameAndUrl(
const UrlSegmentEncoder* encoder,
const ResourceContext* data,
const ResourcePtr& input_resource,
GoogleString* name,
GoogleUrl* mapped_gurl,
GoogleString* failure_reason);
// Creates a reference-counted pointer to a new OutputResource object.
//
// The content type is taken from the input_resource, but can be modified
// with SetType later if that is not correct (e.g. due to image transcoding).
// Constructs an output resource corresponding to the specified input resource
// and encoded using the provided encoder. Assumes permissions checking
// occurred when the input resource was constructed, and does not do it again.
// To avoid if-chains, tolerates a NULL input_resource (by returning NULL).
// TODO(jmaessen, jmarantz): Do we want to permit NULL input_resources here?
// jmarantz has evinced a distaste.
OutputResourcePtr CreateOutputResourceFromResource(
const char* filter_id,
const UrlSegmentEncoder* encoder,
const ResourceContext* data,
const ResourcePtr& input_resource,
OutputResourceKind kind,
GoogleString* failure_reason);
// Creates an output resource where the name is provided. The intent is to
// be able to derive the content from the name, for example, by encoding
// URLs and metadata.
//
// This method succeeds unless the filename is too long.
//
// This name is prepended with path for writing hrefs, and the resulting url
// is encoded and stored at file_prefix when working with the file system.
// So hrefs are:
// $(PATH)/$(NAME).pagespeed[.$EXPERIMENT].$(FILTER_PREFIX).
// $(HASH).$(CONTENT_TYPE_EXT)
//
// EXPERIMENT is set only when there is an active experiment_spec.
//
// Could be private since you should use one of the versions below but put
// here with the rest like it and for documentation clarity.
OutputResourcePtr CreateOutputResourceWithPath(
const StringPiece& mapped_path, const StringPiece& unmapped_path,
const StringPiece& base_url, const StringPiece& filter_id,
const StringPiece& name, OutputResourceKind kind,
GoogleString* failure_reason);
// Fills in the resource namer based on the give filter_id, name and options
// stored in the driver.
void PopulateResourceNamer(
const StringPiece& filter_id,
const StringPiece& name,
ResourceNamer* full_name);
// Version of CreateOutputResourceWithPath which first takes only the
// unmapped path and finds the mapped path using the DomainLawyer
// and the base_url is this driver's base_url.
OutputResourcePtr CreateOutputResourceWithUnmappedUrl(
const GoogleUrl& unmapped_gurl, const StringPiece& filter_id,
const StringPiece& name, OutputResourceKind kind,
GoogleString* failure_reason);
// Version of CreateOutputResourceWithPath where the unmapped and mapped
// paths are different and the base_url is this driver's base_url.
OutputResourcePtr CreateOutputResourceWithMappedPath(
const StringPiece& mapped_path, const StringPiece& unmapped_path,
const StringPiece& filter_id, const StringPiece& name,
OutputResourceKind kind, GoogleString* failure_reason) {
return CreateOutputResourceWithPath(mapped_path, unmapped_path,
decoded_base_url_.AllExceptLeaf(),
filter_id, name, kind, failure_reason);
}
// Version of CreateOutputResourceWithPath where the unmapped and mapped
// paths and the base url are all the same. FOR TESTS ONLY.
OutputResourcePtr CreateOutputResourceWithPath(
const StringPiece& path, const StringPiece& filter_id,
const StringPiece& name, OutputResourceKind kind,
GoogleString* failure_reason) {
return CreateOutputResourceWithPath(path, path, path, filter_id, name,
kind, failure_reason);
}
// Creates an input resource based on input_url. Returns NULL if the input
// resource url isn't valid or is a data url, or can't legally be rewritten
// in the context of this page, in which case *is_authorized will be false.
// Assumes that resources from unauthorized domains may not be rewritten and
// that the resource is not intended exclusively for inlining.
ResourcePtr CreateInputResource(const GoogleUrl& input_url,
bool* is_authorized);
// Creates an input resource. Returns NULL if the input resource url isn't
// valid or is a data url, or can't legally be rewritten in the context of
// this page (which could mean that it was a resource from an unauthorized
// domain being processed by a filter that does not allow unauthorized
// resources, in which case *is_authorized will be false).
//
// There are two "special" options, and if you don't care about them you
// should just call CreateInputResource(input_url, is_authorized) to use
// their defaults:
// * If resources from unauthorized domains may be inlined, set
// inline_authorization_policy to kInlineUnauthorizedResources, otherwise
// set it to kInlineOnlyAuthorizedResources.
// * If this resource will be inlined after fetching, then set intended_for to
// kIntendedForInlining, otherwise use kIntendedForGeneral. This is to
// support AllowWhenInlining.
ResourcePtr CreateInputResource(
const GoogleUrl& input_url,
InlineAuthorizationPolicy inline_authorization_policy,
IntendedFor intended_for,
bool* is_authorized);
// Creates an input resource from the given absolute url. Requires that the
// provided url has been checked, and can legally be rewritten in the current
// page context. Only for use by unit tests.
ResourcePtr CreateInputResourceAbsoluteUncheckedForTestsOnly(
const StringPiece& absolute_url);
// Returns true if some ResourceUrlClaimant has staked a claim on given URL.
// If this returns true, CreateInputResource will fail, but it's probably
// not worth logging any debug filter hints about that.
bool IsResourceUrlClaimed(const GoogleUrl& url) const;
// Checks to see if the input_url has the same origin as and the base url, to
// make sure we're not fetching from another server. Does not consult the
// domain lawyer, and is not affected by AddDomain().
// Precondition: input_url.IsWebValid()
bool MatchesBaseUrl(const GoogleUrl& input_url) const;
// Checks to see if we can write the input_url resource in the domain_url
// taking into account domain authorization, wildcard allow/disallow from
// RewriteOptions, and the intended use of the url's resource. After the
// function is executed, is_authorized_domain will indicate whether input_url
// was found to belong to an authorized domain or not.
bool MayRewriteUrl(const GoogleUrl& domain_url,
const GoogleUrl& input_url,
InlineAuthorizationPolicy inline_authorization_policy,
IntendedFor intended_for,
bool* is_authorized_domain) const;
// Returns the appropriate base gurl to be used for resolving hrefs
// in the document. Note that HtmlParse::google_url() is the URL
// for the HTML file and is used for printing html syntax errors.
const GoogleUrl& base_url() const { return base_url_; }
// The URL that was requested if FetchResource was called.
StringPiece fetch_url() const { return fetch_url_; }
// Returns the decoded version of base_gurl() in case it was encoded by a
// non-default UrlNamer (for the default UrlNamer this returns the same value
// as base_url()). Required when fetching a resource by its encoded name.
const GoogleUrl& decoded_base_url() const { return decoded_base_url_; }
StringPiece decoded_base() const { return decoded_base_url_.Spec(); }
// Quick way to tell if the document url is https (ie was fetched via https).
bool IsHttps() const { return google_url().SchemeIs("https"); }
const UrlSegmentEncoder* default_encoder() const { return &default_encoder_; }
// Finds a filter with the given ID, or returns NULL if none found.
RewriteFilter* FindFilter(const StringPiece& id) const;
// Returns refs_before_base.
bool refs_before_base() { return refs_before_base_; }
// Sets whether or not there were references to urls before the
// base tag (if there is a base tag). This variable has document-level
// scope. It is reset at the beginning of every document by
// ScanFilter.
void set_refs_before_base() { refs_before_base_ = true; }
// Get/set the charset of the containing HTML page. See scan_filter.cc for
// an explanation of how this is determined, but NOTE that the determined
// charset can change as more of the HTML is seen, in particular after a
// meta tag.
StringPiece containing_charset() { return containing_charset_; }
void set_containing_charset(const StringPiece charset) {
charset.CopyToString(&containing_charset_);
}
// Creates and registers a HtmlElement slot for rewriting.
// If this is the first time called for this position, a new slot will be
// returned. On subsequent calls, the original slot will be returned so
// that rewrites are propagated between filters.
HtmlResourceSlotPtr GetSlot(const ResourcePtr& resource,
HtmlElement* elt,
HtmlElement::Attribute* attr);
// Creates and registers an inline resource slot for rewriting.
// If this is the first time called for this position, a new slot will be
// returned. On subsequent calls, the original slot will be returned so
// that rewrites are propagated between filters.
InlineResourceSlotPtr GetInlineSlot(const ResourcePtr& resource,
HtmlCharactersNode* char_node);
// Creates and registers an inline attribute resource slot for rewriting.
// If this is the first time called for this position, a new slot will be
// returned. On subsequent calls, the original slot will be returned so
// that rewrites are propagated between filters.
InlineAttributeSlotPtr GetInlineAttributeSlot(
const ResourcePtr& resource, HtmlElement* element,
HtmlElement::Attribute* attribute);
// Method to start a resource rewrite. This is called by a filter during
// parsing, although the Rewrite might continue after deadlines expire
// and the rewritten HTML must be flushed. Returns InitiateRewrite returns
// false if the system is not healthy enough to support resource rewrites.
bool InitiateRewrite(RewriteContext* rewrite_context)
LOCKS_EXCLUDED(rewrite_mutex());
void InitiateFetch(RewriteContext* rewrite_context);
// Provides a mechanism for a RewriteContext to notify a
// RewriteDriver that it is complete, to allow the RewriteDriver
// to delete itself or return it back to a free pool in the ServerContext.
//
// This will also call back into RewriteContext::Propagate, letting it
// know whether the context is still attached to the HTML DOM
// (and hence safe to render), and to do other bookkeeping.
//
// If 'permit_render' is false, no rendering will be asked for even if
// the context is still attached.
void RewriteComplete(RewriteContext* rewrite_context, bool permit_render);
// Provides a mechanism for a RewriteContext to notify a
// RewriteDriver that a certain number of rewrites have been discovered
// to need to take the slow path.
void ReportSlowRewrites(int num);
// If there are not outstanding references to this RewriteDriver,
// delete it or recycle it to a free pool in the ServerContext.
// If this is a fetch, calling this also signals to the system that you
// are no longer interested in its results.
void Cleanup();
// Adds an extra external reference to the object. You should not
// normally need to call it (NewRewriteDriver does it initially), unless for
// some reason you want to pin the object (e.g. in tests). Matches up with
// Cleanup.
void AddUserReference();
// Debugging routines to print out data about the driver.
GoogleString ToString(bool show_detached_contexts) const;
void PrintState(bool show_detached_contexts); // For debugging.
void PrintStateToErrorLog(bool show_detached_contexts); // For logs.
// Wait for outstanding Rewrite to complete. Once the rewrites are
// complete they can be rendered.
void WaitForCompletion();
// Wait for outstanding rewrite to complete, including any background
// work that may be ongoing even after results were reported.
//
// Note: while this guarantees that the result of the computation is
// known, the thread that performed it may still be running for a
// little bit and accessing the driver.
void WaitForShutDown();
// As above, but with a time bound, and taking a mode parameter to decide
// between WaitForCompletion or WaitForShutDown behavior.
// If timeout_ms <= 0, no time bound will be used.
void BoundedWaitFor(WaitMode mode, int64 timeout_ms)
LOCKS_EXCLUDED(rewrite_mutex());
// If this is set to true, during a Flush of HTML the system will
// wait for results of all rewrites rather than just waiting for
// cache lookups and a small deadline. Note, however, that in very
// rare circumstances some rewrites may still be dropped due to
// excessive load.
//
// Note: reset every time the driver is recycled.
void set_fully_rewrite_on_flush(bool x) {
fully_rewrite_on_flush_ = x;
}
// Returns if this response has a blocking rewrite or not.
bool fully_rewrite_on_flush() const {
return fully_rewrite_on_flush_;
}
// This is relevant only when fully_rewrite_on_flush is true.
// When this is set to true, Flush of HTML will not wait for async events
// while it does wait when it is set to false.
void set_fast_blocking_rewrite(bool x) {
fast_blocking_rewrite_ = x;
}
bool fast_blocking_rewrite() const {
return fast_blocking_rewrite_;
}
// If the value of X-PSA-Blocking-Rewrite request header matches the blocking
// rewrite key, set fully_rewrite_on_flush flag.
void EnableBlockingRewrite(RequestHeaders* request_headers);
// Indicate that this RewriteDriver will be explicitly deleted, and
// thus should not be auto-deleted at the end of the parse. This is
// primarily for tests.
//
// TODO(jmarantz): Consider phasing this out to make tests behave
// more like servers.
void set_externally_managed(bool x) { externally_managed_ = x; }
// Called by RewriteContext to let RewriteDriver know it will be continuing
// on the fetch in background, and so it should defer doing full cleanup
// sequences until DetachedFetchComplete() is called.
void DetachFetch();
// Called by RewriteContext when a detached async fetch is complete, allowing
// the RewriteDriver to be recycled if FetchComplete() got invoked as well.
void DetachedFetchComplete();
// Cleans up the driver and any fetch rewrite contexts, unless the fetch
// rewrite got detached by a call to DetachFetch(), in which case a call to
// DetachedFetchComplete() must also be performed.
void FetchComplete();
// Deletes the specified RewriteContext. If this is the last RewriteContext
// active on this Driver, and there is no other outstanding activity, then
// the RewriteDriver itself can be recycled, and WaitForCompletion can return.
//
// We expect to this method to be called on the Rewrite thread.
void DeleteRewriteContext(RewriteContext* rewrite_context);
int rewrite_deadline_ms() { return options()->rewrite_deadline_ms(); }
// Sets a maximum amount of time to process a page across all flush
// windows; i.e., the entire lifecycle of this driver during a given pageload.
// A negative value indicates no limit.
// Setting fully_rewrite_on_flush() overrides this.
void set_max_page_processing_delay_ms(int x) {
max_page_processing_delay_ms_ = x;
}
int max_page_processing_delay_ms() { return max_page_processing_delay_ms_; }
// Sets the device type chosen for the current property_page.
void set_device_type(UserAgentMatcher::DeviceType x) { device_type_ = x; }
UserAgentMatcher::DeviceType device_type() const { return device_type_; }
// Tries to register the given rewrite context as working on
// its partition key. If this context is the first one to try to handle it,
// returns NULL. Otherwise returns the previous such context.
//
// Must only be called from rewrite thread.
RewriteContext* RegisterForPartitionKey(const GoogleString& partition_key,
RewriteContext* candidate);
// Must be called after all other rewrites that are currently relying on this
// one have had their RepeatedSuccess or RepeatedFailure methods called.
//
// Must only be called from rewrite thread.
void DeregisterForPartitionKey(
const GoogleString& partition_key, RewriteContext* candidate);
// Indicates that a Flush through the HTML parser chain should happen
// soon, e.g. once the network pauses its incoming byte stream.
void RequestFlush() { flush_requested_ = true; }
bool flush_requested() const { return flush_requested_; }
// Executes an Flush() if RequestFlush() was called, e.g. from the
// Listener Filter (see set_event_listener below). Consider an HTML
// parse driven by a UrlAsyncFetcher. When the UrlAsyncFetcher
// temporarily runs out of bytes to read, it calls
// response_writer->Flush(). When that happens, we may want to
// consider flushing the outstanding HTML events through the system
// so that the browser can start fetching subresources and
// rendering. The event_listener (see set_event_listener below)
// helps determine whether enough "interesting" events have passed
// in the current flush window so that we should take this incoming
// network pause as an opportunity.
void ExecuteFlushIfRequested();
// Asynchronous version of the above. Note that you should not
// attempt to write out any data until the callback is invoked.
// (If a flush is not needed, the callback will be invoked immediately).
void ExecuteFlushIfRequestedAsync(Function* callback);
// Overrides HtmlParse::Flush so that it can happen in two phases:
// 1. Pre-render chain runs, resulting in async rewrite activity
// 2. async rewrite activity ends, calling callback, and post-render
// filters run.
// This API is used for unit-tests & Apache (which lacks a useful event
// model) and results in blocking behavior.
//
// FlushAsync is prefered for event-driven servers.
virtual void Flush();
// Initiates an asynchronous Flush. done->Run() will be called when
// the flush is complete. Further calls to ParseText should be deferred until
// the callback is called. Scheduler mutex is not held while done is called.
void FlushAsync(Function* done);
// Queues up a task to run on the (high-priority) rewrite thread.
void AddRewriteTask(Function* task);
// Queues up a task to run on the low-priority rewrite thread.
// Such tasks are expected to be safely cancelable.
void AddLowPriorityRewriteTask(Function* task);
QueuedWorkerPool::Sequence* html_worker() { return html_worker_; }
QueuedWorkerPool::Sequence* rewrite_worker() { return rewrite_worker_; }
QueuedWorkerPool::Sequence* low_priority_rewrite_worker() {
return low_priority_rewrite_worker_;
}
Scheduler* scheduler() { return scheduler_; }
// Used by CacheExtender, CssCombineFilter, etc. for rewriting domains
// of sub-resources in CSS.
DomainRewriteFilter* domain_rewriter() { return domain_rewriter_.get(); }
UrlLeftTrimFilter* url_trim_filter() { return url_trim_filter_.get(); }
// Rewrites CSS content to absolutify any relative embedded URLs, streaming
// the results to the writer. Returns 'false' if the writer returns false
// or if the content was not rewritten because the domains of the gurl
// and resolved_base match.
//
// input_css_base contains the path where the CSS text came from.
// output_css_base contains the path where the CSS will be written.
CssResolutionStatus ResolveCssUrls(const GoogleUrl& input_css_base,
const StringPiece& output_css_base,
const StringPiece& contents,
Writer* writer,
MessageHandler* handler);
// Determines if an URL relative to the given input_base needs to be
// absolutified given that it will end up under output_base:
// - If we are proxying and input_base isn't proxy encoded, then yes.
// - If we aren't proxying and input_base != output_base, then yes.
// - If we aren't proxying and the domain lawyer will shard or rewrite
// input_base, then yes.
// If not NULL also set *proxy_mode to whether proxy mode is active or not.
bool ShouldAbsolutifyUrl(const GoogleUrl& input_base,
const GoogleUrl& output_base,
bool* proxy_mode) const;
// Update the PropertyValue named 'property_name' in dom cohort with
// the value 'property_value'. It is the responsibility of the client to
// ensure that property cache and dom cohort are enabled when this function is
// called. It is a programming error to call this function when property
// cache or dom cohort is not available, more so since the value payload has
// to be serialised before calling this function. Hence this function will
// DFATAL if property cache or dom cohort is not available.
void UpdatePropertyValueInDomCohort(
AbstractPropertyPage* page,
StringPiece property_name,
StringPiece property_value);
// Returns the property page which contains the cached properties associated
// with the current URL.
PropertyPage* property_page() const;
// Returns the property page which contains the cached properties associated
// with the current URL and fallback URL (i.e. without query params). This
// should be used where a property is interested in fallback values if
// actual values are not present.
FallbackPropertyPage* fallback_property_page() const {
return fallback_property_page_;
}
// Returns property page which contains cached properties associated with
// the current origin (host/port/protocol). May be NULL.
PropertyPage* origin_property_page() const;
// Takes ownership of page.
void set_property_page(PropertyPage* page);
// Takes ownership of page.
void set_fallback_property_page(FallbackPropertyPage* page);
// Does not take the ownership of the page.
void set_unowned_fallback_property_page(FallbackPropertyPage* page);
// Takes ownership of page.
void set_origin_property_page(PropertyPage* page);
// Used by ImageRewriteFilter for identifying critical images.
const CriticalLineInfo* critical_line_info() const;
// Inserts the critical images present on the requested html page. It takes
// the ownership of critical_line_info.
void set_critical_line_info(CriticalLineInfo* critical_line_info);
CriticalKeys* beacon_critical_line_info() const;
void set_beacon_critical_line_info(CriticalKeys* beacon_critical_line_info);
const SplitHtmlConfig* split_html_config();
CriticalCssResult* critical_css_result() const;
// Sets the Critical CSS rules info in the driver and the ownership of
// the rules stays with the driver.
void set_critical_css_result(CriticalCssResult* critical_css_rules);
// The JS to detect above-the-fold images should only be enabled if one of the
// filters that uses critical image information is enabled, the property cache
// is enabled (since the critical image information is stored in the property
// cache), and it is not explicitly disabled through options.
bool is_critical_images_beacon_enabled();
// Used by ImageRewriteFilter for identifying critical images.
CriticalImagesInfo* critical_images_info() const {
return critical_images_info_.get();
}
// This should only be called by the CriticalSelectorFinder. Normal users
// should call CriticalSelectorFinder::IsCriticalImage.
// TODO(jud): Remove when the finders reside in RewriteDriver and manage their
// own state.
CriticalSelectorInfo* critical_selector_info() {
return critical_selector_info_.get();
}
// This should only be called by the CriticalSelectorFinder.
// TODO(jud): Remove when the finders reside in RewriteDriver and manage their
// own state.
void set_critical_selector_info(CriticalSelectorInfo* info) {
critical_selector_info_.reset(info);
}
// Inserts the critical images present on the requested html page. It takes
// ownership of critical_images_info. This should only be called by the
// CriticalImagesFinder, normal users should just be using the automatic
// management of critical_images_info that CriticalImagesFinder provides.
void set_critical_images_info(CriticalImagesInfo* critical_images_info) {
critical_images_info_.reset(critical_images_info);
}
// Return true if we must prioritize critical selectors, and we should
// therefore enable its prerequisite filters as well.
bool CriticalSelectorsEnabled() const;
// Return true if we must flatten css imports, either because the filter is
// enabled explicitly or because it is enabled by CriticalSelectorsEnabled.
bool FlattenCssImportsEnabled() const {
return (options()->Enabled(RewriteOptions::kFlattenCssImports) ||
(!options()->Forbidden(RewriteOptions::kFlattenCssImports) &&
(CriticalSelectorsEnabled() ||
options()->Enabled(RewriteOptions::kComputeCriticalCss))));
}
// We expect to this method to be called on the HTML parser thread.
// Returns the number of images whose low quality images are inlined in the
// html page.
int num_inline_preview_images() const { return num_inline_preview_images_; }
// We expect to this method to be called on the HTML parser thread.
void increment_num_inline_preview_images();
// We expect to this method to be called on the HTML parser thread.
// Returns the number of pagespeed resources flushed by flush early flow.
int num_flushed_early_pagespeed_resources() const {
return num_flushed_early_pagespeed_resources_;
}
// We expect to this method to be called on the HTML parser thread or after
// parsing is completed.
void increment_num_flushed_early_pagespeed_resources() {
++num_flushed_early_pagespeed_resources_;
}
// Increment reference count for misc. async ops that need the RewriteDriver
// kept alive.
void IncrementAsyncEventsCount();
// Decrements a reference count bumped up by IncrementAsyncEventsCount()
void DecrementAsyncEventsCount();
// Increment reference count for misc async ops that should be waited for
// before doing rendering for current flush window.
void IncrementRenderBlockingAsyncEventsCount();
// Decrements a reference count bumped up by
// IncrementRenderBlockingAsyncEventsCount()
void DecrementRenderBlockingAsyncEventsCount();
// Determines whether the document's Content-Type has a mimetype indicating
// that browsers should parse it as XHTML.
XhtmlStatus MimeTypeXhtmlStatus();
void set_flushed_cached_html(bool x) { flushed_cached_html_ = x; }
bool flushed_cached_html() { return flushed_cached_html_; }
void set_flushing_cached_html(bool x) { flushing_cached_html_ = x; }
bool flushing_cached_html() const { return flushing_cached_html_; }
void set_flushed_early(bool x) { flushed_early_ = x; }
bool flushed_early() const { return flushed_early_; }
void set_flushing_early(bool x) { flushing_early_ = x; }
bool flushing_early() const { return flushing_early_; }
void set_is_lazyload_script_flushed(bool x) {
is_lazyload_script_flushed_ = x;
}
bool is_lazyload_script_flushed() const {
return is_lazyload_script_flushed_; }
// This method is not thread-safe. Call it only from the html parser thread.
FlushEarlyInfo* flush_early_info();
FlushEarlyRenderInfo* flush_early_render_info() const;
// Takes the ownership of flush_early_render_info. This method is not
// thread-safe. Call it only from the html parser thread.
void set_flush_early_render_info(
FlushEarlyRenderInfo* flush_early_render_info);
// Determines whether we are currently in Debug mode; meaning that the
// site owner or user has enabled filter kDebug.
bool DebugMode() const { return options()->Enabled(RewriteOptions::kDebug); }
// Log the given debug message(s) as HTML comments after the given element,
// if not NULL, it has not been flushed, and if debug is enabled. The form
// that takes a repeated field is intended for use by CachedResult, e.g:
// InsertDebugComment(cached_result.debug_message(), element);
// Messages are HTML-escaped before being written out to the DOM.
void InsertDebugComment(StringPiece unescaped_message, HtmlNode* node);
void InsertDebugComments(
const protobuf::RepeatedPtrField<GoogleString>& unescaped_messages,
HtmlElement* element);
void InsertUnauthorizedDomainDebugComment(StringPiece url,
HtmlElement* element);
// Generates an unauthorized domain debug comment. Public for unit tests.
static GoogleString GenerateUnauthorizedDomainDebugComment(
const GoogleUrl& gurl);
// Saves the origin headers for a request in flush_early_info so that it can
// be used in subsequent request.
void SaveOriginalHeaders(const ResponseHeaders& response_headers);
// log_record() always returns a pointer to a valid AbstractLogRecord, owned
// by the rewrite_driver's request context.
AbstractLogRecord* log_record();
DomStatsFilter* dom_stats_filter() const {
return dom_stats_filter_;
}
// Determines whether the system is healthy enough to rewrite resources.
// Currently, systems get sick based on the health of the metadata cache.
bool can_rewrite_resources() const { return can_rewrite_resources_; }
// Determine whether this driver is nested inside another.
bool is_nested() const { return is_nested_; }
// Determines whether metadata was requested in the response headers and
// verifies that the key in the header is the same as the expected key. An
// empty expected key returns false.
bool MetadataRequested(const RequestHeaders& request_headers) const;
// Did the driver attempt to distribute the fetch?
bool tried_to_distribute_fetch() const { return tried_to_distribute_fetch_; }
// Writes the specified contents into the output resource, and marks it
// as optimized. 'inputs' described the input resources that were used
// to construct the output, and is used to determine whether the
// result can be safely cache extended and be marked publicly cacheable.
// 'content_type' and 'charset' specify the mimetype and encoding of
// the contents, and will help form the Content-Type header.
// 'charset' may be empty when not specified.
//
// Note that this does not escape charset.
//
// Callers should take care that dangerous types like 'text/html' do not
// sneak into content_type.
bool Write(const ResourceVector& inputs,
const StringPiece& contents,
const ContentType* type,
StringPiece charset,
OutputResource* output);
void set_defer_instrumentation_script(bool x) {
defer_instrumentation_script_ = x;
}
bool defer_instrumentation_script() const {
return defer_instrumentation_script_;
}
// Sets the num_initiated_rewrites_. This should only be called from test
// code.
void set_num_initiated_rewrites(int64 x) {
ScopedMutex lock(rewrite_mutex());
num_initiated_rewrites_ = x;
}
int64 num_initiated_rewrites() const {
ScopedMutex lock(rewrite_mutex());
return num_initiated_rewrites_;
}
// Sets the num_detached_rewrites_. This should only be called from test code.
void set_num_detached_rewrites(int64 x) {
ScopedMutex lock(rewrite_mutex());
num_detached_rewrites_ = x;
}
int64 num_detached_rewrites() const {
ScopedMutex lock(rewrite_mutex());
return num_detached_rewrites_;
}
void set_pagespeed_query_params(StringPiece x) {
x.CopyToString(&pagespeed_query_params_);
}
StringPiece pagespeed_query_params() const {
return pagespeed_query_params_;
}
void set_pagespeed_option_cookies(StringPiece x) {
x.CopyToString(&pagespeed_option_cookies_);
}
StringPiece pagespeed_option_cookies() const {
return pagespeed_option_cookies_;
}
// We fragment the cache based on the hostname we got from the request, unless
// that was overridden in the options with a cache_fragment.
const GoogleString& CacheFragment() const;
// Utility function to set/clear cookies for PageSpeed options. gurl is the
// URL of the request from which the host is extracted for a cookie attribute.
// TODO(matterbury): Get the URL from 'this' which we can't do now because it
// isn't set until we've decided that the content of requested URL is HTML.
// Returns true if any Set-Cookie headers are added, in which case
// ComputeCaching has been called on response_headers.
bool SetOrClearPageSpeedOptionCookies(const GoogleUrl& gurl,
ResponseHeaders* response_headers);
// Calls the provided ResourceNamer's Decode() function, passing the hash and
// signature lengths from this RewriteDriver.
bool Decode(StringPiece leaf, ResourceNamer* resource_namer) const;
protected:
virtual void DetermineFiltersBehaviorImpl();
private:
friend class DistributedRewriteContextTest;
friend class RewriteContext;
friend class RewriteDriverTest;
friend class RewriteTestBase;
friend class ServerContextTest;
typedef std::map<GoogleString, RewriteFilter*> StringFilterMap;
// Returns true if the given fetch request should be distributed.
bool ShouldDistributeFetch(const StringPiece& filter_id);
// Distributes the fetch to another task if ShouldDistributeFetch allows it
// for the provided filter_id and streams the result to the provided fetch
// object.
//
// Returns true if an attempt to distribute was made. If the attempt fails
// before async_fetch was written to (before ResponseHeaders) it will call
// RewriteDriver::FetchResource() and skip distribution. If the attempt fails
// after writing to the ResponseHeaders then the fetch will ultimately fail
// and the client will get a broken resource.
//
// Returns false if ShouldDistributeFetch disallows the distribution.
bool DistributeFetch(const StringPiece& url, const StringPiece& filter_id,
AsyncFetch* async_fetch);
// Checks whether outstanding rewrites are completed in a satisfactory fashion
// with respect to given wait_mode and timeout, and invokes done->Run() (with
// rewrite_mutex released) when either finished or timed out. May relinquish
// rewrite_mutex() temporarily to invoke done.
void CheckForCompletionAsync(WaitMode wait_mode, int64 timeout_ms,
Function* done)
EXCLUSIVE_LOCKS_REQUIRED(rewrite_mutex());
// A single check attempt for the above. Will either invoke callback (with
// rewrite_mutex released) or ask scheduler to check again. May relinquish
// rewrite_mutex() temporarily to invoke done.
void TryCheckForCompletion(WaitMode wait_mode, int64 end_time_ms,
Function* done)
EXCLUSIVE_LOCKS_REQUIRED(rewrite_mutex());
// Termination predicate for above.
bool IsDone(WaitMode wait_mode, bool deadline_reached)
EXCLUSIVE_LOCKS_REQUIRED(rewrite_mutex());
// Always wait for pending async events during shutdown or while waiting for
// the completion of all rewriting (except in fast_blocking_rewrite mode).
bool WaitForPendingAsyncEvents(WaitMode wait_mode) {
return wait_mode == kWaitForShutDown ||
(fully_rewrite_on_flush_ && !fast_blocking_rewrite_);
}
// Portion of flush that happens asynchronously off the scheduler
// once the rendering is complete. Calls back to 'callback' after its
// processing, but with the lock released.
void FlushAsyncDone(int num_rewrites, Function* callback);
// Returns the amount of time to wait for rewrites to complete for the
// current flush window. This combines the per-flush window deadline
// (configured via rewrite_deadline_ms()) and the per-page deadline
// (configured via max_page_processing_delay_ms()).
int64 ComputeCurrentFlushWindowRewriteDelayMs();
// Queues up invocation of FlushAsyncDone in our html_workers sequence.
void QueueFlushAsyncDone(int num_rewrites, Function* callback);
// Called as part of implementation of FinishParseAsync, after the
// flush is complete.
void QueueFinishParseAfterFlush(Function* user_callback);
void FinishParseAfterFlush(Function* user_callback);
bool RewritesComplete() const EXCLUSIVE_LOCKS_REQUIRED(rewrite_mutex());
// Sets the base GURL in response to a base-tag being parsed. This
// should only be called by ScanFilter.
void SetBaseUrlIfUnset(const StringPiece& new_base);
// Sets the base URL for a resource fetch. This should only be called from
// test code and from FetchResource.
void SetBaseUrlForFetch(const StringPiece& url);
// Saves a decoding of the Base URL in decoded_base_url_. Use this
// whenever updating base_url_.
void SetDecodedUrlFromBase();
// The rewrite_mutex is owned by the scheduler.
AbstractMutex* rewrite_mutex() const LOCK_RETURNED(scheduler_->mutex()) {
return scheduler_->mutex();
}
// Parses an arbitrary block of an html file
virtual void ParseTextInternal(const char* content, int size);
// Indicates whether we should skip parsing for the given request.
bool ShouldSkipParsing();
// Returns the length of the signature on a signed resource URL.
int SignatureLength() const;
friend class ScanFilter;
// Registers RewriteFilter in the map, but does not put it in the
// html parse filter chain. This allows it to serve resource
// requests.
void RegisterRewriteFilter(RewriteFilter* filter);
// Adds an already-owned rewrite filter to the pre-render chain. This
// is used for filters that are unconditionally created for handling of
// resources, but their presence in the html-rewrite chain is conditional
// on options.
void EnableRewriteFilter(const char* id);
// Internal low-level helper for resource creation.
// Use only when permission checking has been done explicitly on the
// caller side. is_authorized_domain is passed along to Resource object
// creation, in order to decide whether to keep the resource in the usual
// key space or a separate one meant for unauthorized resources only.
ResourcePtr CreateInputResourceUnchecked(const GoogleUrl& gurl,
bool is_authorized_domain);
void AddPreRenderFilters();
void AddPostRenderFilters();
// Helper function to decode the pagespeed url.
bool DecodeOutputResourceNameHelper(const GoogleUrl& url,
const RewriteOptions* options_to_use,
const UrlNamer* url_namer,
ResourceNamer* name_out,
OutputResourceKind* kind_out,
RewriteFilter** filter_out,
GoogleString* url_base,
StringVector* urls) const;
// When HTML parsing is complete, we have learned all we can about the DOM, so
// immediately write anything required into that Cohort into the page property
// cache. Writes to this cohort are predicated so that they only occur if a
// filter that actually makes use of it is enabled. This prevents filling the
// cache with unnecessary entries. To enable writing, a filter should override
// DetermineEnabled to call
// RewriteDriver::set_write_property_cache_dom_cohort(true), or in the case of
// a RewriteFilter, should override
// RewriteFilter::UsesPropertyCacheDomCohort() to return true.
void WriteDomCohortIntoPropertyCache();
// Used by CreateCacheFetcher() and CreateCacheOnlyFetcher().
CacheUrlAsyncFetcher* CreateCustomCacheFetcher(UrlAsyncFetcher* base_fetcher);
// Just before releasing the rewrite driver, check if the feature for storing
// rewritten responses (e.g. html) in cache is enabled. If yes, purge the
// old response if significant amount of rewriting happened after this
// response was stored in the cache. If not, release the rewrite driver. If a
// purge fetch request is issued, the rewrite driver will be released after
// this async fetch request is completed.
void PossiblyPurgeCachedResponseAndReleaseDriver();
// Log statistics to the AbstractLogRecord.
void LogStats();
// This pair of calls helps determine if code that changes event state
// should wake up anyone waiting for rewrite driver's completion.
//
// The usage pattern is something like this:
// ScopedMutex lock(rewrite_mutex());
// bool should_signal_cookie = PrepareShouldSignal();
//
// // Change state
// ...
//
// SignalIfRequired(should_signal_cookie);
//
// WARNING: SignalIfRequired() drops the lock on rewrite_mutex() temporarily,
// so 'this' could get deleted after it returns, so it should not be accessed
// afterwards.
bool PrepareShouldSignal() EXCLUSIVE_LOCKS_REQUIRED(rewrite_mutex());
void SignalIfRequired(bool result_of_prepare_should_signal)
EXCLUSIVE_LOCKS_REQUIRED(rewrite_mutex());
// Only the first base-tag is significant for a document -- any subsequent
// ones are ignored. There should be no URLs referenced prior to the base
// tag, if one exists. See
//
// http://www.whatwg.org/specs/web-apps/current-work/multipage/
// semantics.html#the-base-element
// http://www.whatwg.org/specs/web-apps/current-work/multipage/
// urls.html#document-base-url
//
// Thus we keep the base-tag in the RewriteDriver, and also keep track of
// whether it's been reset already within the document.
bool base_was_set_;
// Stores whether or not there were references to urls before the
// base tag (if there is a base tag) in this document. If there is
// no base tag, this should be false. If the base tag is before all
// other url references, this should also be false.
bool refs_before_base_;
// The charset of the containing HTML page.
GoogleString containing_charset_;
// Copies properties from the request headers to the request context,
// if both are non-null.
void PopulateRequestContext();
bool filters_added_;
bool externally_managed_;
// Memory management stuff. Some of the reference counts we keep track of
// also are used as a count of events, to help determine when we are done.
//
// WARNING: every time you decrement reference counts, you should
// check release_driver_ within the critical section, and call
// PossiblyPurgeCachedResponseAndReleaseDriver() if it is true
// after releasing the lock. The easiest way to get it right is to just call
// DropReference().
enum RefCategory {
kRefUser, // External refcount from users
kRefParsing, // Parser active
// The number of rewrites (RewriteContext) that have been requested,
// and not yet completed, and for which we still hope to render
// them within the flush window. This is waited for.
kRefPendingRewrites,
// The number of rewrites (RewriteContext) that have missed the rendering
// deadline. We don't wait for them, but they still need to keep
// the RewriteDriver alive.
kRefDetachedRewrites,
// Tracks the number of RewriteContexts that have been completed,
// but not yet deleted. Once RewriteComplete has been called,
// rewrite_context->Propagate() is called to render slots (if not
// detached) and to queue up activity that must occur prior to the
// context being deleted: specifically running any successors.
// After all that occurs, DeleteRewriteContext must be called and
// that will decrement this counter.
kRefDeletingRewrites,
// Keeps track of fetch-responding work that's user-facing.
kRefFetchUserFacing,
// Keeps track of any background continuation of a fetch.
kRefFetchBackground,
// Misc async references from outside
//
// TODO(morlovich): Split between events people might want to wait for
// and events which they don't in a follow up.
kRefAsyncEvents,
// Async events we always wait for, even if fully_rewrite_on_flush isn't
// turned on.
kRefRenderBlockingAsyncEvents,
kNumRefCategories
};
friend class CategorizedRefcount<RewriteDriver, RefCategory>;
// Protected by rewrite_mutex().
CategorizedRefcount<RewriteDriver, RefCategory> ref_counts_;
// Interface to CategorizedRefcount
void LastRefRemoved();
StringPiece RefCategoryName(RefCategory cat);
// Drops a reference of given kind, signaling any waiters
// and potentially even releasing the rewrite driver.
void DropReference(RefCategory cat);
// Set to true when the refcount reaches 0. See comment
// above RefCategory for how this should be used.
bool release_driver_;
// If not kNoWait, indicates that WaitForCompletion or similar method
// have been called, and an another thread is waiting for us to notify it of
// everything having been finished in a given mode.
WaitMode waiting_ GUARDED_BY(rewrite_mutex());
// This is set to true if the current wait's deadline has expired.
bool waiting_deadline_reached_ GUARDED_BY(rewrite_mutex());
// If this is true, the usual HTML streaming interface will let rendering
// of every flush window fully complete before proceeding rather than
// use a deadline. This means rewriting of HTML may be slow, and hence
// should not be used for online traffic.
bool fully_rewrite_on_flush_;
// If this is true, we don't wait for async events before flushing bytes to
// the client during a blocking rewrite; else we do wait for async events.
bool fast_blocking_rewrite_;
bool flush_requested_;
bool flush_occurred_;
// If it is true, then cached html is flushed.
bool flushed_cached_html_;
// If it is true, then we are using this RewriteDriver to flush cached html.
bool flushing_cached_html_;
// If it is true, then the bytes were flushed before receiving bytes from the
// origin server.
bool flushed_early_;
// If set to true, then we are using this RewriteDriver to flush HTML to the
// user early. This is only set to true when
// enable_flush_subresources_experimental is true.
bool flushing_early_;
// If it is set to true, then lazyload script is flushed with flush early
// flow.
bool is_lazyload_script_flushed_;
// Tracks whether any filter that uses the dom cohort of the property cache is
// enabled. Writes to the property cache for this cohort are predicated on
// this.
bool write_property_cache_dom_cohort_;
// URL of the HTML pages being rewritten in the HTML flow or the
// of the resource being rewritten in the resource flow.
GoogleUrl base_url_;
// In the resource flow, the URL requested may not have the same
// base as the original resource. decoded_base_url_ stores the base
// of the original (un-rewritten) resource.
GoogleUrl decoded_base_url_;
// This is the URL that is being fetched in a fetch path (not valid in HTML
// path).
GoogleString fetch_url_;
GoogleString user_agent_;
LazyBool should_skip_parsing_;
StringFilterMap resource_filter_map_;
ResponseHeaders* response_headers_;
// request_headers_ is a copy of the Fetch's request headers, and it
// stays alive until the rewrite driver is recycled or deleted.
scoped_ptr<const RequestHeaders> request_headers_;
int status_code_; // Status code of response for this request.
// This group of rewrite-context-related variables is accessed
// only in the main thread of RewriteDriver (aka the HTML thread).
typedef std::vector<RewriteContext*> RewriteContextVector;
RewriteContextVector rewrites_; // ordered list of rewrites to initiate
// The maximum amount of time to wait for page processing across all flush
// windows. A negative value implies no limit.
int max_page_processing_delay_ms_;
typedef std::set<RewriteContext*> RewriteContextSet;
// Contains the RewriteContext* that have been queued into the
// RewriteThread, but have not gotten to the point where
// RewriteComplete() has been called. This set is cleared
// one the rewrite_deadline_ms has passed.
RewriteContextSet initiated_rewrites_ GUARDED_BY(rewrite_mutex());
// Number of total initiated rewrites for the request.
int64 num_initiated_rewrites_ GUARDED_BY(rewrite_mutex());
// Number of total detached rewrites for the request, i.e. rewrites whose
// results did not make it to the response. This is different from
// kRefDetachedRewrites (and detached_rewrites_.size(), which is equal to it)
// since that counter is for the number of rewrites
// currently in the detached state for the current flush window,
// while this variable is total that ever got detached over all of the
// document.
int64 num_detached_rewrites_ GUARDED_BY(rewrite_mutex());
// Contains the RewriteContext* that were still running at the deadline.
// They are said to be in a "detached" state although the RewriteContexts
// themselves don't know that. They will continue performing their
// Rewrite in the RewriteThread, and caching the results. And until
// they complete, the RewriteDriver must stay alive and not be Recycled
// or deleted. WaitForCompletion() blocks until all detached_rewrites
// have been retired.
RewriteContextSet detached_rewrites_ GUARDED_BY(rewrite_mutex());
// Rewrites that may possibly be satisfied from metadata cache alone.
int possibly_quick_rewrites_ GUARDED_BY(rewrite_mutex());
// List of RewriteContext objects for fetch to delete. We do it in
// clear as a simplification.
RewriteContextVector fetch_rewrites_;
// These objects are provided on construction or later, and are
// owned by the caller.
FileSystem* file_system_;
ServerContext* server_context_;
Scheduler* scheduler_;
UrlAsyncFetcher* default_url_async_fetcher_; // the fetcher we got at ctor
// This is the fetcher we use --- it's either the default_url_async_fetcher_,
// or whatever it was temporarily overridden to by SetSessionFetcher.
// This is either owned externally or via owned_url_async_fetchers_.
UrlAsyncFetcher* url_async_fetcher_;
// This is the fetcher that is used to distribute rewrites if enabled. This
// can be NULL if distributed rewriting is not configured. This is owned
// externally.
UrlAsyncFetcher* distributed_async_fetcher_;
// A list of all the UrlAsyncFetchers that we own, as set with
// SetSessionFetcher.
std::vector<UrlAsyncFetcher*> owned_url_async_fetchers_;
DomStatsFilter* dom_stats_filter_;
scoped_ptr<HtmlWriterFilter> html_writer_filter_;
ScanFilter scan_filter_;
scoped_ptr<DomainRewriteFilter> domain_rewriter_;
scoped_ptr<UrlLeftTrimFilter> url_trim_filter_;
// Maps rewrite context partition keys to the context responsible for
// rewriting them, in case a URL occurs more than once.
typedef std::map<GoogleString, RewriteContext*> PrimaryRewriteContextMap;
PrimaryRewriteContextMap primary_rewrite_context_map_;
HtmlResourceSlotSet slots_;
InlineResourceSlotSet inline_slots_;
InlineAttributeSlotSet inline_attribute_slots_;
scoped_ptr<RewriteOptions> options_;
RewriteDriverPool* controlling_pool_; // or NULL if this has custom options.
// Object which manages CacheUrlAsyncFetcher async operations.
scoped_ptr<CacheUrlAsyncFetcher::AsyncOpHooks>
cache_url_async_fetcher_async_op_hooks_;
// The default resource encoder
UrlSegmentEncoder default_encoder_;
// The first chain of filters called before waiting for Rewrites to complete.
FilterList early_pre_render_filters_;
// The second chain of filters called before waiting for Rewrites to complete.
FilterList pre_render_filters_;
// Owned by us.
std::vector<ResourceUrlClaimant*> resource_claimants_;
// A container of filters to delete when RewriteDriver is deleted. This
// can include pre_render_filters as well as those added to the post-render
// chain owned by HtmlParse.
FilterVector filters_to_delete_;
QueuedWorkerPool::Sequence* html_worker_;
QueuedWorkerPool::Sequence* rewrite_worker_;
QueuedWorkerPool::Sequence* low_priority_rewrite_worker_;
Writer* writer_;
// Stores any cached properties associated with the current URL and fallback
// URL (i.e. without query params).
FallbackPropertyPage* fallback_property_page_;
// Boolean value which tells whether property page is owned by driver or not.
bool owns_property_page_;
// Per-origin property page, for things which are site-wide.
scoped_ptr<PropertyPage> origin_property_page_;
// Device type for the current property page.
UserAgentMatcher::DeviceType device_type_;
scoped_ptr<CriticalLineInfo> critical_line_info_;
scoped_ptr<CriticalKeys> beacon_critical_line_info_;
scoped_ptr<SplitHtmlConfig> split_html_config_;
// The critical image finder and critical selector finder will lazy-init these
// fields.
scoped_ptr<CriticalImagesInfo> critical_images_info_;
scoped_ptr<CriticalSelectorInfo> critical_selector_info_;
scoped_ptr<CriticalCssResult> critical_css_result_;
// Memoized computation of whether the current doc has an XHTML mimetype.
bool xhtml_mimetype_computed_;
XhtmlStatus xhtml_status_ : 8;
// Number of images whose low quality images are inlined in the html page by
// InlinePreviewFilter.
int num_inline_preview_images_;
// Number of flushed early pagespeed rewritten resource.
int num_flushed_early_pagespeed_resources_;
// The total number of bytes for which ParseText is called.
int num_bytes_in_;
DebugFilter* debug_filter_;
scoped_ptr<FlushEarlyInfo> flush_early_info_;
scoped_ptr<FlushEarlyRenderInfo> flush_early_render_info_;
bool can_rewrite_resources_;
bool is_nested_;
// Additional request context that may outlive this RewriteDriver. (Thus,
// the context is reference counted.)
RequestContextPtr request_context_;
// Start time for HTML requests. Used for statistics reporting.
int64 start_time_ms_;
scoped_ptr<RequestProperties> request_properties_;
// Helps make sure RewriteDriver and its children are initialized exactly
// once, allowing for multiple calls to RewriteDriver::Initialize as long
// as they are matched to RewriteDriver::Terminate.
static int initialized_count_;
// True if this RewriteDriver attempted to distribute the rewrite. This is
// used to prevent a second attempt in case the first errored out.
bool tried_to_distribute_fetch_;
// If false, add data-pagespeed-no-defer attribute to the script inserted by
// add_instrumentation filter.
bool defer_instrumentation_script_;
// Downstream cache object used for issuing purges.
DownstreamCachePurger downstream_cache_purger_;
// Any PageSpeed options stripped from the original URL.
GoogleString pagespeed_query_params_;
// Any PageSpeed option cookies from the original request.
GoogleString pagespeed_option_cookies_;
DISALLOW_COPY_AND_ASSIGN(RewriteDriver);
};
// Subclass of HTTPCache::Callback that incorporates a given RewriteOptions'
// invalidation policy.
class OptionsAwareHTTPCacheCallback : public HTTPCache::Callback {
public:
virtual ~OptionsAwareHTTPCacheCallback();
virtual bool IsCacheValid(const GoogleString& key,
const ResponseHeaders& headers);
virtual int64 OverrideCacheTtlMs(const GoogleString& key);
virtual ResponseHeaders::VaryOption RespectVaryOnResources() const;
// Validates the specified response for the URL, request, given the specified
// options. This is for checking if cache response can still be used, not for
// determining whether an entry should be written to an HTTP cache.
static bool IsCacheValid(const GoogleString& key,
const RewriteOptions& rewrite_options,
const RequestContextPtr& request_ctx,
const ResponseHeaders& headers);
protected:
// Sub-classes need to ensure that rewrite_options remains valid till
// Callback::Done finishes.
OptionsAwareHTTPCacheCallback(
const RewriteOptions* rewrite_options,
const RequestContextPtr& request_ctx);
private:
const RewriteOptions* rewrite_options_;
DISALLOW_COPY_AND_ASSIGN(OptionsAwareHTTPCacheCallback);
};
} // namespace net_instaweb
#endif // NET_INSTAWEB_REWRITER_PUBLIC_REWRITE_DRIVER_H_