blob: b78b4ae6fda1029a2696398239a4761a015961de [file] [log] [blame]
/*
* 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: hujie@google.com (Jie Hu)
#include "net/instaweb/http/public/mock_url_fetcher.h"
#include "net/instaweb/http/public/request_context.h"
#include "net/instaweb/rewriter/public/mock_resource_callback.h"
#include "net/instaweb/rewriter/public/resource.h" // for Resource, etc
#include "net/instaweb/rewriter/public/rewrite_driver.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_input_resource.h"
#include "pagespeed/kernel/base/basictypes.h" // for arraysize
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/ref_counted_ptr.h"
#include "pagespeed/kernel/base/string.h" // for GoogleString
#include "pagespeed/kernel/base/string_util.h" // for StrCat, etc
#include "pagespeed/kernel/html/html_parse_test_base.h"
#include "pagespeed/kernel/http/content_type.h"
#include "pagespeed/kernel/http/google_url.h"
#include "pagespeed/kernel/http/http_names.h"
#include "pagespeed/kernel/http/request_headers.h"
#include "pagespeed/kernel/http/response_headers.h"
namespace net_instaweb {
class UrlInputResourceTest : public RewriteTestBase {
protected:
// A const used for all tests that don't care about is_auth_domain_expected
// in the CheckResourceFetchHasReferer calls.
static const bool kUnusedBoolArg = true;
// Helper method that does all the verification wrt creation of an input
// resource with the given "url" argument. fetch_url indicates the URL to
// use for setting up fetch responses. Note that fetch_url is almost always
// the same as url, except when standard port numbers (80 for http and 443
// for https) are used, in which case the fetch_url drops these port numbers.
// base_url is the URL of the page wrt which this resource URL is being
// created.
// is_authorized_domain indicates whether the URL corresponds to an explicitly
// authorized domain or not. is_auth_domain_expected is only used when
// is_authorized_domain is false, and it indicates whether the fetched URL's
// domain is to be considered authorized after input resource creation or not.
void ResourceFetchHelper(const GoogleString& url,
const GoogleString& fetch_url,
const GoogleString& base_url,
bool is_background_fetch,
bool is_authorized_domain,
bool is_auth_domain_expected,
const ResourcePtr& resource,
const GoogleString& expected_cache_key,
const GoogleString& expected_referer,
const RequestContextPtr& request_context) {
SetBaseUrlForFetch(base_url);
EXPECT_STREQ(url, resource->url());
EXPECT_STREQ(expected_cache_key, resource->cache_key());
resource->set_is_background_fetch(is_background_fetch);
MockResourceCallback cb(resource, factory()->thread_system());
resource->LoadAsync(Resource::kLoadEvenIfNotCacheable, request_context,
&cb);
cb.Wait();
ASSERT_TRUE(cb.done());
ASSERT_TRUE(cb.success());
EXPECT_STREQ(expected_referer, mock_url_fetcher()->last_referer());
}
void CheckResourceFetchHasReferer(const GoogleString& url,
const GoogleString& fetch_url,
const GoogleString& base_url,
bool is_background_fetch,
bool is_authorized_domain,
bool is_auth_domain_expected,
const GoogleString& expected_cache_key,
const GoogleString& expected_referer) {
PrepareResourceFetch(fetch_url);
ResourcePtr resource(MakeUrlInputResource(url, is_authorized_domain));
RequestContextPtr request_context(
RequestContext::NewTestRequestContext(factory()->thread_system()));
ResourceFetchHelper(url, fetch_url, base_url, is_background_fetch,
is_authorized_domain, is_auth_domain_expected,
resource, expected_cache_key, expected_referer,
request_context);
EXPECT_STREQ(expected_referer, mock_url_fetcher()->last_referer());
if (!is_authorized_domain) {
GoogleUrl tmp_url(fetch_url);
EXPECT_EQ(is_auth_domain_expected,
request_context->IsSessionAuthorizedFetchOrigin(
tmp_url.Origin().as_string()));
}
}
void CheckExtractedGzippedResource(const GoogleString& url,
const GoogleString& fetch_url,
const GoogleString& base_url,
bool is_background_fetch,
bool is_authorized_domain,
bool is_auth_domain_expected,
const GoogleString& expected_cache_key,
const GoogleString& expected_referer) {
const char kHello[] = "hello";
const char kHelloGzip[] =
"\x1f\x8b\x08\x08\x64\x7d\x33\x56\x00\x03\x68\x65\x6c\x6c\x6f\x00\xcb"
"\x48\xcd\xc9\xc9\x07\x00\x86\xa6\x10\x36\x05\x00\x00\x00";
StringPiece payload(kHelloGzip, sizeof(kHelloGzip));
PrepareGzippedResourceFetch(fetch_url, payload);
ResourcePtr resource(MakeUrlInputResource(url, is_authorized_domain));
RequestContextPtr request_context(
RequestContext::NewTestRequestContext(factory()->thread_system()));
ResourceFetchHelper(
url, fetch_url, base_url, is_background_fetch, is_authorized_domain,
is_auth_domain_expected, resource, expected_cache_key, expected_referer,
request_context);
EXPECT_STREQ(kHello, resource->ExtractUncompressedContents());
}
UrlInputResource* MakeUrlInputResource(const GoogleString& url,
bool is_authorized_domain) {
return new UrlInputResource(rewrite_driver(), &kContentTypeJpeg, url,
is_authorized_domain);
}
void PrepareResourceFetch(const GoogleString& resource_url) {
mock_url_fetcher()->set_verify_pagespeed_header_off(true);
ResponseHeaders response_headers;
DefaultResponseHeaders(kContentTypeJpeg, 100, &response_headers);
SetFetchResponse(AbsolutifyUrl(resource_url), response_headers, "payload");
}
void PrepareGzippedResourceFetch(const GoogleString& resource_url,
StringPiece payload) {
mock_url_fetcher()->set_verify_pagespeed_header_off(true);
ResponseHeaders response_headers;
DefaultResponseHeaders(kContentTypeJpeg, 100, &response_headers);
response_headers.Add(HttpAttributes::kContentEncoding,
HttpAttributes::kGzip);
SetFetchResponse(AbsolutifyUrl(resource_url), response_headers, payload);
}
};
// Test of referer (BackgroundFetch): When the resource fetching request header
// misses referer, we set the referer for it. Base url and resource url are
// same.
TEST_F(UrlInputResourceTest, TestBackgroundFetchRefererSameDomain) {
GoogleString url = StrCat(kTestDomain, "1.jpg");
CheckResourceFetchHasReferer(url, url, kTestDomain,
true, true, kUnusedBoolArg,
url, kTestDomain);
}
// Test extraction of gzipped resource.
TEST_F(UrlInputResourceTest, TestExtractGzippedResource) {
GoogleString url = "http://other.com/1.txt";
CheckExtractedGzippedResource(
url, url, kTestDomain, true /* is_background_fetch */,
false /* is_authorized_domain */, true /* is_auth_domain_expected */,
"unauth://other.com/1.txt", kTestDomain);
}
// Test of referer (BackgroundFetch): When the resource fetching request header
// misses referer, we set the referer for it. Base url and resource url are
// different.
TEST_F(UrlInputResourceTest, TestBackgroundFetchRefererDomain) {
GoogleString url = "http://other.com/1.jpg";
CheckResourceFetchHasReferer(url, url, kTestDomain, true, true,
kUnusedBoolArg, url,
kTestDomain);
}
// Test of referer (NonBackgroundFetch): When the resource fetching request
// header misses referer, we check if there is any referer from the original
// request header. If that referer is empty, No referer would be set for this
// fetching request.
TEST_F(UrlInputResourceTest, TestNonBackgroundFetchWithRefererMissing) {
GoogleString url = "http://other.com/1.jpg";
RequestHeaders headers;
rewrite_driver()->SetRequestHeaders(headers);
CheckResourceFetchHasReferer(url, url, kTestDomain,
false, true, kUnusedBoolArg,
url, "");
}
// Test of referer (NonBackgroundFetch): When the resource fetching request
// header misses referer, we set the referer for it from the original request
// header.
TEST_F(UrlInputResourceTest, TestNonBackgroundFetchWithReferer) {
GoogleString url = "http://other.com/1.jpg";
RequestHeaders headers;
headers.Add(HttpAttributes::kReferer, kTestDomain);
rewrite_driver()->SetRequestHeaders(headers);
CheckResourceFetchHasReferer(url, url, kTestDomain,
false, true, kUnusedBoolArg,
url, kTestDomain);
}
/* Tests related to unauthorized http domains. */
// Test that unauthorized resources are created correctly with http protocol.
TEST_F(UrlInputResourceTest, TestUnauthorizedDomainHttp) {
GoogleString url = "http://other.com/1.jpg";
CheckResourceFetchHasReferer(url, url, kTestDomain,
true, false, true,
"unauth://other.com/1.jpg", kTestDomain);
}
// Test that unauthorized resources are not created with wrong protocol.
TEST_F(UrlInputResourceTest, TestUnauthorizedDomainWrongProtocol) {
GoogleString url = "ftp://other.com/1.jpg";
PrepareResourceFetch(url);;
SetBaseUrlForFetch(kTestDomain);
ASSERT_DEATH(MakeUrlInputResource(url, false), "");
}
// Test that unauthorized resources are not created with a relative URL.
TEST_F(UrlInputResourceTest, TestUnauthorizedDomainRelativeURL) {
GoogleString url = "/1.jpg";
PrepareResourceFetch(url);;
SetBaseUrlForFetch(kTestDomain);
ASSERT_DEATH(MakeUrlInputResource(url, false), "");
}
// Test that unauthorized resources are created when a standard (80) port is
// specified for http.
TEST_F(UrlInputResourceTest, TestUnauthorizedDomainHttpWithCorrectPort) {
GoogleString url = "http://other.com:80/1.jpg";
CheckResourceFetchHasReferer(url, "http://other.com/1.jpg", kTestDomain,
true, false, true,
"unauth://other.com/1.jpg", kTestDomain);
}
// Test that unauthorized resources are not created when a non-standard port
// is specified for http.
TEST_F(UrlInputResourceTest, TestNoUnauthorizedDomainHttpWithWrongPort) {
GoogleString url = "http://other.com:1234/1.jpg";
CheckResourceFetchHasReferer(url, url, kTestDomain,
true, false, false,
"unauth://other.com:1234/1.jpg", kTestDomain);
}
// Test that unauthorized resources are not created when a https (443) port is
// specified for http.
TEST_F(UrlInputResourceTest, TestNoUnauthorizedDomainHttpWithHttpsPort) {
GoogleString url = "http://other.com:443/1.jpg";
CheckResourceFetchHasReferer(url, url, kTestDomain,
true, false, false,
"unauth://other.com:443/1.jpg", kTestDomain);
}
/* Tests related to unauthorized https domains. */
// Test that unauthorized resources are created correctly with https protocol.
TEST_F(UrlInputResourceTest, TestUnauthorizedDomainHttps) {
GoogleString url = "https://other.com/1.jpg";
CheckResourceFetchHasReferer(url, url, kTestDomain,
true, false, true,
"unauths://other.com/1.jpg", kTestDomain);
}
// Test that unauthorized resources are created when a standard (443) port is
// specified for https.
TEST_F(UrlInputResourceTest, TestUnauthorizedDomainHttpsWithCorrectPort) {
GoogleString url = "https://other.com:443/1.jpg";
CheckResourceFetchHasReferer(url, "https://other.com/1.jpg", kTestDomain,
true, false, true,
"unauths://other.com/1.jpg", kTestDomain);
}
// Test that unauthorized resources are not created when a non-standard port
// is specified for https.
TEST_F(UrlInputResourceTest, TestNoUnauthorizedDomainHttpsWithWrongPort) {
GoogleString url = "https://other.com:1234/1.jpg";
CheckResourceFetchHasReferer(url, url, kTestDomain,
true, false, false,
"unauths://other.com:1234/1.jpg", kTestDomain);
}
// Test that unauthorized resources are not created when a http (80) port is
// specified for https.
TEST_F(UrlInputResourceTest, TestNoUnauthorizedDomainHttpsWithHttpPort) {
GoogleString url = "https://other.com:80/1.jpg";
CheckResourceFetchHasReferer(url, url, kTestDomain,
true, false, false,
"unauths://other.com:80/1.jpg", kTestDomain);
}
struct PortTest {
const char* spec;
int expected_int_port;
int int_port;
};
// Test that verifies that standard port numbers are treated as PORT_UNSPECIFIED
// by GoogleUrl.
TEST_F(UrlInputResourceTest, IntPort) {
const PortTest port_tests[] = {
// http
{"http://www.google.com/", 80, url::PORT_UNSPECIFIED},
{"http://www.google.com:80/", 80, url::PORT_UNSPECIFIED},
{"http://www.google.com:443/", 443, 443},
{"http://www.google.com:1234/", 1234, 1234},
// https
{"https://www.google.com/", 443, url::PORT_UNSPECIFIED},
{"https://www.google.com:443/", 443, url::PORT_UNSPECIFIED},
{"https://www.google.com:80/", 80, 80},
{"https://www.google.com:1234/", 1234, 1234},
};
for (int i = 0; i < arraysize(port_tests); ++i) {
GoogleUrl url(port_tests[i].spec);
EXPECT_TRUE(url.IsWebValid());
EXPECT_EQ(port_tests[i].expected_int_port, url.EffectiveIntPort());
EXPECT_EQ(port_tests[i].int_port, url.IntPort());
}
}
} // namespace net_instaweb