1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/proxy/proxy_script_fetcher_impl.h" 6 7 #include <string> 8 9 #include "base/file_path.h" 10 #include "base/compiler_specific.h" 11 #include "base/path_service.h" 12 #include "base/utf_string_conversions.h" 13 #include "net/base/net_util.h" 14 #include "net/base/ssl_config_service_defaults.h" 15 #include "net/base/test_completion_callback.h" 16 #include "net/disk_cache/disk_cache.h" 17 #include "net/http/http_cache.h" 18 #include "net/http/http_network_session.h" 19 #include "net/url_request/url_request_test_util.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 #include "testing/platform_test.h" 22 23 namespace net { 24 25 // TODO(eroman): 26 // - Test canceling an outstanding request. 27 // - Test deleting ProxyScriptFetcher while a request is in progress. 28 29 namespace { 30 31 const FilePath::CharType kDocRoot[] = 32 FILE_PATH_LITERAL("net/data/proxy_script_fetcher_unittest"); 33 34 struct FetchResult { 35 int code; 36 string16 text; 37 }; 38 39 // A non-mock URL request which can access http:// and file:// urls. 40 class RequestContext : public URLRequestContext { 41 public: 42 RequestContext() { 43 ProxyConfig no_proxy; 44 set_host_resolver( 45 CreateSystemHostResolver(HostResolver::kDefaultParallelism, 46 NULL, NULL)); 47 set_cert_verifier(new CertVerifier); 48 set_proxy_service(ProxyService::CreateFixed(no_proxy)); 49 set_ssl_config_service(new SSLConfigServiceDefaults); 50 51 HttpNetworkSession::Params params; 52 params.host_resolver = host_resolver(); 53 params.cert_verifier = cert_verifier(); 54 params.proxy_service = proxy_service(); 55 params.ssl_config_service = ssl_config_service(); 56 scoped_refptr<HttpNetworkSession> network_session( 57 new HttpNetworkSession(params)); 58 set_http_transaction_factory(new HttpCache( 59 network_session, 60 HttpCache::DefaultBackend::InMemory(0))); 61 } 62 63 private: 64 ~RequestContext() { 65 delete http_transaction_factory(); 66 delete cert_verifier(); 67 delete host_resolver(); 68 } 69 }; 70 71 // Get a file:// url relative to net/data/proxy/proxy_script_fetcher_unittest. 72 GURL GetTestFileUrl(const std::string& relpath) { 73 FilePath path; 74 PathService::Get(base::DIR_SOURCE_ROOT, &path); 75 path = path.AppendASCII("net"); 76 path = path.AppendASCII("data"); 77 path = path.AppendASCII("proxy_script_fetcher_unittest"); 78 GURL base_url = FilePathToFileURL(path); 79 return GURL(base_url.spec() + "/" + relpath); 80 } 81 82 } // namespace 83 84 class ProxyScriptFetcherImplTest : public PlatformTest { 85 public: 86 ProxyScriptFetcherImplTest() 87 : test_server_(TestServer::TYPE_HTTP, FilePath(kDocRoot)) { 88 } 89 90 static void SetUpTestCase() { 91 URLRequest::AllowFileAccess(); 92 } 93 94 protected: 95 TestServer test_server_; 96 }; 97 98 TEST_F(ProxyScriptFetcherImplTest, FileUrl) { 99 scoped_refptr<URLRequestContext> context(new RequestContext); 100 ProxyScriptFetcherImpl pac_fetcher(context); 101 102 { // Fetch a non-existent file. 103 string16 text; 104 TestCompletionCallback callback; 105 int result = pac_fetcher.Fetch(GetTestFileUrl("does-not-exist"), 106 &text, &callback); 107 EXPECT_EQ(ERR_IO_PENDING, result); 108 EXPECT_EQ(ERR_FILE_NOT_FOUND, callback.WaitForResult()); 109 EXPECT_TRUE(text.empty()); 110 } 111 { // Fetch a file that exists. 112 string16 text; 113 TestCompletionCallback callback; 114 int result = pac_fetcher.Fetch(GetTestFileUrl("pac.txt"), 115 &text, &callback); 116 EXPECT_EQ(ERR_IO_PENDING, result); 117 EXPECT_EQ(OK, callback.WaitForResult()); 118 EXPECT_EQ(ASCIIToUTF16("-pac.txt-\n"), text); 119 } 120 } 121 122 // Note that all mime types are allowed for PAC file, to be consistent 123 // with other browsers. 124 TEST_F(ProxyScriptFetcherImplTest, HttpMimeType) { 125 ASSERT_TRUE(test_server_.Start()); 126 127 scoped_refptr<URLRequestContext> context(new RequestContext); 128 ProxyScriptFetcherImpl pac_fetcher(context); 129 130 { // Fetch a PAC with mime type "text/plain" 131 GURL url(test_server_.GetURL("files/pac.txt")); 132 string16 text; 133 TestCompletionCallback callback; 134 int result = pac_fetcher.Fetch(url, &text, &callback); 135 EXPECT_EQ(ERR_IO_PENDING, result); 136 EXPECT_EQ(OK, callback.WaitForResult()); 137 EXPECT_EQ(ASCIIToUTF16("-pac.txt-\n"), text); 138 } 139 { // Fetch a PAC with mime type "text/html" 140 GURL url(test_server_.GetURL("files/pac.html")); 141 string16 text; 142 TestCompletionCallback callback; 143 int result = pac_fetcher.Fetch(url, &text, &callback); 144 EXPECT_EQ(ERR_IO_PENDING, result); 145 EXPECT_EQ(OK, callback.WaitForResult()); 146 EXPECT_EQ(ASCIIToUTF16("-pac.html-\n"), text); 147 } 148 { // Fetch a PAC with mime type "application/x-ns-proxy-autoconfig" 149 GURL url(test_server_.GetURL("files/pac.nsproxy")); 150 string16 text; 151 TestCompletionCallback callback; 152 int result = pac_fetcher.Fetch(url, &text, &callback); 153 EXPECT_EQ(ERR_IO_PENDING, result); 154 EXPECT_EQ(OK, callback.WaitForResult()); 155 EXPECT_EQ(ASCIIToUTF16("-pac.nsproxy-\n"), text); 156 } 157 } 158 159 TEST_F(ProxyScriptFetcherImplTest, HttpStatusCode) { 160 ASSERT_TRUE(test_server_.Start()); 161 162 scoped_refptr<URLRequestContext> context(new RequestContext); 163 ProxyScriptFetcherImpl pac_fetcher(context); 164 165 { // Fetch a PAC which gives a 500 -- FAIL 166 GURL url(test_server_.GetURL("files/500.pac")); 167 string16 text; 168 TestCompletionCallback callback; 169 int result = pac_fetcher.Fetch(url, &text, &callback); 170 EXPECT_EQ(ERR_IO_PENDING, result); 171 EXPECT_EQ(ERR_PAC_STATUS_NOT_OK, callback.WaitForResult()); 172 EXPECT_TRUE(text.empty()); 173 } 174 { // Fetch a PAC which gives a 404 -- FAIL 175 GURL url(test_server_.GetURL("files/404.pac")); 176 string16 text; 177 TestCompletionCallback callback; 178 int result = pac_fetcher.Fetch(url, &text, &callback); 179 EXPECT_EQ(ERR_IO_PENDING, result); 180 EXPECT_EQ(ERR_PAC_STATUS_NOT_OK, callback.WaitForResult()); 181 EXPECT_TRUE(text.empty()); 182 } 183 } 184 185 TEST_F(ProxyScriptFetcherImplTest, ContentDisposition) { 186 ASSERT_TRUE(test_server_.Start()); 187 188 scoped_refptr<URLRequestContext> context(new RequestContext); 189 ProxyScriptFetcherImpl pac_fetcher(context); 190 191 // Fetch PAC scripts via HTTP with a Content-Disposition header -- should 192 // have no effect. 193 GURL url(test_server_.GetURL("files/downloadable.pac")); 194 string16 text; 195 TestCompletionCallback callback; 196 int result = pac_fetcher.Fetch(url, &text, &callback); 197 EXPECT_EQ(ERR_IO_PENDING, result); 198 EXPECT_EQ(OK, callback.WaitForResult()); 199 EXPECT_EQ(ASCIIToUTF16("-downloadable.pac-\n"), text); 200 } 201 202 TEST_F(ProxyScriptFetcherImplTest, NoCache) { 203 ASSERT_TRUE(test_server_.Start()); 204 205 scoped_refptr<URLRequestContext> context(new RequestContext); 206 ProxyScriptFetcherImpl pac_fetcher(context); 207 208 // Fetch a PAC script whose HTTP headers make it cacheable for 1 hour. 209 GURL url(test_server_.GetURL("files/cacheable_1hr.pac")); 210 { 211 string16 text; 212 TestCompletionCallback callback; 213 int result = pac_fetcher.Fetch(url, &text, &callback); 214 EXPECT_EQ(ERR_IO_PENDING, result); 215 EXPECT_EQ(OK, callback.WaitForResult()); 216 EXPECT_EQ(ASCIIToUTF16("-cacheable_1hr.pac-\n"), text); 217 } 218 219 // Now kill the HTTP server. 220 ASSERT_TRUE(test_server_.Stop()); 221 222 // Try to fetch the file again -- if should fail, since the server is not 223 // running anymore. (If it were instead being loaded from cache, we would 224 // get a success. 225 { 226 string16 text; 227 TestCompletionCallback callback; 228 int result = pac_fetcher.Fetch(url, &text, &callback); 229 EXPECT_EQ(ERR_IO_PENDING, result); 230 EXPECT_EQ(ERR_CONNECTION_REFUSED, callback.WaitForResult()); 231 } 232 } 233 234 TEST_F(ProxyScriptFetcherImplTest, TooLarge) { 235 ASSERT_TRUE(test_server_.Start()); 236 237 scoped_refptr<URLRequestContext> context(new RequestContext); 238 ProxyScriptFetcherImpl pac_fetcher(context); 239 240 // Set the maximum response size to 50 bytes. 241 int prev_size = pac_fetcher.SetSizeConstraint(50); 242 243 // These two URLs are the same file, but are http:// vs file:// 244 GURL urls[] = { 245 test_server_.GetURL("files/large-pac.nsproxy"), 246 GetTestFileUrl("large-pac.nsproxy") 247 }; 248 249 // Try fetching URLs that are 101 bytes large. We should abort the request 250 // after 50 bytes have been read, and fail with a too large error. 251 for (size_t i = 0; i < arraysize(urls); ++i) { 252 const GURL& url = urls[i]; 253 string16 text; 254 TestCompletionCallback callback; 255 int result = pac_fetcher.Fetch(url, &text, &callback); 256 EXPECT_EQ(ERR_IO_PENDING, result); 257 EXPECT_EQ(ERR_FILE_TOO_BIG, callback.WaitForResult()); 258 EXPECT_TRUE(text.empty()); 259 } 260 261 // Restore the original size bound. 262 pac_fetcher.SetSizeConstraint(prev_size); 263 264 { // Make sure we can still fetch regular URLs. 265 GURL url(test_server_.GetURL("files/pac.nsproxy")); 266 string16 text; 267 TestCompletionCallback callback; 268 int result = pac_fetcher.Fetch(url, &text, &callback); 269 EXPECT_EQ(ERR_IO_PENDING, result); 270 EXPECT_EQ(OK, callback.WaitForResult()); 271 EXPECT_EQ(ASCIIToUTF16("-pac.nsproxy-\n"), text); 272 } 273 } 274 275 TEST_F(ProxyScriptFetcherImplTest, Hang) { 276 ASSERT_TRUE(test_server_.Start()); 277 278 scoped_refptr<URLRequestContext> context(new RequestContext); 279 ProxyScriptFetcherImpl pac_fetcher(context); 280 281 // Set the timeout period to 0.5 seconds. 282 base::TimeDelta prev_timeout = pac_fetcher.SetTimeoutConstraint( 283 base::TimeDelta::FromMilliseconds(500)); 284 285 // Try fetching a URL which takes 1.2 seconds. We should abort the request 286 // after 500 ms, and fail with a timeout error. 287 { GURL url(test_server_.GetURL("slow/proxy.pac?1.2")); 288 string16 text; 289 TestCompletionCallback callback; 290 int result = pac_fetcher.Fetch(url, &text, &callback); 291 EXPECT_EQ(ERR_IO_PENDING, result); 292 EXPECT_EQ(ERR_TIMED_OUT, callback.WaitForResult()); 293 EXPECT_TRUE(text.empty()); 294 } 295 296 // Restore the original timeout period. 297 pac_fetcher.SetTimeoutConstraint(prev_timeout); 298 299 { // Make sure we can still fetch regular URLs. 300 GURL url(test_server_.GetURL("files/pac.nsproxy")); 301 string16 text; 302 TestCompletionCallback callback; 303 int result = pac_fetcher.Fetch(url, &text, &callback); 304 EXPECT_EQ(ERR_IO_PENDING, result); 305 EXPECT_EQ(OK, callback.WaitForResult()); 306 EXPECT_EQ(ASCIIToUTF16("-pac.nsproxy-\n"), text); 307 } 308 } 309 310 // The ProxyScriptFetcher should decode any content-codings 311 // (like gzip, bzip, etc.), and apply any charset conversions to yield 312 // UTF8. 313 TEST_F(ProxyScriptFetcherImplTest, Encodings) { 314 ASSERT_TRUE(test_server_.Start()); 315 316 scoped_refptr<URLRequestContext> context(new RequestContext); 317 ProxyScriptFetcherImpl pac_fetcher(context); 318 319 // Test a response that is gzip-encoded -- should get inflated. 320 { 321 GURL url(test_server_.GetURL("files/gzipped_pac")); 322 string16 text; 323 TestCompletionCallback callback; 324 int result = pac_fetcher.Fetch(url, &text, &callback); 325 EXPECT_EQ(ERR_IO_PENDING, result); 326 EXPECT_EQ(OK, callback.WaitForResult()); 327 EXPECT_EQ(ASCIIToUTF16("This data was gzipped.\n"), text); 328 } 329 330 // Test a response that was served as UTF-16 (BE). It should 331 // be converted to UTF8. 332 { 333 GURL url(test_server_.GetURL("files/utf16be_pac")); 334 string16 text; 335 TestCompletionCallback callback; 336 int result = pac_fetcher.Fetch(url, &text, &callback); 337 EXPECT_EQ(ERR_IO_PENDING, result); 338 EXPECT_EQ(OK, callback.WaitForResult()); 339 EXPECT_EQ(ASCIIToUTF16("This was encoded as UTF-16BE.\n"), text); 340 } 341 } 342 343 TEST_F(ProxyScriptFetcherImplTest, DataURLs) { 344 scoped_refptr<URLRequestContext> context(new RequestContext); 345 ProxyScriptFetcherImpl pac_fetcher(context); 346 347 const char kEncodedUrl[] = 348 "data:application/x-ns-proxy-autoconfig;base64,ZnVuY3Rpb24gRmluZFByb3h5R" 349 "m9yVVJMKHVybCwgaG9zdCkgewogIGlmIChob3N0ID09ICdmb29iYXIuY29tJykKICAgIHJl" 350 "dHVybiAnUFJPWFkgYmxhY2tob2xlOjgwJzsKICByZXR1cm4gJ0RJUkVDVCc7Cn0="; 351 const char kPacScript[] = 352 "function FindProxyForURL(url, host) {\n" 353 " if (host == 'foobar.com')\n" 354 " return 'PROXY blackhole:80';\n" 355 " return 'DIRECT';\n" 356 "}"; 357 358 // Test fetching a "data:"-url containing a base64 encoded PAC script. 359 { 360 GURL url(kEncodedUrl); 361 string16 text; 362 TestCompletionCallback callback; 363 int result = pac_fetcher.Fetch(url, &text, &callback); 364 EXPECT_EQ(OK, result); 365 EXPECT_EQ(ASCIIToUTF16(kPacScript), text); 366 } 367 368 const char kEncodedUrlBroken[] = 369 "data:application/x-ns-proxy-autoconfig;base64,ZnVuY3Rpb24gRmluZFByb3h5R"; 370 371 // Test a broken "data:"-url containing a base64 encoded PAC script. 372 { 373 GURL url(kEncodedUrlBroken); 374 string16 text; 375 TestCompletionCallback callback; 376 int result = pac_fetcher.Fetch(url, &text, &callback); 377 EXPECT_EQ(ERR_FAILED, result); 378 } 379 } 380 381 } // namespace net 382