| /* |
| * 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) |
| |
| #include "net/instaweb/rewriter/public/url_partnership.h" |
| |
| #include "net/instaweb/rewriter/public/domain_lawyer.h" |
| #include "net/instaweb/rewriter/public/rewrite_options.h" |
| #include "net/instaweb/rewriter/public/rewrite_test_base.h" |
| #include "pagespeed/kernel/base/google_message_handler.h" |
| #include "pagespeed/kernel/base/gtest.h" |
| #include "pagespeed/kernel/base/scoped_ptr.h" |
| #include "pagespeed/kernel/base/string.h" |
| #include "pagespeed/kernel/base/string_util.h" |
| #include "pagespeed/kernel/http/google_url.h" |
| |
| namespace { |
| |
| const char kOriginalRequest[] = "http://www.nytimes.com/index.html"; |
| const char kResourceUrl1[] = "r/styles/style.css?appearance=reader/writer?"; |
| const char kResourceUrl2[] = "r/styles/style2.css?appearance=reader"; |
| const char kResourceUrl3[] = "r/main.css"; |
| const char kCdnResourceUrl[] = "http://graphics8.nytimes.com/styles/style.css"; |
| |
| // Resources 1-3 but specified absolutely |
| const char kAbsoluteResourceUrl1[] = |
| "http://www.nytimes.com/r/styles/style.css?appearance=reader/writer?"; |
| const char kAbsoluteResourceUrl2[] = |
| "http://www.nytimes.com/r/styles/style2.css?appearance=reader"; |
| const char kAbsoluteResourceUrl3[] = "http://www.nytimes.com/r/main.css"; |
| |
| } // namespace |
| |
| |
| namespace net_instaweb { |
| |
| class UrlPartnershipTest : public RewriteTestBase { |
| protected: |
| UrlPartnershipTest() |
| : styles_path_("http://www.nytimes.com/r/styles/"), |
| r_path_("http://www.nytimes.com/r/"), |
| style_url_("style.css?appearance=reader/writer?"), |
| style2_url_("style2.css?appearance=reader") { |
| } |
| |
| virtual void SetUp() { |
| RewriteTestBase::SetUp(); |
| GoogleUrl original_gurl(kOriginalRequest); |
| partnership_.reset(new UrlPartnership(rewrite_driver())); |
| partnership_->Reset(original_gurl); |
| } |
| |
| // Add up to 3 URLs -- url2 and url3 are ignored if null. |
| bool AddUrls(const char* url1, const char* url2, const char* url3) { |
| bool ret = partnership_->AddUrl(url1, &message_handler_); |
| if (url2 != NULL) { |
| ret &= partnership_->AddUrl(url2, &message_handler_); |
| } |
| if (url3 != NULL) { |
| ret &= partnership_->AddUrl(url3, &message_handler_); |
| } |
| return ret; |
| } |
| |
| // Gets the full path of an index as a GoogleString. |
| GoogleString FullPath(int index) { |
| const GoogleUrl* gurl = partnership_->FullPath(index); |
| StringPiece spec = gurl->Spec(); |
| return GoogleString(spec.data(), spec.size()); |
| } |
| |
| DomainLawyer* domain_lawyer() { return options()->WriteableDomainLawyer(); } |
| scoped_ptr<UrlPartnership> partnership_; |
| GoogleString styles_path_; |
| GoogleString r_path_; |
| GoogleString style_url_; |
| GoogleString style2_url_; |
| GoogleMessageHandler message_handler_; |
| }; |
| |
| TEST_F(UrlPartnershipTest, OneUrlFlow) { |
| ASSERT_TRUE(AddUrls(kResourceUrl1, NULL, NULL)); |
| ASSERT_EQ(1, partnership_->num_urls()); |
| EXPECT_EQ(styles_path_, partnership_->ResolvedBase()); |
| EXPECT_EQ(style_url_, partnership_->RelativePath(0)); |
| EXPECT_EQ(styles_path_ + style_url_, FullPath(0)); |
| } |
| |
| TEST_F(UrlPartnershipTest, OneUrlFlowAbsolute) { |
| ASSERT_TRUE(AddUrls(kAbsoluteResourceUrl1, NULL, NULL)); |
| ASSERT_EQ(1, partnership_->num_urls()); |
| EXPECT_EQ(styles_path_, partnership_->ResolvedBase()); |
| EXPECT_EQ(style_url_, partnership_->RelativePath(0)); |
| EXPECT_EQ(styles_path_ + style_url_, FullPath(0)); |
| } |
| |
| TEST_F(UrlPartnershipTest, TwoUrlFlowSamePath) { |
| AddUrls(kResourceUrl1, kResourceUrl2, NULL); |
| ASSERT_EQ(2, partnership_->num_urls()); |
| EXPECT_EQ(styles_path_, partnership_->ResolvedBase()); |
| EXPECT_EQ(style_url_, partnership_->RelativePath(0)); |
| EXPECT_EQ(style2_url_, partnership_->RelativePath(1)); |
| } |
| |
| TEST_F(UrlPartnershipTest, TwoUrlFlowSamePathMixed) { |
| AddUrls(kAbsoluteResourceUrl1, kResourceUrl2, NULL); |
| ASSERT_EQ(2, partnership_->num_urls()); |
| EXPECT_EQ(styles_path_, partnership_->ResolvedBase()); |
| EXPECT_EQ(style_url_, partnership_->RelativePath(0)); |
| EXPECT_EQ(style2_url_, partnership_->RelativePath(1)); |
| } |
| |
| TEST_F(UrlPartnershipTest, ThreeUrlFlowDifferentPaths) { |
| AddUrls(kResourceUrl1, kResourceUrl2, kResourceUrl3); |
| ASSERT_EQ(3, partnership_->num_urls()); |
| EXPECT_EQ(r_path_, partnership_->ResolvedBase()); |
| // We add 2 to the expected values of the 3 kResourceUrl* below to |
| // skip over the "r/". |
| EXPECT_EQ(GoogleString(kResourceUrl1 + 2), partnership_->RelativePath(0)); |
| EXPECT_EQ(GoogleString(kResourceUrl2 + 2), partnership_->RelativePath(1)); |
| EXPECT_EQ(GoogleString(kResourceUrl3 + 2), partnership_->RelativePath(2)); |
| } |
| |
| TEST_F(UrlPartnershipTest, ThreeUrlFlowDifferentPathsAbsolute) { |
| AddUrls(kAbsoluteResourceUrl1, kAbsoluteResourceUrl2, kAbsoluteResourceUrl3); |
| ASSERT_EQ(3, partnership_->num_urls()); |
| EXPECT_EQ(r_path_, partnership_->ResolvedBase()); |
| // We add 2 to the expected values of the 3 kResourceUrl* below to |
| // skip over the "r/". |
| EXPECT_EQ(GoogleString(kResourceUrl1 + 2), partnership_->RelativePath(0)); |
| EXPECT_EQ(GoogleString(kResourceUrl2 + 2), partnership_->RelativePath(1)); |
| EXPECT_EQ(GoogleString(kResourceUrl3 + 2), partnership_->RelativePath(2)); |
| } |
| |
| TEST_F(UrlPartnershipTest, ThreeUrlFlowDifferentPathsMixed) { |
| AddUrls(kAbsoluteResourceUrl1, kResourceUrl2, kAbsoluteResourceUrl3); |
| ASSERT_EQ(3, partnership_->num_urls()); |
| EXPECT_EQ(r_path_, partnership_->ResolvedBase()); |
| // We add 2 to the expected values of the 3 kResourceUrl* below to |
| // skip over the "r/". |
| EXPECT_EQ(GoogleString(kResourceUrl1 + 2), partnership_->RelativePath(0)); |
| EXPECT_EQ(GoogleString(kResourceUrl2 + 2), partnership_->RelativePath(1)); |
| EXPECT_EQ(GoogleString(kResourceUrl3 + 2), partnership_->RelativePath(2)); |
| } |
| |
| TEST_F(UrlPartnershipTest, ExternalDomainNotDeclared) { |
| EXPECT_FALSE(AddUrls(kCdnResourceUrl, NULL, NULL)); |
| } |
| |
| TEST_F(UrlPartnershipTest, ExternalDomainDeclared) { |
| domain_lawyer()->AddDomain("http://graphics8.nytimes.com", &message_handler_); |
| EXPECT_TRUE(partnership_->AddUrl(kCdnResourceUrl, &message_handler_)); |
| } |
| |
| TEST_F(UrlPartnershipTest, ExternalDomainDeclaredButNotMapped) { |
| // This test shows that while we can start partnerships from nytimes.com |
| // or graphics8.nytimes.com, we cannot combine those without a mapping. |
| domain_lawyer()->AddDomain("http://graphics8.nytimes.com", &message_handler_); |
| EXPECT_TRUE(partnership_->AddUrl(kCdnResourceUrl, &message_handler_)); |
| EXPECT_FALSE(partnership_->AddUrl(kResourceUrl1, &message_handler_)); |
| } |
| |
| TEST_F(UrlPartnershipTest, AbsExternalDomainDeclaredButNotMapped) { |
| // This test shows that while we can start partnerships from nytimes.com |
| // or graphics8.nytimes.com, we cannot combine those without a mapping. |
| domain_lawyer()->AddDomain("http://graphics8.nytimes.com", &message_handler_); |
| EXPECT_TRUE(partnership_->AddUrl(kCdnResourceUrl, &message_handler_)); |
| EXPECT_FALSE(partnership_->AddUrl(kAbsoluteResourceUrl1, &message_handler_)); |
| } |
| |
| TEST_F(UrlPartnershipTest, EmptyTail) { |
| EXPECT_FALSE(partnership_->AddUrl("", &message_handler_)); |
| EXPECT_TRUE(partnership_->AddUrl("http://www.nytimes.com", |
| &message_handler_)); |
| EXPECT_TRUE(partnership_->AddUrl("http://www.nytimes.com/", |
| &message_handler_)); |
| } |
| |
| TEST_F(UrlPartnershipTest, EmptyWithPartner) { |
| GoogleUrl base_gurl("http://www.google.com/styles/x.html"); |
| UrlPartnership p(rewrite_driver()); |
| p.Reset(base_gurl); |
| EXPECT_TRUE(p.AddUrl("/styles", &message_handler_)); |
| EXPECT_FALSE(p.AddUrl("", &message_handler_)); |
| EXPECT_TRUE(p.AddUrl("/", &message_handler_)); |
| EXPECT_TRUE(p.AddUrl("..", &message_handler_)); |
| } |
| |
| TEST_F(UrlPartnershipTest, NeedsATrim) { |
| AddUrls(" http://www.nytimes.com/needs_a_trim.jpg ", NULL, NULL); |
| EXPECT_EQ(GoogleString("needs_a_trim.jpg"), partnership_->RelativePath(0)); |
| } |
| |
| TEST_F(UrlPartnershipTest, RemoveLast) { |
| AddUrls(kAbsoluteResourceUrl1, kAbsoluteResourceUrl2, kAbsoluteResourceUrl3); |
| EXPECT_EQ(r_path_, partnership_->ResolvedBase()); |
| partnership_->RemoveLast(); |
| EXPECT_EQ(styles_path_, partnership_->ResolvedBase()); |
| } |
| |
| TEST_F(UrlPartnershipTest, ResourcesFromMappedDomains) { |
| domain_lawyer()->AddRewriteDomainMapping( |
| "http://graphics8.nytimes.com", "http://www.nytimes.com", |
| &message_handler_); |
| domain_lawyer()->AddRewriteDomainMapping( |
| "http://graphics8.nytimes.com", "http://styles.com", &message_handler_); |
| |
| // We can legally combine resources across multiple domains if they are |
| // all mapped together. |
| ASSERT_TRUE(AddUrls(kCdnResourceUrl, kResourceUrl1, |
| "http://styles.com/external.css")); |
| EXPECT_EQ("http://graphics8.nytimes.com/", partnership_->ResolvedBase()); |
| } |
| |
| TEST_F(UrlPartnershipTest, ResourcesFromMappedSameDomainsDifferentPaths) { |
| domain_lawyer()->AddRewriteDomainMapping( |
| "http://cdn.com/nytimes", "http://www.nytimes.com", &message_handler_); |
| domain_lawyer()->AddRewriteDomainMapping( |
| "http://cdn.com/nytimes", "http://styles.com", &message_handler_); |
| domain_lawyer()->AddDomain("http://cdn.com/notw", &message_handler_); |
| |
| // We can combine these because they're mapped to the same domain but |
| // different paths. |
| EXPECT_TRUE(AddUrls("http://cdn.com/notw/style.css", |
| "r/styles/style.css?appearance=reader/writer?", |
| "http://styles.com/external.css")); |
| EXPECT_EQ("http://cdn.com/", partnership_->ResolvedBase()); |
| } |
| |
| TEST_F(UrlPartnershipTest, ResourcesFromMappedSameDomainsSamePaths) { |
| domain_lawyer()->AddRewriteDomainMapping( |
| "http://cdn.com/nytimes", "http://www.nytimes.com", &message_handler_); |
| domain_lawyer()->AddRewriteDomainMapping( |
| "http://cdn.com/nytimes", "http://styles.com", &message_handler_); |
| |
| // We can legally combine resources across multiple domains if they all |
| // map to the same domain+path. |
| ASSERT_TRUE(AddUrls("http://cdn.com/nytimes/style.css", |
| "r/styles/style.css?appearance=reader/writer?", |
| "http://styles.com/external.css")); |
| EXPECT_EQ("http://cdn.com/nytimes/", partnership_->ResolvedBase()); |
| } |
| |
| TEST_F(UrlPartnershipTest, ResourcesFromMappedDifferentDomainsSamePaths) { |
| domain_lawyer()->AddRewriteDomainMapping( |
| "http://cdn.com/nytimes", "http://www.nytimes.com", &message_handler_); |
| domain_lawyer()->AddRewriteDomainMapping( |
| "http://cdn.com/nytimes", "http://styles.com", &message_handler_); |
| domain_lawyer()->AddRewriteDomainMapping( |
| "http://cdn.com/nypost", "http://www.nypost.com", &message_handler_); |
| domain_lawyer()->AddRewriteDomainMapping( |
| "http://cdn.com/nypost", "http://money.com", &message_handler_); |
| |
| // We can combine these because they all map to cdn.com. |
| ASSERT_TRUE(AddUrls("http://cdn.com/nypost/style.css", |
| "r/styles/style.css?appearance=reader/writer?", |
| "http://money.com/external.css")); |
| EXPECT_EQ("http://cdn.com/", partnership_->ResolvedBase()); |
| } |
| |
| TEST_F(UrlPartnershipTest, AllowDisallow) { |
| options()->Disallow("*/*.css"); |
| options()->Allow("*/a*.css"); |
| EXPECT_FALSE(partnership_->AddUrl("foo.css", &message_handler_)); |
| EXPECT_TRUE(partnership_->AddUrl("afoo.css", &message_handler_)); |
| EXPECT_TRUE(partnership_->AddUrl("foo.jpg", &message_handler_)); |
| } |
| |
| TEST_F(UrlPartnershipTest, CombineAcrossPaths) { |
| options()->set_combine_across_paths(true); |
| EXPECT_TRUE(partnership_->AddUrl("a/foo.css", &message_handler_)); |
| EXPECT_TRUE(partnership_->AddUrl("b/bar.css", &message_handler_)); |
| EXPECT_TRUE(partnership_->AddUrl("a/baz.css", &message_handler_)); |
| } |
| |
| TEST_F(UrlPartnershipTest, NoCombineAcrossPaths) { |
| options()->set_combine_across_paths(false); |
| EXPECT_TRUE(partnership_->AddUrl("a/foo.css", &message_handler_)); |
| EXPECT_FALSE(partnership_->AddUrl("b/bar.css", &message_handler_)); |
| EXPECT_TRUE(partnership_->AddUrl("a/baz.css", &message_handler_)); |
| } |
| |
| } // namespace net_instaweb |