| /* |
| * Copyright 2012 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: mdw@google.com (Matt Welsh) |
| |
| // Unit-tests for ProxyFetch and related classes |
| |
| #include "pagespeed/automatic/proxy_fetch.h" |
| |
| #include "base/logging.h" |
| #include "net/instaweb/http/public/log_record.h" |
| #include "net/instaweb/http/public/logging_proto_impl.h" |
| #include "net/instaweb/http/public/mock_callback.h" |
| #include "net/instaweb/http/public/request_context.h" |
| #include "net/instaweb/rewriter/public/rewrite_driver.h" |
| #include "net/instaweb/rewriter/public/rewrite_options.h" |
| #include "net/instaweb/rewriter/public/rewrite_test_base.h" |
| #include "net/instaweb/rewriter/public/server_context.h" |
| #include "pagespeed/kernel/base/abstract_mutex.h" |
| #include "pagespeed/kernel/base/function.h" |
| #include "pagespeed/kernel/base/gtest.h" |
| #include "pagespeed/kernel/base/message_handler.h" |
| #include "pagespeed/kernel/base/null_message_handler.h" |
| #include "pagespeed/kernel/base/scoped_ptr.h" |
| #include "pagespeed/kernel/base/thread_system.h" |
| #include "pagespeed/kernel/html/html_parse_test_base.h" |
| #include "pagespeed/kernel/http/http_names.h" |
| #include "pagespeed/kernel/http/request_headers.h" |
| #include "pagespeed/kernel/http/response_headers.h" |
| #include "pagespeed/kernel/thread/mock_scheduler.h" |
| #include "pagespeed/kernel/thread/thread_synchronizer.h" |
| #include "pagespeed/kernel/thread/worker_test_base.h" |
| #include "pagespeed/kernel/util/platform.h" |
| |
| namespace net_instaweb { |
| |
| // A stripped-down mock of ProxyFetch, used for testing PropertyCacheComplete(). |
| class MockProxyFetch : public ProxyFetch { |
| public: |
| MockProxyFetch(AsyncFetch* async_fetch, |
| ProxyFetchFactory* factory, |
| ServerContext* server_context) |
| : ProxyFetch("http://www.google.com", false, |
| NULL, // callback |
| async_fetch, |
| NULL, // no original content fetch |
| GetNewRewriteDriver(server_context, async_fetch), |
| server_context, |
| NULL, // timer |
| factory), |
| complete_(false) { |
| response_headers()->set_status_code(HttpStatus::kOK); |
| } |
| |
| ~MockProxyFetch() { } |
| |
| static RewriteDriver* GetNewRewriteDriver(ServerContext* server_context, |
| AsyncFetch* async_fetch) { |
| RewriteDriver* driver = server_context->NewRewriteDriver( |
| async_fetch->request_context()); |
| RequestHeaders headers; |
| driver->SetRequestHeaders(headers); |
| return driver; |
| } |
| |
| void PropertyCacheComplete( |
| ProxyFetchPropertyCallbackCollector* callback_collector) { |
| complete_ = true; |
| } |
| |
| void Done(bool success) { |
| HandleDone(success); |
| } |
| |
| bool complete() const { return complete_; } |
| |
| RewriteDriver* driver() const { return driver_; } |
| |
| private: |
| bool complete_; |
| DISALLOW_COPY_AND_ASSIGN(MockProxyFetch); |
| }; |
| |
| // A wrapper around MockProxyFetch that manages all objects on the heap and |
| // initializes things for no parsing and then parses a token script. You must |
| // NOT call Done() on the MockProxyFetch object as we do it in our destructor. |
| class ManagedMockProxyFetch { |
| public: |
| ManagedMockProxyFetch(ServerContext* server_context, |
| bool allow_options_to_be_set_by_cookies, |
| StringPiece sticky_query_parameters, |
| StringPiece sticky_query_parameters_token, |
| StringPiece query_params, StringPiece option_cookies) { |
| server_context->global_options()->ClearSignatureForTesting(); |
| server_context->global_options()->set_allow_options_to_be_set_by_cookies( |
| allow_options_to_be_set_by_cookies); |
| server_context->global_options()->set_sticky_query_parameters( |
| sticky_query_parameters); |
| // Suppresses parsing and the invocation of mock_proxy_fetch_->Done(). |
| server_context->global_options()->set_max_html_parse_bytes(0L); |
| server_context->global_options()->ComputeSignature(); |
| message_handler_.reset(new NullMessageHandler()); |
| async_fetch_.reset(new StringAsyncFetch( |
| RequestContext::NewTestRequestContext( |
| server_context->thread_system()))); |
| async_fetch_->response_headers()->Add("Content-Type", "text/html"); |
| async_fetch_->response_headers()->ComputeCaching(); |
| async_fetch_->request_context()->set_sticky_query_parameters_token( |
| sticky_query_parameters_token); |
| fetch_factory_.reset(new ProxyFetchFactory(server_context)); |
| mock_proxy_fetch_ = new MockProxyFetch(async_fetch_.get(), |
| fetch_factory_.get(), |
| server_context); |
| mock_proxy_fetch_->driver()->set_pagespeed_query_params(query_params); |
| mock_proxy_fetch_->driver()->set_pagespeed_option_cookies(option_cookies); |
| mock_proxy_fetch_->Write("<html>HTML</html>.", message_handler_.get()); |
| mock_proxy_fetch_->Flush(message_handler_.get()); |
| } |
| |
| ~ManagedMockProxyFetch() { |
| mock_proxy_fetch_->Done(true); |
| } |
| |
| MockProxyFetch* mock_proxy_fetch() const { return mock_proxy_fetch_; } |
| |
| private: |
| scoped_ptr<NullMessageHandler> message_handler_; |
| scoped_ptr<StringAsyncFetch> async_fetch_; |
| scoped_ptr<ProxyFetchFactory> fetch_factory_; |
| MockProxyFetch* mock_proxy_fetch_; // Not scoped_ptr as it self-deletes. |
| }; |
| |
| class ProxyFetchPropertyCallbackCollectorTest : public RewriteTestBase { |
| public: |
| void PostLookupTask() { |
| post_lookup_called_ = true; |
| } |
| void CheckPageNotNullPostLookupTask( |
| ProxyFetchPropertyCallbackCollector* collector, |
| WorkerTestBase::SyncPoint* sync_point) { |
| EXPECT_TRUE(collector->fallback_property_page() != NULL); |
| sync_point->Notify(); |
| } |
| |
| void AddCheckPageNotNullPostLookupTask( |
| ProxyFetchPropertyCallbackCollector* collector, |
| WorkerTestBase::SyncPoint* sync_point) { |
| collector->AddPostLookupTask(MakeFunction( |
| this, |
| &ProxyFetchPropertyCallbackCollectorTest:: |
| CheckPageNotNullPostLookupTask, |
| collector, |
| sync_point)); |
| } |
| |
| protected: |
| ProxyFetchPropertyCallbackCollectorTest() : |
| thread_system_(Platform::CreateThreadSystem()), |
| server_context_(server_context()), |
| post_lookup_called_(false) { |
| } |
| |
| scoped_ptr<ThreadSystem> thread_system_; |
| ServerContext* server_context_; |
| |
| // Create a collector. |
| ProxyFetchPropertyCallbackCollector* MakeCollector() { |
| ProxyFetchPropertyCallbackCollector* collector = |
| new ProxyFetchPropertyCallbackCollector( |
| server_context_, RewriteTestBase::kTestDomain, |
| RequestContext::NewTestRequestContext(thread_system_.get()), |
| options(), UserAgentMatcher::kDesktop); |
| // Collector should not contain any PropertyPages |
| EXPECT_EQ(NULL, collector->ReleasePropertyPage( |
| ProxyFetchPropertyCallback::kPropertyCachePage)); |
| |
| return collector; |
| } |
| |
| void EnableCollectorPrefix() { |
| ThreadSynchronizer* sync = server_context()->thread_synchronizer(); |
| sync->EnableForPrefix(ProxyFetch::kCollectorDoneFinish); |
| sync->EnableForPrefix(ProxyFetch::kCollectorDetachFinish); |
| sync->EnableForPrefix(ProxyFetch::kCollectorConnectProxyFetchFinish); |
| sync->EnableForPrefix(ProxyFetch::kCollectorRequestHeadersCompleteFinish); |
| } |
| |
| // Add a callback to the collector. |
| ProxyFetchPropertyCallback* AddCallback( |
| ProxyFetchPropertyCallbackCollector* collector, |
| ProxyFetchPropertyCallback::PageType page_type) { |
| AbstractMutex* mutex = thread_system_->NewMutex(); |
| DCHECK(page_type == ProxyFetchPropertyCallback::kPropertyCachePage); |
| ProxyFetchPropertyCallback* callback = |
| new ProxyFetchPropertyCallback( |
| page_type, page_property_cache(), RewriteTestBase::kTestDomain, |
| "hash", UserAgentMatcher::kDesktop, collector, mutex); |
| EXPECT_EQ(page_type, callback->page_type()); |
| collector->AddCallback(callback); |
| return callback; |
| } |
| |
| void AddPostLookupConnectProxyFetchCallDone( |
| ProxyFetchPropertyCallbackCollector* collector, |
| MockProxyFetch* mock_proxy_fetch, |
| ProxyFetchPropertyCallback* callback) { |
| collector->AddPostLookupTask(MakeFunction( |
| this, &ProxyFetchPropertyCallbackCollectorTest::PostLookupTask)); |
| collector->ConnectProxyFetch(mock_proxy_fetch); |
| callback->Done(true); |
| } |
| |
| void ConnectProxyFetchAddPostLookupCallDone( |
| ProxyFetchPropertyCallbackCollector* collector, |
| MockProxyFetch* mock_proxy_fetch, |
| ProxyFetchPropertyCallback* callback) { |
| collector->ConnectProxyFetch(mock_proxy_fetch); |
| collector->AddPostLookupTask(MakeFunction( |
| this, &ProxyFetchPropertyCallbackCollectorTest::PostLookupTask)); |
| callback->Done(true); |
| } |
| |
| void CallDoneAddPostLookupConnectProxyFetch( |
| ProxyFetchPropertyCallbackCollector* collector, |
| MockProxyFetch* mock_proxy_fetch, |
| ProxyFetchPropertyCallback* callback) { |
| callback->Done(true); |
| collector->AddPostLookupTask(MakeFunction( |
| this, &ProxyFetchPropertyCallbackCollectorTest::PostLookupTask)); |
| collector->ConnectProxyFetch(mock_proxy_fetch); |
| } |
| |
| void TestAddPostlookupTask(bool add_before_done, |
| bool add_before_proxy_fetch) { |
| EnableCollectorPrefix(); |
| scoped_ptr<ProxyFetchPropertyCallbackCollector> collector; |
| collector.reset(MakeCollector()); |
| ProxyFetchPropertyCallback* page_callback = AddCallback( |
| collector.get(), ProxyFetchPropertyCallback::kPropertyCachePage); |
| ExpectStringAsyncFetch async_fetch( |
| true, RequestContext::NewTestRequestContext(thread_system_.get())); |
| ProxyFetchFactory factory(server_context_); |
| MockProxyFetch* mock_proxy_fetch = new MockProxyFetch( |
| &async_fetch, &factory, server_context_); |
| if (add_before_done && add_before_proxy_fetch) { |
| AddPostLookupConnectProxyFetchCallDone( |
| collector.get(), mock_proxy_fetch, page_callback); |
| } else if (add_before_done && !add_before_proxy_fetch) { |
| ConnectProxyFetchAddPostLookupCallDone( |
| collector.get(), mock_proxy_fetch, page_callback); |
| } else if (!add_before_done && add_before_proxy_fetch) { |
| CallDoneAddPostLookupConnectProxyFetch( |
| collector.get(), mock_proxy_fetch, page_callback); |
| } else { |
| // Not handled. Make this fail. |
| } |
| collector->RequestHeadersComplete(); |
| EXPECT_TRUE(post_lookup_called_); |
| mock_proxy_fetch->Done(true); |
| } |
| |
| private: |
| bool post_lookup_called_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ProxyFetchPropertyCallbackCollectorTest); |
| }; |
| |
| |
| // Test fixture for ProxyFetch. |
| class ProxyFetchTest : public RewriteTestBase { |
| }; |
| |
| TEST_F(ProxyFetchTest, TestInhibitParsing) { |
| NullMessageHandler handler; |
| server_context()->global_options()->ClearSignatureForTesting(); |
| server_context()->global_options()->set_max_html_parse_bytes(0L); |
| server_context()->global_options()->ComputeSignature(); |
| StringAsyncFetch fetch( |
| RequestContext::NewTestRequestContext( |
| server_context()->thread_system())); |
| fetch.response_headers()->Add("Content-Type", "text/html"); |
| fetch.response_headers()->ComputeCaching(); |
| ProxyFetchFactory factory(server_context_); |
| MockProxyFetch* mock_proxy_fetch = new MockProxyFetch( |
| &fetch, &factory, server_context()); |
| mock_proxy_fetch->Write("<html>HTML</html>.", &handler); |
| mock_proxy_fetch->Flush(&handler); |
| |
| // We never parsed the HTML, but we did log HTML content type. |
| EXPECT_FALSE(mock_proxy_fetch->started_parse_); |
| { |
| AbstractLogRecord* log_record = |
| mock_proxy_fetch->request_context()->log_record(); |
| ScopedMutex lock(log_record->mutex()); |
| LoggingInfo* info = log_record->logging_info(); |
| EXPECT_TRUE(info->is_html_response()); |
| } |
| |
| mock_proxy_fetch->Done(true); |
| } |
| |
| namespace { |
| |
| class FlushLoggingStringAsyncFetch : public StringAsyncFetch { |
| public: |
| explicit FlushLoggingStringAsyncFetch( |
| const RequestContextPtr& request_context) |
| : StringAsyncFetch(request_context) {} |
| |
| virtual ~FlushLoggingStringAsyncFetch() {} |
| |
| bool HandleFlush(MessageHandler* handler) override { |
| HandleWrite("|Flush|", handler); |
| return true; |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(FlushLoggingStringAsyncFetch); |
| }; |
| |
| } // namespace |
| |
| TEST_F(ProxyFetchTest, TestFollowFlushes) { |
| NullMessageHandler handler; |
| RewriteOptions* options = server_context()->global_options(); |
| options->ClearSignatureForTesting(); |
| options->DisableFilter(RewriteOptions::kAddHead); |
| options->set_follow_flushes(true); |
| options->ComputeSignature(); |
| FlushLoggingStringAsyncFetch fetch( |
| RequestContext::NewTestRequestContext( |
| server_context()->thread_system())); |
| fetch.response_headers()->Add("Content-Type", "text/html"); |
| ProxyFetchFactory factory(server_context_); |
| MockProxyFetch* mock_proxy_fetch = new MockProxyFetch( |
| &fetch, &factory, server_context()); |
| mock_proxy_fetch->response_headers()->ComputeCaching(); |
| |
| // To get a more predictable output with regard to flush markers, we wait |
| // for flushes to be fully processed before continuing. ProxyFetch may |
| // otherwise aggregate, reposition, or ignore induced flushes. |
| mock_proxy_fetch->Write("<html><d>1</d>", &handler); |
| mock_proxy_fetch->Flush(&handler); |
| mock_scheduler()->AwaitQuiescence(); |
| mock_proxy_fetch->Write("<d>2</d></html>", &handler); |
| mock_proxy_fetch->Flush(&handler); |
| mock_scheduler()->AwaitQuiescence(); |
| |
| // Verify we are parsing HTML. |
| EXPECT_TRUE(mock_proxy_fetch->started_parse_); |
| |
| mock_proxy_fetch->Done(true); |
| mock_scheduler()->AwaitQuiescence(); |
| EXPECT_EQ(0, server_context()->num_active_rewrite_drivers()); |
| EXPECT_EQ("<html><d>1</d>|Flush|<d>2</d></html>|Flush||Flush|", |
| fetch.buffer()); |
| |
| // Test following flushes off behaves as expected. |
| options->ClearSignatureForTesting(); |
| options->set_follow_flushes(false); |
| options->ComputeSignature(); |
| |
| FlushLoggingStringAsyncFetch fetch_off( |
| RequestContext::NewTestRequestContext( |
| server_context()->thread_system())); |
| fetch_off.response_headers()->Add("Content-Type", "text/html"); |
| |
| mock_proxy_fetch = new MockProxyFetch( |
| &fetch_off, &factory, server_context()); |
| mock_proxy_fetch->response_headers()->ComputeCaching(); |
| |
| mock_proxy_fetch->Write("<html><d>1</d>", &handler); |
| mock_proxy_fetch->Flush(&handler); |
| mock_scheduler()->AwaitQuiescence(); |
| mock_proxy_fetch->Write("<d>2</d></html>", &handler); |
| mock_proxy_fetch->Flush(&handler); |
| mock_scheduler()->AwaitQuiescence(); |
| |
| EXPECT_TRUE(mock_proxy_fetch->started_parse_); |
| |
| mock_proxy_fetch->Done(true); |
| mock_scheduler()->AwaitQuiescence(); |
| EXPECT_EQ(0, server_context()->num_active_rewrite_drivers()); |
| EXPECT_EQ("<html><d>1</d><d>2</d></html>|Flush|", fetch_off.buffer()); |
| } |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, EmptyCollectorTest) { |
| // Test that creating an empty collector works. |
| EnableCollectorPrefix(); |
| scoped_ptr<ProxyFetchPropertyCallbackCollector> collector; |
| collector.reset(MakeCollector()); |
| // This should not fail |
| collector->Detach(HttpStatus::kUnknownStatusCode); |
| } |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, DoneBeforeDetach) { |
| EnableCollectorPrefix(); |
| // Test that calling Done() before Detach() works. |
| ProxyFetchPropertyCallbackCollector* collector = MakeCollector(); |
| ProxyFetchPropertyCallback* callback = AddCallback( |
| collector, ProxyFetchPropertyCallback::kPropertyCachePage); |
| |
| // callback->IsCacheValid could be called anytime before callback->Done. |
| // Will return true because there are no cache invalidation URL patterns. |
| EXPECT_TRUE(callback->IsCacheValid(1L)); |
| |
| // Invoke the callback. |
| callback->Done(true); |
| |
| // Collector should now have a page property. |
| scoped_ptr<AbstractPropertyPage> page; |
| page.reset(collector->ReleaseFallbackPropertyPage()); |
| EXPECT_TRUE(NULL != page.get()); |
| |
| // This should not fail - will also delete the collector. |
| collector->Detach(HttpStatus::kUnknownStatusCode); |
| } |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, UrlInvalidDoneBeforeDetach) { |
| EnableCollectorPrefix(); |
| // Invalidate all URLs cached before timestamp 2. |
| options_->AddUrlCacheInvalidationEntry("*", 2L, true); |
| // Test that calling Done() before Detach() works. |
| ProxyFetchPropertyCallbackCollector* collector = MakeCollector(); |
| ProxyFetchPropertyCallback* callback = AddCallback( |
| collector, ProxyFetchPropertyCallback::kPropertyCachePage); |
| |
| // callback->IsCacheValid could be called anytime before callback->Done. |
| // Will return false due to the invalidation entry. |
| EXPECT_FALSE(callback->IsCacheValid(1L)); |
| |
| // Invoke the callback. |
| callback->Done(true); |
| |
| // Collector should now have a page property. |
| scoped_ptr<AbstractPropertyPage> page; |
| page.reset(collector->ReleaseFallbackPropertyPage()); |
| EXPECT_TRUE(NULL != page.get()); |
| |
| // This should not fail - will also delete the collector. |
| collector->Detach(HttpStatus::kUnknownStatusCode); |
| } |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, DetachBeforeDone) { |
| EnableCollectorPrefix(); |
| // Test that calling Detach() before Done() works. |
| ProxyFetchPropertyCallbackCollector* collector = MakeCollector(); |
| collector->RequestHeadersComplete(); |
| ProxyFetchPropertyCallback* callback = AddCallback( |
| collector, ProxyFetchPropertyCallback::kPropertyCachePage); |
| |
| // callback->IsCacheValid could be called anytime before callback->Done. |
| // Will return true because there are no cache invalidation URL patterns. |
| EXPECT_TRUE(callback->IsCacheValid(1L)); |
| |
| // Will not delete the collector since we did not call Done yet. |
| collector->Detach(HttpStatus::kUnknownStatusCode); |
| |
| // This call is after Detach (but before callback->Done). |
| // ProxyFetchPropertyCallbackCollector::IsCacheValid returns false if |
| // detached. |
| EXPECT_FALSE(callback->IsCacheValid(1L)); |
| |
| // This will delete the collector. |
| callback->Done(true); |
| } |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, DoneBeforeSetProxyFetch) { |
| EnableCollectorPrefix(); |
| // Test that calling Done() before SetProxyFetch() works. |
| scoped_ptr<ProxyFetchPropertyCallbackCollector> collector; |
| collector.reset(MakeCollector()); |
| ProxyFetchPropertyCallback* callback = AddCallback( |
| collector.get(), ProxyFetchPropertyCallback::kPropertyCachePage); |
| |
| // callback->IsCacheValid could be called anytime before callback->Done. |
| // Will return true because there are no cache invalidation URL patterns. |
| EXPECT_TRUE(callback->IsCacheValid(1L)); |
| |
| // Invoke the callback. |
| callback->Done(true); |
| |
| // Construct mock ProxyFetch to test SetProxyFetch(). |
| ExpectStringAsyncFetch async_fetch( |
| true, RequestContext::NewTestRequestContext(thread_system_.get())); |
| ProxyFetchFactory factory(server_context_); |
| MockProxyFetch* mock_proxy_fetch = new MockProxyFetch( |
| &async_fetch, &factory, server_context_); |
| |
| // Should not be complete since SetProxyFetch() called first. |
| EXPECT_FALSE(mock_proxy_fetch->complete()); |
| |
| // Collector should now have a page property. |
| scoped_ptr<AbstractPropertyPage> page; |
| page.reset(collector->ReleaseFallbackPropertyPage()); |
| EXPECT_TRUE(NULL != page.get()); |
| |
| collector.get()->ConnectProxyFetch(mock_proxy_fetch); |
| // Should be complete since SetProxyFetch() called after Done(). |
| EXPECT_TRUE(mock_proxy_fetch->complete()); |
| |
| // Needed for cleanup. |
| mock_proxy_fetch->Done(true); |
| } |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, SetProxyFetchBeforeDone) { |
| EnableCollectorPrefix(); |
| // Test that calling SetProxyFetch() before Done() works. |
| scoped_ptr<ProxyFetchPropertyCallbackCollector> collector; |
| collector.reset(MakeCollector()); |
| ProxyFetchPropertyCallback* callback = AddCallback( |
| collector.get(), ProxyFetchPropertyCallback::kPropertyCachePage); |
| |
| // Construct mock ProxyFetch to test SetProxyFetch(). |
| ExpectStringAsyncFetch async_fetch( |
| true, RequestContext::NewTestRequestContext(thread_system_.get())); |
| ProxyFetchFactory factory(server_context_); |
| MockProxyFetch* mock_proxy_fetch = new MockProxyFetch( |
| &async_fetch, &factory, server_context_); |
| |
| // callback->IsCacheValid could be called anytime before callback->Done. |
| // Will return true because there are no cache invalidation URL patterns. |
| EXPECT_TRUE(callback->IsCacheValid(1L)); |
| |
| collector.get()->ConnectProxyFetch(mock_proxy_fetch); |
| // Should not be complete since SetProxyFetch() called first. |
| EXPECT_FALSE(mock_proxy_fetch->complete()); |
| |
| EXPECT_TRUE(callback->IsCacheValid(1L)); |
| |
| // Now invoke the callback. |
| callback->Done(true); |
| |
| // Collector should now have a page property. |
| scoped_ptr<AbstractPropertyPage> page; |
| page.reset(collector->ReleaseFallbackPropertyPage()); |
| EXPECT_TRUE(NULL != page.get()); |
| |
| // Not yet complete since RequestHeadersComplete() not called yet. |
| EXPECT_FALSE(mock_proxy_fetch->complete()); |
| |
| collector->RequestHeadersComplete(); |
| |
| // Should be complete since both Done() and RequestHeadersComplete() called. |
| EXPECT_TRUE(mock_proxy_fetch->complete()); |
| |
| // Needed for cleanup. |
| mock_proxy_fetch->Done(true); |
| } |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, PostLookupProxyFetchDone) { |
| TestAddPostlookupTask(true, true); |
| } |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, DonePostLookupProxyFetch) { |
| TestAddPostlookupTask(false, true); |
| } |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, ProxyFetchPostLookupDone) { |
| TestAddPostlookupTask(true, false); |
| } |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, FallbackPagePostLookupRace) { |
| EnableCollectorPrefix(); |
| // This test will check PostLookup tasks should not have Null |
| // fallback_property_page. |
| scoped_ptr<ProxyFetchPropertyCallbackCollector> collector; |
| collector.reset(MakeCollector()); |
| collector->RequestHeadersComplete(); |
| ProxyFetchPropertyCallback* page_callback = AddCallback( |
| collector.get(), ProxyFetchPropertyCallback::kPropertyCachePage); |
| ExpectStringAsyncFetch async_fetch( |
| true, RequestContext::NewTestRequestContext(thread_system_.get())); |
| ProxyFetchFactory factory(server_context_); |
| MockProxyFetch* mock_proxy_fetch = new MockProxyFetch( |
| &async_fetch, &factory, server_context_); |
| |
| ThreadSystem* thread_system = server_context()->thread_system(); |
| QueuedWorkerPool pool(1, "test", thread_system); |
| QueuedWorkerPool::Sequence* sequence = pool.NewSequence(); |
| WorkerTestBase::SyncPoint sync_point(thread_system); |
| sequence->Add( |
| MakeFunction( |
| static_cast<ProxyFetchPropertyCallbackCollectorTest*>(this), |
| &ProxyFetchPropertyCallbackCollectorTest:: |
| AddCheckPageNotNullPostLookupTask, |
| collector.get(), &sync_point)); |
| page_callback->Done(true); |
| sync_point.Wait(); |
| collector->ConnectProxyFetch(mock_proxy_fetch); |
| mock_proxy_fetch->Done(true); |
| } |
| |
| |
| TEST_F(ProxyFetchPropertyCallbackCollectorTest, TestOptionsValid) { |
| RewriteOptions* options = new RewriteOptions(thread_system_.get()); |
| ProxyFetchPropertyCallbackCollector* collector = |
| new ProxyFetchPropertyCallbackCollector( |
| server_context_, |
| RewriteTestBase::kTestDomain, |
| RequestContext::NewTestRequestContext(thread_system_.get()), |
| options, |
| UserAgentMatcher::kDesktop); |
| ThreadSynchronizer* sync = server_context()->thread_synchronizer(); |
| sync->EnableForPrefix(ProxyFetch::kCollectorFinish); |
| sync->EnableForPrefix(ProxyFetch::kCollectorDetachStart); |
| sync->EnableForPrefix(ProxyFetch::kCollectorRequestHeadersCompleteFinish); |
| collector->RequestHeadersComplete(); |
| ProxyFetchPropertyCallback* page_callback = AddCallback( |
| collector, ProxyFetchPropertyCallback::kPropertyCachePage); |
| |
| collector->Detach(HttpStatus::kUnknownStatusCode); |
| delete options; |
| collector->IsCacheValid(1L); |
| sync->Signal(ProxyFetch::kCollectorDetachStart); |
| page_callback->Done(true); |
| sync->Wait(ProxyFetch::kCollectorFinish); |
| } |
| |
| TEST_F(ProxyFetchTest, TestStickyPageSpeedOptions) { |
| // The various options we set as cookies. |
| StringPieceVector pagespeed_options; |
| pagespeed_options.push_back("PageSpeedImageRecompressionQuality=77"); |
| pagespeed_options.push_back("ModPagespeedImageLimitOptimizedPercent=55"); |
| pagespeed_options.push_back("ModPagespeedCssInlineMaxBytes=19"); |
| GoogleString cookies = JoinCollection(pagespeed_options, "&"); |
| // Add the sticky query param as a QP to show that it's not set as a cookie. |
| GoogleString params = StrCat("PageSpeedStickyQueryParameters=on&", cookies); |
| // For producing the expected response headers cookies string. |
| const char kCookiesPrefix[] = "[\""; |
| const char kCookieSetExpires[] = "; Expires=Tue, 02 Feb 2010 19:01:26 GMT"; |
| const char kCookieClearExpires[] = "; Expires=Thu, 01 Jan 1970 00:00:00 GMT"; |
| const char kCookieAttrs[] = "; Domain=www.google.com; Path=/; HttpOnly"; |
| const char kCookiesSeparator[] = "\",\""; |
| const char kCookiesSuffix[] = "\"]"; |
| GoogleString current_cookies = |
| StrCat(kCookiesPrefix, |
| JoinCollection(pagespeed_options, StrCat(kCookieSetExpires, |
| kCookieAttrs, |
| kCookiesSeparator)), |
| kCookieSetExpires, kCookieAttrs, kCookiesSuffix); |
| GoogleString expired_cookies = |
| StrCat(kCookiesPrefix, |
| JoinCollection(pagespeed_options, StrCat(kCookieClearExpires, |
| kCookieAttrs, |
| kCookiesSeparator)), |
| kCookieClearExpires, kCookieAttrs, kCookiesSuffix); |
| // Strip out the values from the expiration cookies. Pardon the hackiness. |
| GlobalReplaceSubstring("=77;", ";", &expired_cookies); |
| GlobalReplaceSubstring("=55;", ";", &expired_cookies); |
| GlobalReplaceSubstring("=19;", ";", &expired_cookies); |
| |
| // 0. Allow options to be set by cookie but don't allow them to be sticky. |
| { |
| ManagedMockProxyFetch managed_mock(server_context_, true, "", "right value", |
| params, ""); |
| MockProxyFetch* mock_proxy_fetch = managed_mock.mock_proxy_fetch(); |
| GoogleString response_cookies; |
| EXPECT_FALSE(mock_proxy_fetch->response_headers()->GetCookieString( |
| &response_cookies)); |
| EXPECT_STREQ("", response_cookies); |
| } |
| // 1. Allow options to be set by cookie, allow them to be sticky, but don't |
| // ask for stickiness in the request. |
| { |
| ManagedMockProxyFetch managed_mock(server_context_, true, "right value", "", |
| params, ""); |
| MockProxyFetch* mock_proxy_fetch = managed_mock.mock_proxy_fetch(); |
| GoogleString response_cookies; |
| EXPECT_FALSE(mock_proxy_fetch->response_headers()->GetCookieString( |
| &response_cookies)); |
| EXPECT_STREQ("", response_cookies); |
| } |
| // 2. Don't allow options to be set by cookie, allow them to be made sticky, |
| // don't ask for stickiness in the request, but pass in some already-set |
| // cookies - these should be expired for us. |
| { |
| ManagedMockProxyFetch managed_mock(server_context_, false, "right value", |
| "", "", cookies); |
| MockProxyFetch* mock_proxy_fetch = managed_mock.mock_proxy_fetch(); |
| GoogleString response_cookies; |
| EXPECT_TRUE(mock_proxy_fetch->response_headers()->GetCookieString( |
| &response_cookies)); |
| EXPECT_STREQ(expired_cookies, response_cookies); |
| } |
| // 3. Allow options to be set by cookie, allow them to be made sticky, and |
| // ask for stickiness in the request. |
| { |
| ManagedMockProxyFetch managed_mock(server_context_, true, "right value", |
| "right value", params, ""); |
| MockProxyFetch* mock_proxy_fetch = managed_mock.mock_proxy_fetch(); |
| GoogleString response_cookies; |
| EXPECT_TRUE(mock_proxy_fetch->response_headers()->GetCookieString( |
| &response_cookies)); |
| EXPECT_STREQ(current_cookies, response_cookies); |
| } |
| // 4. Pass in some options-as-cookies without any query params or changes. |
| // We expect no cookies in the response as the browser has them already. |
| { |
| ManagedMockProxyFetch managed_mock(server_context_, true, "right value", "", |
| "", cookies); |
| MockProxyFetch* mock_proxy_fetch = managed_mock.mock_proxy_fetch(); |
| GoogleString response_cookies; |
| EXPECT_FALSE(mock_proxy_fetch->response_headers()->GetCookieString( |
| &response_cookies)); |
| EXPECT_STREQ("", response_cookies); |
| } |
| // 5. Explicitly ask for sticky options-by-cookies to be cleared. |
| { |
| ManagedMockProxyFetch managed_mock(server_context_, true, "right value", |
| "different value", "", cookies); |
| MockProxyFetch* mock_proxy_fetch = managed_mock.mock_proxy_fetch(); |
| GoogleString response_cookies; |
| EXPECT_TRUE(mock_proxy_fetch->response_headers()->GetCookieString( |
| &response_cookies)); |
| EXPECT_STREQ(expired_cookies, response_cookies); |
| } |
| } |
| |
| } // namespace net_instaweb |