blob: 35ad57206bcd48e55b5a7149089a9374948251c0 [file] [log] [blame]
/*
* Copyright 2011 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: mmohabey@google.com (Megha Mohabey)
#ifndef PAGESPEED_AUTOMATIC_PROXY_INTERFACE_TEST_BASE_H_
#define PAGESPEED_AUTOMATIC_PROXY_INTERFACE_TEST_BASE_H_
#include "net/instaweb/http/public/async_fetch.h"
#include "net/instaweb/http/public/url_async_fetcher.h"
#include "net/instaweb/rewriter/public/rewrite_test_base.h"
#include "net/instaweb/rewriter/public/test_rewrite_driver_factory.h"
#include "net/instaweb/rewriter/public/url_namer.h"
#include "pagespeed/automatic/proxy_interface.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/html/empty_html_filter.h"
#include "pagespeed/kernel/http/response_headers.h"
#include "pagespeed/kernel/thread/worker_test_base.h"
namespace net_instaweb {
class MockCriticalImagesFinder;
class GoogleUrl;
class HtmlElement;
class HtmlFilter;
class MessageHandler;
class PropertyValue;
class RequestHeaders;
class RewriteDriver;
class RewriteOptions;
const char kPageUrl[] = "page.html";
const char kBackgroundFetchHeader[] = "X-Background-Fetch";
// Creates a proxy URL naming rule that encodes an "owner" domain and an
// "origin" domain, all inside a fixed proxy-domain.
class ProxyUrlNamer : public UrlNamer {
public:
static const char kProxyHost[];
ProxyUrlNamer() : authorized_(true) {}
// Given the request_url, generate the original url.
virtual bool Decode(const GoogleUrl& gurl,
const RewriteOptions* rewrite_options,
GoogleUrl* domain,
GoogleString* decoded) const;
virtual bool IsAuthorized(const GoogleUrl& gurl,
const RewriteOptions& options) const {
return authorized_;
}
void set_authorized(bool authorized) { authorized_ = authorized; }
private:
bool authorized_;
DISALLOW_COPY_AND_ASSIGN(ProxyUrlNamer);
};
// Mock filter which gets passed to the new rewrite driver created in
// proxy_fetch.
//
// This is used to check the flow for injecting data into filters via the
// ProxyInterface, including:
// property_cache.
class MockFilter : public EmptyHtmlFilter {
public:
explicit MockFilter(RewriteDriver* driver)
: driver_(driver),
num_elements_(0),
num_elements_property_(NULL) {
}
virtual void StartDocument();
virtual void StartElement(HtmlElement* element);
virtual void EndDocument();
virtual const char* Name() const { return "MockFilter"; }
private:
RewriteDriver* driver_;
int num_elements_;
PropertyValue* num_elements_property_;
DISALLOW_COPY_AND_ASSIGN(MockFilter);
};
// Hook provided to TestRewriteDriverFactory to add a new filter when
// a rewrite_driver is created.
class CreateFilterCallback
: public TestRewriteDriverFactory::CreateFilterCallback {
public:
CreateFilterCallback() {}
virtual ~CreateFilterCallback() {}
virtual HtmlFilter* Done(RewriteDriver* driver) {
return new MockFilter(driver);
}
private:
DISALLOW_COPY_AND_ASSIGN(CreateFilterCallback);
};
// Subclass of AsyncFetch that adds a response header indicating whether the
// fetch is for a user-facing request, or a background rewrite.
class BackgroundFetchCheckingAsyncFetch : public SharedAsyncFetch {
public:
explicit BackgroundFetchCheckingAsyncFetch(AsyncFetch* base_fetch)
: SharedAsyncFetch(base_fetch),
async_fetch_(base_fetch) {}
virtual ~BackgroundFetchCheckingAsyncFetch() {}
virtual void HandleHeadersComplete() {
SharedAsyncFetch::HandleHeadersComplete();
response_headers()->Add(kBackgroundFetchHeader,
async_fetch_->IsBackgroundFetch() ? "1" : "0");
// Call ComputeCaching again since Add sets cache_fields_dirty_ to true.
response_headers()->ComputeCaching();
}
virtual void HandleDone(bool success) {
SharedAsyncFetch::HandleDone(success);
delete this;
}
private:
AsyncFetch* async_fetch_;
DISALLOW_COPY_AND_ASSIGN(BackgroundFetchCheckingAsyncFetch);
};
// Subclass of UrlAsyncFetcher that wraps the AsyncFetch with a
// BackgroundFetchCheckingAsyncFetch.
class BackgroundFetchCheckingUrlAsyncFetcher : public UrlAsyncFetcher {
public:
explicit BackgroundFetchCheckingUrlAsyncFetcher(UrlAsyncFetcher* fetcher)
: base_fetcher_(fetcher),
num_background_fetches_(0) {}
virtual ~BackgroundFetchCheckingUrlAsyncFetcher() {}
virtual void Fetch(const GoogleString& url,
MessageHandler* message_handler,
AsyncFetch* fetch) {
if (fetch->IsBackgroundFetch()) {
num_background_fetches_++;
}
BackgroundFetchCheckingAsyncFetch* new_fetch =
new BackgroundFetchCheckingAsyncFetch(fetch);
base_fetcher_->Fetch(url, message_handler, new_fetch);
}
int num_background_fetches() { return num_background_fetches_; }
void clear_num_background_fetches() { num_background_fetches_ = 0; }
private:
UrlAsyncFetcher* base_fetcher_;
int num_background_fetches_;
DISALLOW_COPY_AND_ASSIGN(BackgroundFetchCheckingUrlAsyncFetcher);
};
class ProxyInterfaceTestBase : public RewriteTestBase {
public:
void TestHeadersSetupRace();
protected:
static const int kHtmlCacheTimeSec = 5000;
ProxyInterfaceTestBase();
virtual void SetUp();
virtual void TearDown();
void FetchFromProxy(
const StringPiece& url,
const RequestHeaders& request_headers,
bool expect_success,
GoogleString* string_out,
ResponseHeaders* headers_out,
bool proxy_fetch_property_callback_collector_created);
void FetchFromProxy(const StringPiece& url,
const RequestHeaders& request_headers,
bool expect_success,
GoogleString* string_out,
ResponseHeaders* headers_out);
void FetchFromProxy(const StringPiece& url,
bool expect_success,
GoogleString* string_out,
ResponseHeaders* headers_out);
void FetchFromProxyLoggingFlushes(const StringPiece& url,
bool expect_success,
GoogleString* string_out);
void FetchFromProxyNoWait(const StringPiece& url,
const RequestHeaders& request_headers,
bool expect_success,
bool log_flush,
ResponseHeaders* headers_out);
void WaitForFetch(bool proxy_fetch_property_callback_collector_created);
void TestPropertyCache(const StringPiece& url,
bool delay_pcache, bool thread_pcache,
bool expect_success);
void TestPropertyCacheWithHeadersAndOutput(
const StringPiece& url, bool delay_pcache, bool thread_pcache,
bool expect_success, bool check_stats, bool add_create_filter_callback,
bool expect_detach_before_pcache, const RequestHeaders& request_headers,
ResponseHeaders* response_headers, GoogleString* output);
void SetCriticalImagesInFinder(StringSet* critical_images);
void SetCssCriticalImagesInFinder(StringSet* css_critical_images);
scoped_ptr<ProxyInterface> proxy_interface_;
scoped_ptr<WorkerTestBase::SyncPoint> sync_;
ResponseHeaders callback_response_headers_;
GoogleString callback_buffer_;
bool callback_done_value_;
private:
friend class FilterCallback;
MockCriticalImagesFinder* mock_critical_images_finder_;
};
} // namespace net_instaweb
#endif // PAGESPEED_AUTOMATIC_PROXY_INTERFACE_TEST_BASE_H_