| /* |
| * Copyright 2013 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: morlovich@google.com (Maksim Orlovich) |
| |
| #include "net/instaweb/rewriter/public/google_font_service_input_resource.h" |
| |
| #include "net/instaweb/http/public/counting_url_async_fetcher.h" |
| #include "net/instaweb/http/public/ua_sensitive_test_fetcher.h" |
| #include "net/instaweb/rewriter/public/mock_resource_callback.h" |
| #include "net/instaweb/rewriter/public/resource.h" |
| #include "net/instaweb/rewriter/public/rewrite_driver.h" |
| #include "net/instaweb/rewriter/public/rewrite_test_base.h" |
| #include "net/instaweb/rewriter/public/server_context.h" |
| #include "pagespeed/kernel/base/gtest.h" |
| #include "pagespeed/kernel/base/ref_counted_ptr.h" |
| #include "pagespeed/kernel/base/scoped_ptr.h" |
| #include "pagespeed/kernel/base/string.h" |
| #include "pagespeed/kernel/base/timer.h" |
| #include "pagespeed/kernel/http/content_type.h" |
| #include "pagespeed/kernel/http/google_url.h" |
| #include "pagespeed/kernel/http/response_headers.h" |
| |
| namespace net_instaweb { |
| |
| namespace { |
| |
| const char kRoboto[] = "http://fonts.googleapis.com/css?family=Roboto"; |
| const char kRobotoSsl[] = "https://fonts.googleapis.com/css?family=Roboto"; |
| const char kNonCss[] = "http://fonts.googleapis.com/some.txt"; |
| |
| class GoogleFontServiceInputResourceTest : public RewriteTestBase { |
| protected: |
| virtual void SetUp() { |
| RewriteTestBase::SetUp(); |
| GoogleFontServiceInputResource::InitStats(statistics()); |
| |
| // Font loader CSS gets Cache-Control:private, max-age=86400 |
| ResponseHeaders response_headers; |
| SetDefaultLongCacheHeaders(&kContentTypeCss, &response_headers); |
| response_headers.SetDateAndCaching( |
| timer()->NowMs(), 86400 * Timer::kSecondMs, ", private"); |
| |
| // Set them up in spots where UaSensitiveFetcher would direct them. |
| SetFetchResponse(StrCat(kRoboto, "&UA=Chromezilla"), |
| response_headers, "font_chromezilla"); |
| |
| SetFetchResponse(StrCat(kRoboto, "&UA=Safieri"), |
| response_headers, "font_safieri"); |
| |
| SetFetchResponse(StrCat(kRobotoSsl, "&UA=Chromezilla"), |
| response_headers, "sfont_chromezilla"); |
| |
| SetFetchResponse(StrCat(kRobotoSsl, "&UA=Safieri"), |
| response_headers, "sfont_safieri"); |
| |
| ResponseHeaders non_css; |
| SetDefaultLongCacheHeaders(&kContentTypeText, &non_css); |
| SetFetchResponse(StrCat(kNonCss, "?UA=Chromezilla"), |
| non_css, "something weird"); |
| } |
| |
| void ResetUserAgent(StringPiece user_agent) { |
| ClearRewriteDriver(); |
| rewrite_driver()->SetSessionFetcher( |
| new UserAgentSensitiveTestFetcher(rewrite_driver()->async_fetcher())); |
| SetCurrentUserAgent(user_agent); |
| SetDriverRequestHeaders(); |
| } |
| }; |
| |
| TEST_F(GoogleFontServiceInputResourceTest, Creation) { |
| ResetUserAgent("Chromezilla"); |
| |
| scoped_ptr<GoogleFontServiceInputResource> resource; |
| |
| GoogleUrl url1("efpeRO#@($@#K$!@($"); |
| resource.reset(GoogleFontServiceInputResource::Make(url1, rewrite_driver())); |
| EXPECT_TRUE(resource.get() == NULL); |
| |
| GoogleUrl url2("http://example.com/foo.css"); |
| resource.reset(GoogleFontServiceInputResource::Make(url2, rewrite_driver())); |
| EXPECT_TRUE(resource.get() == NULL); |
| |
| GoogleUrl url3(kRoboto); |
| resource.reset(GoogleFontServiceInputResource::Make(url3, rewrite_driver())); |
| ASSERT_TRUE(resource.get() != NULL); |
| EXPECT_EQ(kRoboto, resource->url()); |
| EXPECT_EQ( |
| "gfnt://fonts.googleapis.com/css?family=Roboto&X-PS-UA=Chromezilla", |
| resource->cache_key()); |
| |
| GoogleUrl url4(kRobotoSsl); |
| resource.reset(GoogleFontServiceInputResource::Make(url4, rewrite_driver())); |
| ASSERT_TRUE(resource.get() != NULL); |
| EXPECT_EQ(kRobotoSsl, resource->url()); |
| EXPECT_EQ( |
| "gfnts://fonts.googleapis.com/css?family=Roboto&X-PS-UA=Chromezilla", |
| resource->cache_key()); |
| } |
| |
| TEST_F(GoogleFontServiceInputResourceTest, Load) { |
| GoogleUrl url(kRoboto); |
| ResetUserAgent("Chromezilla"); |
| |
| ResourcePtr resource( |
| GoogleFontServiceInputResource::Make(url, rewrite_driver())); |
| ASSERT_TRUE(resource.get() != NULL); |
| MockResourceCallback callback(resource, |
| server_context()->thread_system()); |
| resource->LoadAsync(Resource::kReportFailureIfNotCacheable, |
| rewrite_driver()->request_context(), |
| &callback); |
| EXPECT_TRUE(callback.done()); |
| EXPECT_TRUE(callback.success()); |
| EXPECT_EQ("font_chromezilla", resource->ExtractUncompressedContents()); |
| EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count()); |
| |
| // Make sure it's cached. |
| ResourcePtr resource2( |
| GoogleFontServiceInputResource::Make(url, rewrite_driver())); |
| ASSERT_TRUE(resource2.get() != NULL); |
| MockResourceCallback callback2(resource2, |
| server_context()->thread_system()); |
| resource2->LoadAsync(Resource::kReportFailureIfNotCacheable, |
| rewrite_driver()->request_context(), |
| &callback2); |
| EXPECT_TRUE(callback2.done()); |
| EXPECT_TRUE(callback2.success()); |
| EXPECT_EQ("font_chromezilla", resource2->ExtractUncompressedContents()); |
| EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count()); |
| |
| // But that different UA gets different string. |
| ResetUserAgent("Safieri"); |
| ResourcePtr resource3( |
| GoogleFontServiceInputResource::Make(url, rewrite_driver())); |
| ASSERT_TRUE(resource3.get() != NULL); |
| MockResourceCallback callback3(resource3, |
| server_context()->thread_system()); |
| resource3->LoadAsync(Resource::kReportFailureIfNotCacheable, |
| rewrite_driver()->request_context(), |
| &callback3); |
| EXPECT_TRUE(callback3.done()); |
| EXPECT_TRUE(callback3.success()); |
| EXPECT_EQ("font_safieri", resource3->ExtractUncompressedContents()); |
| EXPECT_EQ(2, counting_url_async_fetcher()->fetch_count()); |
| } |
| |
| TEST_F(GoogleFontServiceInputResourceTest, UANormalization) { |
| const char kIE7a[] = |
| "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; " |
| ".NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"; |
| |
| GoogleUrl url(kRoboto); |
| scoped_ptr<GoogleUrl> url_plus_ua(url.CopyAndAddQueryParam("UA", kIE7a)); |
| ResponseHeaders response_headers; |
| SetDefaultLongCacheHeaders(&kContentTypeCss, &response_headers); |
| response_headers.SetDateAndCaching( |
| timer()->NowMs(), 86400 * Timer::kSecondMs, ", private"); |
| SetFetchResponse(url_plus_ua->Spec(), response_headers, "font_IE7"); |
| |
| // Try fetches with a couple of possible aliases. The one we uploaded it under |
| // is first, since it's the only one the fetcher replies to. |
| ResetUserAgent(kIE7a); |
| |
| ResourcePtr resource( |
| GoogleFontServiceInputResource::Make(url, rewrite_driver())); |
| ASSERT_TRUE(resource.get() != NULL); |
| MockResourceCallback callback(resource, |
| server_context()->thread_system()); |
| resource->LoadAsync(Resource::kReportFailureIfNotCacheable, |
| rewrite_driver()->request_context(), |
| &callback); |
| EXPECT_TRUE(callback.done()); |
| EXPECT_TRUE(callback.success()); |
| EXPECT_EQ("font_IE7", resource->ExtractUncompressedContents()); |
| EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count()); |
| |
| // Different list of .NET versions. |
| const char kIE7b[] = |
| "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; " |
| ".NET CLR 2.0.50727; .NET4.0C; .NET4.0E)"; |
| ResetUserAgent(kIE7b); |
| |
| ResourcePtr resource2( |
| GoogleFontServiceInputResource::Make(url, rewrite_driver())); |
| ASSERT_TRUE(resource2.get() != NULL); |
| MockResourceCallback callback2(resource2, |
| server_context()->thread_system()); |
| resource2->LoadAsync(Resource::kReportFailureIfNotCacheable, |
| rewrite_driver()->request_context(), |
| &callback2); |
| EXPECT_TRUE(callback2.done()); |
| EXPECT_TRUE(callback2.success()); |
| EXPECT_EQ("font_IE7", resource->ExtractUncompressedContents()); |
| EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count()); |
| } |
| |
| TEST_F(GoogleFontServiceInputResourceTest, LoadParallel) { |
| GoogleUrl url(kRoboto); |
| SetupWaitFetcher(); |
| |
| ResetUserAgent("Chromezilla"); |
| ResourcePtr resource( |
| GoogleFontServiceInputResource::Make(url, rewrite_driver())); |
| ASSERT_TRUE(resource.get() != NULL); |
| MockResourceCallback callback(resource, |
| server_context()->thread_system()); |
| resource->LoadAsync(Resource::kReportFailureIfNotCacheable, |
| rewrite_driver()->request_context(), |
| &callback); |
| EXPECT_FALSE(callback.done()); |
| |
| ResetUserAgent("Safieri"); |
| ResourcePtr resource2( |
| GoogleFontServiceInputResource::Make(url, rewrite_driver())); |
| ASSERT_TRUE(resource2.get() != NULL); |
| MockResourceCallback callback2(resource2, |
| server_context()->thread_system()); |
| resource2->LoadAsync(Resource::kReportFailureIfNotCacheable, |
| rewrite_driver()->request_context(), |
| &callback2); |
| EXPECT_FALSE(callback2.done()); |
| |
| CallFetcherCallbacks(); |
| |
| ASSERT_TRUE(callback.done()); |
| EXPECT_TRUE(callback.success()); |
| EXPECT_EQ("font_chromezilla", resource->ExtractUncompressedContents()); |
| |
| ASSERT_TRUE(callback2.done()); |
| EXPECT_TRUE(callback2.success()); |
| EXPECT_EQ("font_safieri", resource2->ExtractUncompressedContents()); |
| } |
| |
| TEST_F(GoogleFontServiceInputResourceTest, FetchFailure) { |
| SetFetchFailOnUnexpected(false); |
| |
| // Regression test --- don't crash when fetch fails. |
| // Bug discovered by accident due to a bug in a test. |
| ResetUserAgent("Huhzilla"); |
| GoogleUrl url(kRoboto); |
| ResourcePtr resource( |
| GoogleFontServiceInputResource::Make(url, rewrite_driver())); |
| ASSERT_TRUE(resource.get() != NULL); |
| MockResourceCallback callback(resource, |
| server_context()->thread_system()); |
| resource->LoadAsync(Resource::kLoadEvenIfNotCacheable, |
| rewrite_driver()->request_context(), |
| &callback); |
| EXPECT_TRUE(callback.done()); |
| EXPECT_FALSE(callback.success()); |
| EXPECT_EQ("", resource->ExtractUncompressedContents()); |
| EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count()); |
| } |
| |
| TEST_F(GoogleFontServiceInputResourceTest, DontLoadNonCss) { |
| ResetUserAgent("Chromezilla"); |
| |
| GoogleUrl non_css_url(kNonCss); |
| ResourcePtr resource( |
| GoogleFontServiceInputResource::Make(non_css_url, rewrite_driver())); |
| ASSERT_TRUE(resource.get() != NULL); |
| MockResourceCallback callback(resource, |
| server_context()->thread_system()); |
| resource->LoadAsync(Resource::kLoadEvenIfNotCacheable, |
| rewrite_driver()->request_context(), |
| &callback); |
| EXPECT_TRUE(callback.done()); |
| EXPECT_FALSE(resource->HttpStatusOk()); |
| EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count()); |
| |
| // Make sure we don't end up caching a success, either. |
| ResourcePtr resource2( |
| GoogleFontServiceInputResource::Make(non_css_url, rewrite_driver())); |
| ASSERT_TRUE(resource2.get() != NULL); |
| MockResourceCallback callback2(resource2, |
| server_context()->thread_system()); |
| resource2->LoadAsync(Resource::kLoadEvenIfNotCacheable, |
| rewrite_driver()->request_context(), |
| &callback2); |
| EXPECT_TRUE(callback2.done()); |
| EXPECT_FALSE(resource->HttpStatusOk()); |
| EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count()); |
| } |
| |
| TEST_F(GoogleFontServiceInputResourceTest, IsFontServiceUrlTest) { |
| GoogleUrl roboto_url(kRoboto); |
| EXPECT_TRUE(GoogleFontServiceInputResource::IsFontServiceUrl(roboto_url)); |
| |
| GoogleUrl roboto_ssl_url(kRobotoSsl); |
| EXPECT_TRUE(GoogleFontServiceInputResource::IsFontServiceUrl(roboto_ssl_url)); |
| |
| GoogleUrl example_url("http://example.com/"); |
| EXPECT_FALSE(GoogleFontServiceInputResource::IsFontServiceUrl(example_url)); |
| } |
| |
| } // namespace |
| |
| } // namespace net_instaweb |