| /* |
| * 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) |
| // |
| // Provides the ResourceCombiner class which helps implement filters that |
| // combine multiple resources, as well as the TimedBool, which is useful |
| // when figuring out how long results are valid for. |
| |
| #ifndef NET_INSTAWEB_REWRITER_PUBLIC_RESOURCE_COMBINER_H_ |
| #define NET_INSTAWEB_REWRITER_PUBLIC_RESOURCE_COMBINER_H_ |
| |
| #include "net/instaweb/rewriter/public/resource.h" |
| #include "net/instaweb/rewriter/public/server_context.h" |
| #include "net/instaweb/rewriter/public/url_partnership.h" |
| #include "pagespeed/kernel/base/basictypes.h" |
| #include "pagespeed/kernel/base/gtest_prod.h" |
| #include "pagespeed/kernel/base/string.h" |
| #include "pagespeed/kernel/base/string_util.h" |
| |
| namespace net_instaweb { |
| |
| struct ContentType; |
| class MessageHandler; |
| class OutputResource; |
| class RewriteDriver; |
| class RewriteFilter; |
| class Writer; |
| |
| // A boolean with an expiration date. |
| struct TimedBool { |
| // A date, in milliseconds since the epoch, after which value should |
| // no longer be considered valid. |
| int64 expiration_ms; |
| bool value; |
| }; |
| |
| // This class is a utility for filters that combine multiple resource |
| // files into one. It provides two major pieces of functionality to help out: |
| // 1) It keeps a ResourceVector and provides methods to keep track |
| // of resources and URLs that can be safely combined together while |
| // encoding the information on the pieces in the combined URL. |
| // 2) It implements Fetch, reconstructing combinations as needed. |
| class ResourceCombiner { |
| public: |
| // Slack to leave in URL size, so that other filters running afterwards |
| // can expand the URLs without going over maximum allowed sizes. |
| // |
| // Why 100? First example I saw, CssFilter expanded a CssCombined URL |
| // by 36 chars. So 100 seemed like a nice round number to allow two |
| // filters to run after this and then for there still be a little slack. |
| // |
| // TODO(sligocki): Set this more intelligently. |
| static const int kUrlSlack = 100; |
| |
| // Note: extension should not include the leading dot here. |
| ResourceCombiner(RewriteDriver* rewrite_driver, |
| const StringPiece& extension, |
| RewriteFilter* filter); |
| |
| virtual ~ResourceCombiner(); |
| |
| // Resets the current combiner to an empty state, incorporating the base URL. |
| // Make sure this gets called before documents --- on a ::Flush() is enough. |
| // If a subclass needs to do some of its own reseting, see Clear(). |
| void Reset(); |
| |
| // Computes a name for the URL that meets all known character-set and |
| // size restrictions. |
| GoogleString UrlSafeId() const; |
| |
| // Returns the number of URLs that have been successfully added. |
| int num_urls() const { return partnership_.num_urls(); } |
| |
| const ResourceVector& resources() const { return resources_; } |
| |
| // Base common to all URLs. Always has a trailing slash. |
| GoogleString ResolvedBase() const { return partnership_.ResolvedBase(); } |
| |
| // TODO(jmarantz): rename this to AddResource now that async flow is live. |
| TimedBool AddResourceNoFetch(const ResourcePtr& resource, |
| MessageHandler* handler); |
| |
| protected: |
| // Removes the last resource that was added here, assuming the last call to |
| // AddResource was successful. If the last call to AddResource returned |
| // false, behavior is undefined. |
| virtual void RemoveLastResource(); |
| |
| // Returns one resource containing the combination of all added resources, |
| // creating it if necessary. Caller takes ownership. Returns NULL if the |
| // combination does not exist and cannot be created. Will not combine fewer |
| // than 2 resources. |
| OutputResourcePtr Combine(MessageHandler* handler); |
| |
| // Override this if your combination is not a matter of combining |
| // text pieces (perhaps adjusted by WritePiece). |
| virtual bool WriteCombination(const ResourceVector& combine_resources, |
| const OutputResourcePtr& combination, |
| MessageHandler* handler); |
| |
| // Override this to alter how pieces are processed when included inside |
| // a combination. Returns whether successful. The default implementation |
| // writes input->contents() to the writer without any alteration. |
| // 'index' is the position of this piece in the combination, while |
| // num_pieces is the total number of pieces. |
| virtual bool WritePiece(int index, int num_pieces, const Resource* input, |
| OutputResource* combination, Writer* writer, |
| MessageHandler* handler); |
| |
| // Override this if you need to remove some state whenever Reset() is called. |
| // Your implementation must call the superclass. |
| virtual void Clear(); |
| |
| ServerContext* const server_context_; |
| RewriteDriver* const rewrite_driver_; |
| |
| private: |
| friend class AggregateCombiner; |
| |
| // Implement this to control the content-type given the combination. |
| virtual const ContentType* CombinationContentType() = 0; |
| |
| // Recomputes the leaf size if our base has changed |
| void UpdateResolvedBase(); |
| |
| // Computes the total size |
| void ComputeLeafSize(); |
| |
| // Incrementally updates the accumulated leaf size without re-examining |
| // every element in the combined file. |
| void AccumulateLeafSize(const StringPiece& url); |
| |
| // Determines whether our accumulated leaf size is too big, taking into |
| // account both per-segment and total-url limitations. |
| bool UrlTooBig(); |
| |
| // Computes the total combined resources size. |
| // Override this if combined resource size to be restricted to some limit. |
| virtual void AccumulateCombinedSize(const ResourcePtr& resource) {} |
| |
| // Determines whether our accumulated resources size is too big. |
| // Override this if combined resource size to be restricted to some limit. |
| virtual bool ContentSizeTooBig() const { return false; } |
| |
| // Override this if you need to forbid some combinations based on the |
| // content of the resource (e.g. with resource->HttpStatusOk()) |
| // This is called before the URL is added to UrlPartnership's |
| // data structures. If the method returns false, it should set |
| // *failure_reason to a user-comprehensible explanation. |
| virtual bool ResourceCombinable(Resource* resource, |
| GoogleString* failure_reason, |
| MessageHandler* handler); |
| |
| UrlPartnership partnership_; |
| ResourceVector resources_; |
| StringVector multipart_encoder_urls_; |
| int prev_num_components_; |
| int accumulated_leaf_size_; |
| GoogleString resolved_base_; |
| const int url_overhead_; |
| RewriteFilter* filter_; |
| |
| FRIEND_TEST(ResourceCombinerTest, TestRemove); |
| FRIEND_TEST(ResourceCombinerTest, TestRemoveFrom3); |
| FRIEND_TEST(ResourceCombinerTest, TestRebaseRemove); |
| FRIEND_TEST(ResourceCombinerTest, TestRebaseRemoveAdd); |
| |
| DISALLOW_COPY_AND_ASSIGN(ResourceCombiner); |
| }; |
| |
| } // namespace net_instaweb |
| |
| #endif // NET_INSTAWEB_REWRITER_PUBLIC_RESOURCE_COMBINER_H_ |