blob: 536fb404a15220e2cfed33cbe8f5b6ba80c5f246 [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)
//
// 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_